Reservations as a consistency boundary
What This Concept Is
An aggregate defines a boundary within which a business rule must hold after every accepted command. A capacity pool with reservation records is one possible model when the rule is “active reservations never exceed this pool's capacity.” Its suitability depends on the domain and contention, not the presence of a class named Aggregate.
Why It Matters Here
A bounded context is a language/model boundary and may contain many aggregates. A reservation aggregate does not need to own billing, shipping or customer records just because those contexts participate in the same workflow. Keep identifiers for external concepts and model their outcomes explicitly.
Concrete Example
For capacity 3, reserve(a,2) succeeds. reserve(b,2) is refused because held capacity would become 4. release(a) succeeds, then reserve(b,2) succeeds. A later reserve(a,2) is rejected: this lab gives an ID one reservation lifecycle, and released IDs are terminal. It retains released records so a delayed reserve cannot resurrect an old reservation.
Repeating reserve(b,2) while b remains held is a no-op; reserve(b,1) is an identity conflict. Repeating release(a) is a no-op. These are explicit domain choices, not universal idempotency rules. A different business may permit a new reservation after release, but should distinguish its identity from a delayed duplicate.
Common Confusion / Misconception
The capacity is not the number of records: released reservations remain in history but consume no capacity. A request rejected before mutation must leave the old state unchanged. An aggregate is not a container for every object in a workflow, and an in-memory invariant does not make concurrent saves safe.
How To Use It
Write allowed commands, invariant, refusal reasons and identity lifecycle before implementation. Use immutable snapshots for predictions and replay. Separate policy decisions such as expiry, partial release and overbooking into explicit changes to the model; do not hide them in generic setters. State which owner may change the pool's capacity.
Check Yourself
Why retain a released identity? Is changing the quantity a retry or a new command? Why can two individually valid transitions still oversubscribe the pool? What workload would make one pool aggregate a contention hotspot?
Mini Drill or Application
Hand-predict the five-command trace above. Add a conflicting retry and repeated release. Implement the lab and preserve three independent traces. Propose an expiry policy and identify the extra clock and delayed-message assumptions it would require; do not claim the reference already implements it.
Read This Only If Stuck
Learning Domain-Driven Design — tactical modeling and aggregates. Fowler's aggregate explanation gives the root/integrity framing; the reservation lifecycle here is an original teaching contract.