Split reads and writes on a architectural level.
It's based on CQS (Command Query Separation) as described by Bertrand Meyer. A method either reads state or mutates state, but not both.
CQRS was discovered by Greg Young.
CQRS enables rich domain models, which are not anaemic.
CAP - You can choose two out of three.
- CA: RDBMS
In distributed systems, we always have network partions, so we only can have these combinations:
- CP: NoSQL stores
- AP: DNS, Caching
You can seperately tune CAP on read and write side. - Read side: Available and Partition Tolerant - Write side: Consistent and Partition Tolerant
- Name contains noun and verb, must be imperative
- CreateUser(id, name, email, password)
- ActivateUser(id)
- RenameUser(id, oldName, newName)
- ChangeUserEmail(id, oldEmail, newEmail)
- ChangeUserPassword(id, oldPassword, newPassword)
- DeactiveateUser(id, reason)
- DeleteUser(id)
- Immutable
- It's best to design commands to be idempotent
- Contains no logic
- Handles cross cutting concerns
- Authorization
- Logging
- Validation
- Loads or creates an Aggregate
- Invokes a method on the loaded/created Aggregate instance
- void handle(command)
- May be part of a Unit of Work. But it's better not!
- Consistency boundary
- Holds only state needed in write side logic
- Handles commands, which may fail
- May call Services
- sending emails
- checking for uniqueness (beware there's only eventual consistency, between read and write side)
- processing credit cards
- May call Services
- Applies events, which can not fail
- State can be replayed by replaying a stream of events
- Optionally can create snapshots of internal state or apply a snapshot
- Issued by aggregate, after successful completion of a command
- Name contains noun and verb, must be past tense
- UserCreated(id, name, email, password)
- UserActivated(id)
- UserRenamed(id, oldName, newName)
- UserEmailChanged(id, oldEmail, newEmail)
- UserPasswordChanged(id, oldPassword, newPassword)
- UserDeactivated(id, reason)
- UserDeleted(id)
- Can't fail, because happend in the past!
- Immutable
- Append only
- Stores events for Aggregates by id
- get_events(id) -> {ok, [events]}
- add_event(id, event) -> ok
- Stores snapshots for Aggregates by id
- get_last_snapshot(id) -> {ok, snapshot} | not_found
- add_snapshot(id, snapshot) -> ok
- Backend can be as simple as serializing into files
- Receives events and does something with it
- A long running business "transaction"
- Creates snapshots from event streams
- Dynamic creation of snapshots e.g. based on load times
- Build CQRS infrastructure based on Erlang/OTP or Akka
- Have a statically typed language for domain logic (Commands, Aggregates, Events, etc.)
- Akka would be perfect fit, but it requires Java or Scala
- What would be a good language, if Erlang/OTP was used?