Skip to content

Instantly share code, notes, and snippets.

@jaymecd
Last active August 29, 2015 14:14
Show Gist options
  • Save jaymecd/a4bf9e6b08eee891a00e to your computer and use it in GitHub Desktop.
Save jaymecd/a4bf9e6b08eee891a00e to your computer and use it in GitHub Desktop.

CQRS - Command Query Responsibility Segregation

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

Write Side

Command

  • 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

Command Handler

  • 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!

Aggregate

  • 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
  • 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

Event

  • 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

Event Store

  • 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

Event Handler

  • Receives events and does something with it

Sagas

  • A long running business "transaction"

Snapshotter

  • Creates snapshots from event streams
  • Dynamic creation of snapshots e.g. based on load times

Read Side

Projections

Ideas

  • 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?

Sources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment