Skip to content

Latest commit

 

History

History
95 lines (59 loc) · 2.98 KB

README.md

File metadata and controls

95 lines (59 loc) · 2.98 KB

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).

workflow

Existing experiments

Classical example

source

  • 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

Aggregate with exposed queries

source

  • 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

  • aggregate initialized with already sourced state

Functional aggregate

source

  • no single aggregate, just functions that take state and return events

Polymorphic

source

  • 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

  • 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

  • yield is used to publish events (no unpublished_events in aggregate)
  • aggregate repository separated from logic

Aggregate with repository

source

  • 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

  • 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

  • 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