DPC Zettelkasten Open in the explorer

Repository Pattern

Each domain record gets a storage-agnostic interface plus a jOOQ implementation, so services never name a database.

Every persisted record type in Medieval Factions has two files: an interface named Mf<Thing>Repository and an implementation named JooqMf<Thing>Repository. The naming convention is the whole convention — the technology appears in the implementation's name and nowhere else.

What the interface says

MfFactionRepository declares six methods: three overloads of getFaction, a getFactions, an upsert, and a delete. There is no save/update split, no query language, no transaction handle, and no Connection. A caller cannot tell from the interface whether the data lives in a database, a flat file, or memory.

Note upsert rather than separate insert and update. Because domain records are immutable data classes carrying their own Value Class Identifier, the caller always has a complete record in hand and never needs to express "change these three columns".

Why bother

Three payoffs, in descending order of how much they actually matter here:

  1. Testability. A fake repository is a MutableMap and twenty lines.
  2. Dialect independence. The interface is what lets H2, MySQL, and PostgreSQL all work — see jOOQ Persistence.
  3. Replaceability. In principle the storage could be swapped. In practice nobody has, and this is the weakest of the three arguments.

The mapping happens in the implementation

The jOOQ classes are where domain records meet columns, including the awkward parts: Faction Role lists are serialized to a JSON column with Gson, and Value Class Identifier values are unwrapped to strings. Keeping that in the implementation is what allows the domain model to be shaped for the simulation rather than for the schema.

The repository is also where Optimistic Locking lives, because the version check has to be part of the same statement as the write.

Sources

Linked from