Skip to content

Instantly share code, notes, and snippets.

@briandonahue
Created September 8, 2011 21:14
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save briandonahue/1204748 to your computer and use it in GitHub Desktop.
Save briandonahue/1204748 to your computer and use it in GitHub Desktop.
Greg Young's CQRS Class - Random Notes

CQRS – Greg Young – 8/26/2011

Insurance Domain – Value to Business Model (S-M-L)

  • Claims – L
  • Sales – S-M
  • Accounting – S
  • Actuarials – S
  • Intranet – S
  • Public Web – M-L

Get a rough estimate of ROI on analysis in various subdomains. Depends on Business Value of subdomain

Take a few days (not much more) to map the organization subdomains and business language

Class – Teacher – Course

Assigning Teacher to a Class, Class’s max students determined by max students of first teacher.
Class.Assign(teacher){
c.Teachers.Add(teacher); // But do we need the whole teacher, or just the max students?
}

You really only need the max students.

If you find yourself wanting aggregates to have relationships with other aggregates – you are modeling incorrectly. Shouldn’t happen. Organize in terms of behaviors not data relationships

Make temporal concepts explicit – i.e. what happens when a class changes after students are enrolled. What about after registration opens, but before students enrolled?

Benefits of encapsulating state

  1. No one can mess with it
  2. Can expose role interfaces
  3. Can’t couple your tests to internal state – only behavior. This allows you to refactor, not refucktor! :)

If you’re using ActiveRecord you are not doing OOP. You are doing procedural code over a relational model.

Use factory methods to create relationships/objects off of Aggregate – classInstance.CreateEnrollment(student);
Pure Creates – Class.Create() should be rare

Very often if you have a collection of something, there is a name for that collection, and it’s probably a key component of your domain model.

You cannot do DDD with DTO map to domains CRUD-based design. Intention is lost, everything is CRUD

CommandHandlers are the only things that should have dependencies injected via containers
CommandHandlers pass dependencies down to the Aggregate Roots

public class DeactivateInventoryItemCommandHandler<DeactivateInventoryItemCommand> {

  public DeactivateInventoryItemAppService(IRepo<Inventory> repo, IDriversLicenseLookupService lookup){
    repo = repo;
    lookup = lookup;
  }

  public void Handle(DeactivateInventoryItemCommand cmd){
    var item = repo.GetById(cmd.Id)
    item.Deactivate(lookup, cmd.reason); // pass in lookup service
    repo.Save(item);
  }
}
bc.

Without event sourcing, you can have a subscriber and event log on write side and get most of the benefits.  Greg says he'll show examples and which benefits you get with ES and which ones you can get without ES.

Somewhat obvious, but if you are running without Event Sourcing, you don't have an Event Store.  You have concurrent (in process) event handlers that handle the events from your aggregates, update your DB, and put the events on a message queue.  All in one transaction.  If queueing an event fails, must roll back the database changes. (DTC).  You do store an event log. This can be transitioned to Event Sourcing.

Handlers can call stored procs.  DBA can build stored procs.  Now you isolate the cost of DBA, DB Licenses.  Moving to Event Source

Event Store is essentially a DB + queue in one.

Events can be versioned as long as things are _Additive_.  Kind of like adding columns and tables being easy, but removing/renaming is not.

Greg does not like inheriting from previous versions of events, just duplicate old fields in the new version. New version of the event must be convertible from last version of the event.

With event sourcing, you can migrate from old system to new system and use the event log as your migration point, and run the old system and new system side by side, writing events to the same store.

Transitioning to CQRS:

Analyze for intent (will be harder in places where it's just CRUD)
Find an aggregate - start there
Start having that aggregate throw events on an event log for all it's behaviors
Then move one aggregate at a time.
Need some functionality to initialize the event log from the legacy format (DB)


Testing the read layer - does not assert off DB, or store
He tests that given a set of events, and you request a DTO, it is populated correctly.  ALlows you to change backend stores, does not couple your tests to your schema, etc.


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