Numbering as a core primitive, with the gapless cost made explicit
- Dominant language
- C#
- Stars
- 6
- Forks
- 7
- Avg merge
- 4h 42m
- Merged PRs (30d)
- 307
Description
Every system on the list needs a human-readable reference: an invoice number, an order number, a booking reference, a student number, a delivery note number.
The only implementation is `NumberSequence` in `BarakoCMS.Accounting/Domain/NumberSequence.cs`, which means anything that is not accounting either takes a dependency on the accounting module or writes its own.
It is also the wrong shape for the load. #551 already records why: one row per named sequence under Marten optimistic concurrency, the loser retries, so the abort rate rises with concurrency. That is correct for two editors and wrong for a counter every write increments.
## What to add
Numbering as a primitive core owns, with the sequence choice made explicitly rather than inherited:
- **A Postgres sequence** where gaps are acceptable. Fast, contention-free, and gaps happen on rollback.
- **Hi-lo batch allocation** where throughput matters more than density. Each instance takes a block.
- **A serial allocator** where gapless is a requirement, with the cost stated plainly.
Gapless is a business and sometimes a legal requirement for invoices, and it costs serialization. #551 makes exactly this point. The primitive should make the choice visible at configuration time rather than picking one silently.
## What else it needs
**A format.** `INV-2026-00042`. A prefix, a padding width, and a period that resets, because a yearly reset is the common requirement and a counter that never resets cannot express it.
**Per tenant.** Two tenants must not share a sequence and must not see each other's numbers. Conjoined tenancy handles the storage; the allocator has to be written so the tenant is in the key rather than assumed.
**Assigned at the right moment.** For a gapless sequence the number is allocated on confirmation, not on draft creation, or every abandoned draft burns a number. That is a policy on the sequence, not a hard rule.
## Relationship to #551
#551 fixes the accounting one where it is. This lifts the capability into core so nothing else has to reimplement it, and gives #551 somewhere to move to. Whether #551 is closed by this or fixed first and migrated later is worth deciding rather than leaving both open against each other.
## Done when
- A content type can declare a numbering rule and a saved entry gets a number.
- The three strategies exist and the configuration says which is in use.
- A test drives concurrent allocation and proves no duplicate, and for the gapless strategy proves no gap.
- Two tenants allocating at once do not see each other's numbers.
Contributor guide
Research direction
Start with BarakoCMS.Accounting/Domain/NumberSequence.cs and issue #551 to understand the existing allocator and its concurrency trade-offs. Trace how core content types and tenant-aware storage are configured, then use the stated done criteria to define concurrent allocation tests for the three strategies, formatting, resets, and tenant isolation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, postgresql
- Domain
- backend, database
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100