ADR-0007: The bank's definition is a module of its own
Status: Accepted Date: 2026-10-10, accepted 2026-10-11 Stage: ADR-0002 stage 8 (roadmap 1.8), stories 1.8.2, 1.8.4 and 1.8.5 (renumbered 2026-10-11: 1.8.3, 1.8.6 and 1.8.7)
Context
gobank is becoming a general banking solution: a ledger, a general
ledger, customers, payments, users, a BFF and the product contract of
ADR-0006. What makes the running demo this bank is scattered through
it: the catalogue is a list in bank/products, the blurb for each
product beside it, the name "Model Bank" in the demo, the base-rate
series in the simulation. A second bank, a white label on the same
capabilities, would be a fork.
Decision
One Go module per bank entity holds everything that makes the general solution this bank, as code. gobank holds the capabilities; a running bank is gobank opened with one definition; another bank is another definition module over the same capabilities.
| In the definition | In the capabilities (gobank) |
|---|---|
| The bank's name | the ledger, the GL, customers, payments, users, the BFF and apps |
| The product versions it carries at one time, in catalogue order, each with the bank's blurb and whether it is withdrawn from sale: the catalogue and its migration data, release by release | the product contract (gobank-products), the runner, the adoption record |
| Its policy: the savings and lending base rates derived from the market rate, their spreads and lags (ADR-0006, story 1.8.4, now 1.8.6) | the parameters component that stores settings and resolves them |
| Its own products, once a product is the bank's rather than the library's (story 1.8.5, now 1.8.7) | products a bank shares with others |
| Customisation that overrules defaults as it arises: data lifetime, security settings | the defaults |
The shape today is bank/definition.Bank (name, product listings) and
the package modelbank in gobank that implements it for the Model
Bank; bank.Open takes the definition and refuses to open without one.
The package becomes the module modelbank, in its own repository, in
story 1.8.4 (now 1.8.6) when the policy joins it: a module with one product list and
no policy is not yet worth a repository.
Ownership and cadence
The definition module is a separate repository with a release cadence of its own, because it is the client bank's: its staff and engineers change it, not gobank's maintainers, and they change it on their timetable (a product launched, a policy revised) rather than gobank's. gobank is released by its maintainers and taken up by a bank when it chooses.
The interface
What the two repositories share is therefore a contract that must stay stable across many gobank releases:
| Surface | Lives in | Used by the definition to |
|---|---|---|
bank/definition.Bank and products.Listing |
gobank | say what the bank is and carries |
gbp.Version, Facts, Intents, Declaration (ADR-0006) |
gobank-products | write the bank's own rules versions and policy |
bank.Open and its options |
gobank | run |
Rules for it: a change is additive, a new field or method with a default, never a removal or a change of meaning in a minor release; a removal is deprecated for a release first and named in the changelog; the definition pins the gobank and gobank-products versions it was built against and declares the oldest it supports; gobank's own test suite builds against the Model Bank's definition at that oldest version, so a break is found by the maintainers, not the client. Anything the definition needs that is not on this surface is a change to gobank, released first, and a bank waits for it.
Release or setting
Not everything about the bank is a release. The parameter tree (ADR-0006) is run-time configuration: a rate, a spread, a lag, an allowance, a term is changed by a setting, decided for a day, with no release. Rules are code, and a change to rules is a tested release: a new rules version in gobank-products or in the bank module, and the definition carrying it. Between the two sits the product itself: a product is an instance of a tested rules version with its own name and its own parameter values. A new fixed-rate saver with a different term is the same rules with another term, and that is configuration, not a release: the bank creates it, names it, sets its parameters and later withdraws it through the parameters it already has.
| Change | How | Who |
|---|---|---|
| A rate, a spread, a lag, an allowance, a term | a setting, effective from a day | the bank's staff, through the parameters |
| A new product on existing rules: another term, another rate, another name | a product instance created in the catalogue, its parameters set | the bank's staff |
| A product withdrawn from sale | the instance withdrawn | the bank's staff |
| New or changed rules: a new event answer, a new derivation, a new parameter | a rules version, tested, released; the definition lists it | engineers, a release |
| The definition's own contents: name, the rules it carries, the policy, the overrides | the module, released | engineers, a release |
The definition therefore carries the bank's initial catalogue and the rules versions it is allowed to instantiate; what the bank has done with them since is in the database, as every other setting is.
Amendment, 2026-10-13 (story 1.8.9)
The definition carries the policy: definition.Bank.Policy, a
gbp.Policy, which the bank refuses to open without. The Model Bank's
is modelbank/policy/v1, and the bank is named the Isle of Alba Bank.
The package modelbank is still in gobank at this story. The direction
for its move, decided 2026-10-13: a bank is a top-level module that
imports gobank (the capabilities: bank, bff, core) and
gobank-products (the contract) and adds its definition and its wiring,
so that most of this code is taken as a library and wrapped to make a
bank. gobank-products stays the contract and the support for writing
rules and policies, not the home of a definition. gobank's own tests
build against test definitions of gobank's own, not the Model Bank's,
so no module depends back on a bank.
The interface rules above ("a change is additive … never a removal or a change of meaning in a minor release") apply from version 1. While the code is pre-1.0 the interface may be restructured as the stories need; once it is stable and 1.0 is released, normal Go module rules apply and the contract stays stable until a major version change.
Consequences
Easier: a white label is a module; a product change is a diff in one place; the capabilities are tested against more than one definition (stage 9's scenario tests build a definition each).
Harder: an interface that must be kept stable and widened only additively, which slows gobank's own refactoring where it touches the definition's surface; a change that needs both repositories is released in order, gobank first, and the bank waits; the definition's module version becomes part of what a deployment names; ADR-0006's product identity moves out of the rules package into the catalogue (its amendment there).
Given up: a product list that can be read off the definition alone. The definition says what the bank started with and what it may build products from; the catalogue in the database says what it sells today.