An experiment of different aggregate implementations. All implementations must pass same test suite: arranged with commands, asserted with events.
Quite typical workflow of an issue in a popular task tracking software (Jira).
- probably most recognized implementation (appeared in Greg Young's CQRS example)
- does not expose its internal state via reader methods
- testability without persistence (just check if operation yields correct event)
Module source: https://github.com/RailsEventStore/rails_event_store/tree/5378b343dbf427f5ea68f7ddfc66d6a449a6ff82/aggregate_root/lib
- clear separation of state sourcing (with projection)
- aggregate not aware of events
- handler queries aggregate whether particular action is possible
- aggregate initialized with already sourced state
- no single aggregate, just functions that take state and return events
- domain classes per each state
- no messaging in domain classes
- no id in domain class
- invalid state transition cared by raising exception
- domain classes per each state
- no messaging in domain classes
- no id in domain class
- invalid state transition cared by not having such methods on objects (duck)
- yield is used to publish events (no unpublished_events in aggregate)
- aggregate repository separated from logic
- aggregate is unware of infrastructure
- aggregate can built itself from events (but it could be recreated in any way)
- aggregate keeps the state in PORO way
- aggregate registers events aka changes
- aggregate provides API to read registered events
- Infrastructure (through repository in this case) is responsible for building/saving the aggregate so it could be done in any way - Event Sourcing, serialization etc
- better mental model by not having separate classes per state
- one object which changes roles
extend(Role.clone)
is used as Ruby ignores subsequent extend with the same module
- clear separation of state sourcing (with projection)
- aggregate not aware of events
- aggregate object is still responsible for holding invariants
- no id in domain class