Domain Modeling and Pure Domain
1. Conceptualization & Domain Modeling
Let’s model the Counter domain. A counter seems simple, but in an enterprise environment, every state change requires complete auditability, precise validation rules, and high scalability.Mapping the Domain Requirements
To model this domain, we map the requirements to core DDD and Event Sourcing patterns:- Aggregate Root (
Counter): The primary consistency boundary. It maintains the current counter value, tracks the stream revision (for optimistic concurrency), and ensures that all state mutations are applied sequentially. - Value Object (
CounterId): A type-safe newtype wrapper around aStringrepresenting the unique ID of our counter stream. - Commands (
CounterCommand): Intentions to change state. These represent the write operations:Increment { amount: i32 }: Requests to add a positive amount.Decrement { amount: i32 }: Requests to subtract a positive amount.Reset: Requests to reset the counter to zero.
- Events (
CounterEvent): Historical, immutable facts that have occurred. These represent our historical log:Incremented { amount: i32 }Decremented { amount: i32 }ResetPerformed { value: i32 }
Why This Matters
By separating commands (intentions) from events (facts), we separate validation from execution.[!IMPORTANT] Command Handling is Validative: Commands can be rejected if they violate invariants. Event Application is Infallible: Events represent the past. Once an event is committed, it cannot be rejected or fail to apply; it must mutate the aggregate state without further checks.
2. Implementing the Pure Domain
Let’s examine the full, pure domain implementation located insideexamples/counter-app/src/domain.rs. Notice that this file has absolutely no infrastructure dependencies (no databases, no network frameworks). It is pure, highly testable Rust logic that implements our framework’s Aggregate trait.
[!TIP] Notice the usage ofchecked_addandchecked_subinhandle. This ensures the aggregate defends its invariants before accepting changes, whileapplyusessaturating_addas a secondary safety mechanism when applying historical facts.