1 of 37

CQRS Lite and Otherwise

george mauer - http://georgemauer.net

2 of 37

I’m George

I’m Director of Development at SURGE Consulting

3 of 37

I’m George

I’m Director of Development at SURGE Consulting

4 of 37

5 of 37

CQS

It’s a

Bertrand Russel thing so you know it’s

right

6 of 37

Separating Read and Write Objects is the Core Concept

CustomerService

void MakeCustomerPreferred(CustomerId)

Customer GetCustomer(CustomerId)

CustomerSet GetCustomersWithName(Name)

CustomerSet GetPreferredCustomers()

void ChangeCustomerLocale(CustomerId, NewLocale)

void CreateCustomer(Customer)

void EditCustomerDetails(CustomerDetails)

CustomerWriteService

void MakeCustomerPreferred(CustomerId)

void ChangeCustomerLocale(CustomerId, NewLocale)

void CreateCustomer(Customer)

void EditCustomerDetails(CustomerDetails)

CustomerReadService

Customer GetCustomer(CustomerId)

CustomerSet GetCustomersWithName(Name)

CustomerSet GetPreferredCustomers()

http://codebetter.com/gregyoung/2010/02/16/cqrs-task-based-uis-event-sourcing-agh/

7 of 37

CQRS Evolved From DDD

8 of 37

Udi Dahan

Greg Young

9 of 37

CQRS a la Greg Young

10 of 37

Guidance from Udi Dahan

11 of 37

So how does The Mob define it?

12 of 37

Task-based UI

13 of 37

Message Bus

14 of 37

Explicit Commands

15 of 37

Explicit Events

And an audit log

16 of 37

Event Sourcing

There is no database

17 of 37

Split Read/Write Data Models

18 of 37

Partitions & Multiple Processes

19 of 37

Eventual Consistency & SLAs

20 of 37

21 of 37

22 of 37

It’s Just Weird!

23 of 37

CQRS Lite

24 of 37

Which of these can we cut?

  • Task-based UI
  • Message Bus
  • Explicit Commands
  • Explicit Events / Audit Log
  • Event Sourcing
  • Read/Write Models
  • Eventual Consistency
  • Partition Tolerance

25 of 37

Which of these can we cut?

  • Task-based UI
  • Message Bus
  • Explicit Commands
  • Explicit Events / Audit Log
  • Event Sourcing
  • Read/Write Models
  • Eventual Consistency
  • Partition Tolerance

Wishful ThinkingSimplifyOKOKLaterConceptually, Yes. As Needed

Introduce as neededMuch Later

26 of 37

Start with standard

n-tier MVC architecture

27 of 37

Write ViewModels are Commands

28 of 37

Write actions delegate to applicators and manage transactions

[HttpPost, Route("")]public async Task<dynamic> Create(CreateTimeEntryCommand cmd) {var id = await timeEntryModification.Apply(cmd); await Db.SaveChangesAsync();� cmd.LogSuccess();return Created($"api/time-entry/{id}", id);}

public class TimeEntryModification :� IApplyCommand<DeleteTimeEntryCommand>,� IApplyCommand<EditTimeEntryCommand>,� IApplyCommand<AdminOverrideEditTimeEntryCommand>,IApplyCommand<CreateTimeEntryCommand, Guid> {�� public async Task<Guid> Apply(CreateTimeEntryCommand cmd) {// ..return id;}��}

29 of 37

BONUS IDEA: Applicators can access internal state

Read-only properties enforce the pattern

Applicators are internal to entities

Probably not necessary for the pattern

¯\_(ツ)_/¯

30 of 37

Applicators publish Events

which can be subscribed to

public async Task<Guid> Apply(LockPeriodCommand cmd) {var tp = new TimePeriod(cmd.Start, cmd.End);// yes, this is db-inefficient, but this is not a huge dataset and an infrequent operationif ( (await db.LockedPeriods.ToListAsync()).Any(x => tp.OverlapsWith(x.TimePeriod())) )throw new InvalidOperationException("Locked time periods cannot overlap");var lp = new LockedPeriod(tp);� db.LockedPeriods.Add(lp); await messages.Dispatch(new LockPeriodCommand.Occurred {

LockedPeriodId = lp.Id, TimePeriod = lp.TimePeriod() });return lp.Id;}

public class Adjustment :IHandleEvent<LockPeriodCommand.Occurred> {�� public async Task Apply(LockPeriodCommand.Occurred ev) =>� await listItemsStore.Update(li => � li.IsUnlocked = false, await listItems.DataForAnyUser(queryModel(ev.TimePeriod, true)));}

31 of 37

Just Implement your own �Message Bus

32 of 37

Queries can be handled by a read service (can be a controller)

and are as simple as possible

33 of 37

and use custom stores as needed

34 of 37

To Summarize

  • Write models should be Commands
  • Each command handled in dedicated class
  • Successful command publishes an event
  • Use a simple in-process message bus
  • Events can be subscribed to
  • Reads are directly in controllers
  • And as simple as possible - use custom stores!

35 of 37

Enable gradual transition

The goal:

36 of 37

37 of 37

Questions?

gmauer@gmail.com

@togakangaroo

http://georgemauer.net

Slides:

http://bit.ly/2rBaixy