https://github.com/railseventstore/aggregates
https://github.com/railseventstore/aggregates
Last synced: 10 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/railseventstore/aggregates
- Owner: RailsEventStore
- Created: 2018-12-22T22:31:04.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2025-03-03T22:12:45.000Z (12 months ago)
- Last Synced: 2025-03-30T21:06:36.834Z (11 months ago)
- Language: Ruby
- Homepage:
- Size: 312 KB
- Stars: 81
- Watchers: 18
- Forks: 11
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Aggregates
An experiment of different aggregate implementations. All implementations must pass same test suite: arranged with commands, asserted with events.
## Experiment subject
Quite typical workflow of an issue in a popular task tracking software (Jira).

## Existing experiments
### Classical example
[source](examples/aggregate_root)
- probably most recognized implementation (appeared in [Greg Young's CQRS example](https://github.com/gregoryyoung/m-r/blob/31d315faf272182d7567a038bbe832a73b879737/SimpleCQRS/Domain.cs#L63-L96))
- 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
### Aggregate with exposed queries
[source](examples/query_based)
- clear separation of state sourcing (with projection)
- aggregate not aware of events
- handler queries aggregate whether particular action is possible
### Aggregate with extracted state
[source](examples/extracted_state)
- aggregate initialized with already sourced state
### Functional aggregate
[source](examples/functional)
- no single aggregate, just functions that take state and return events
### Polymorphic
[source](examples/polymorphic)
- domain classes per each state
- no messaging in domain classes
- no id in domain class
- invalid state transition cared by raising exception
More: https://blog.arkency.com/make-your-ruby-code-more-modular-and-functional-with-polymorphic-aggregate-classes/
### Duck typing
[source](examples/duck_typing)
- 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)
### Aggregate with yield
[source](examples/yield_based)
- yield is used to publish events (no unpublished_events in aggregate)
- aggregate repository separated from logic
### Aggregate with repository
[source](examples/repository)
- 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
### Roles/DCI
[source](examples/roles)
- 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
### PORO with attributes
[source](examples/poro)
- 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