Creating a register
:::info Status
Outline — expand with a full worked example (e.g. adding a new BalanceRegister<> and wiring
a document to write it).
:::
:::tip Use the shipped skill
Prefer the create-register skill (in your scaffolded repo's .claude/skills/) over
hand-rolling this — it covers all four register families in one skill: Balance, Turnover, and
both document-bound and independent/periodic Info registers.
:::
Registers are analytical storage with no UI/form of their own — they exist purely to be written by document behaviors and read by reports or the generated register-transactions viewer.
The three families
| Type | Status | Use for |
|---|---|---|
BalanceRegister<> | Implemented, active | Running totals (inventory balances, exchange rates) |
Info-register family (InfoRegister/PeriodicInfoRegister/DocumentInfoRegister) | Implemented, active | Point-in-time or periodic scalar facts tied to a document/date |
TurnoverRegister<> | Infrastructure exists, no live consumer yet | Period-bucketed movement totals |
Unlike form-bearing entities, a register has no *Dto/*Validator/generated UI of its own —
just the *Base register type plus a Dimensions type implementing
IRegisterDimensions<TSelf> (which the Equality generator
fills in BuildEqualityPredicate() for).
Wiring a document to write it
Register writers are resolved only via ISubmitScope.GetWriter<TWriter>() inside
OnSubmitAsync/OnDeleteAsync — never by constructor injection. See
Overall architecture
for why this chokepoint exists.
Gotchas worth knowing before you start
- Concurrency uses a manually-incremented
Versioncolumn with bounded (3-attempt) retry, not a lock-free atomic upsert — a deliberate scope-down (techdebt/TECH_DEBT.mdTD-004), fine at current traffic levels but worth knowing if you're debugging a retry-exhausted failure under heavy concurrent writes to the same dimension row. - Use
DimensionPredicateBuilder+ the generatedBuildEqualityPredicate()to filter to just the touched dimension rows — don't hand-roll a reflection-based filter. - Every Document needs
IEntityWithRowseven if it has no tabular lines, if it participates in the register-transactions viewer.
See also
- Reference: Register Engine
- Platform docs:
docs/kandra-register-engine-design.md,docs/1c-register-architecture.md