Introduction to CQRS Approaches to System Architecture@NeilRobbinsneil@computer.org
Tonights show is brought to by the following #tags#cqrs#glnetHopefully not#fail, #WTF, or #WasteOfTime
What are we going to cover?Overview of the spaceCQS, Commands, Queries, DDDSimple CQRSDistributing CQRSEventsPub-SubEventual Consistency & Conflict ManagementEvent Sourcing & CQRSWhat & Why?Some Code – Phew!
Introducing CQS
An interface should be attractive!Simple
Easy to learn
Easy to remember
Withstand the tests of
Time
ChangeCommand-Query Separation PrincipleFunctions should not produce abstract side effectsFrom: Object-Oriented Software Construction, 2nd Edition by Bertrand Meyer
Command-Query Separation PrincipleAsking a question should not change the answerFrom: Object-Oriented Software Construction, 2nd Edition by Bertrand Meyer
Command-Query Separation Principlepublic interface IAmACqsExample{  void DoSomething();  void DoSomething(Argument arg);  Result GetSomething();  Result GetSomething(Argument arg);}
Introducing CQRS
So what is CQRS?Started out as:DDDD (Domain Driven Design + Distributed)Architectural CQSNow:CommandQueryResponsibilitySegregation
So what is CQRS? – Part DeuxA trinityCommandsQueriesIntent capturing UI
Simple CQRSFaçade\ClientLogically distributableCommandsFullyConsistentQueries
Commands in CQRS
What does a command look like?CreateAnOrderForTheCustomerImperative
Specific
Clearly represent client intent
Never past tense
Highly likely to succeedCommand HandlingGet the decorators in!
CQRS Meets DDDBut it doesn’t have toCore domain, supporting domainsNeed not be everywhere, some can be transaction script with Anaemic objectsUbiquitous language can lead to a clean interfaceOrganising by aggregate root can make for simpler persistenceDomain object should never be put in an invalid state!
Domain Objects: Forget Properties! public class Event    {        public string Name { get; set; }        public int Venue { get; set; }        public DateTime Date { get; set; }        public IEnumerable<Session> Sessions { get; set; }    }    public class Session    {        public PresentationPresentation { get; set; }        public string Location { get; set; }        public DateTime Time { get; set; }    }
Domain Objects: No QueriesNo Queries on your domain objects!Yes, that definitely includes any getters!Nada! (well maybe one, we’ll see that much later)All querying will be done through the Query systemNo binding to domain objects
Domain Objects: Control your mutationsOnly Command methods exposedThey don’t return things either!Controlled mutation of stateEnsures that aggregate roots do not enter into invalid statesEver! No IsValid() method (it’d be a query anyway)
Queries in CQRS
So what about Queries?They’re just queriesThey support the UI which should provide a Decision Support SystemGet quickly from the databaseImmutable objects
UI in CQRS
Intentful UIShopping Cart ServiceCommandCommandRatings ServiceCommandCommandProduct Images ServiceCommandCommandCommandBuying Choices ServiceOffers ServiceBought Together Service
Intentful UICaptures IntentAligns with commandsWant to know WHY something changed, not just that it didDecide on an appropriate level of granularity, what’s important to your client/business?Customer address changedOrCustomer moved houseOrCustomer’s address was incorrect
Intentful UIUses the query infrastructureFor displayFor decision supportShould be a pit of successCommands should succeedWill contain logic, is typically rich
Distributing CQRS
Distributing CQRSIntroduces some issuesKeeping things in syncFaults become more likelyCan give some winsAvailabilityScalabilityMay/Should make some choicesCAP Theorum
Introducing EventsSomething happenedPast-tenseCapture the details of the changeSimple immutable data-structuresCan be fed into MapReduce jobsCan be consumed more widely across the enterpriseIntegrate with other systemsSend emailsUpdate DataMarts/Warehouses
Façade\ClientBusCommandsEventuallyConsistentQueriesHandler
Keeping the Query Side in SyncDon’t have to forego ConsistencyCan perform 2 Phase CommitsCan use quorumsButthere’s a price in the face of a faultwrite performance will degraderestrictive in the face of wide-geographical distributionCan use:RDBMS crazyness ;)PubSub
SubscriberSubscriberPublisherSubscribersSubscriber
Event Sourcing & CQRS
Event Sourcing…there are times when we don't just want to see where we are,we also want to know how we got therehttp://martinfowler.com/eaaDev/EventSourcing.html2 Ways to persist changesSnapshot of current stateJust the change itself
ID : 123Name : The Art of WarAuthor: Sun TzuISBN: 1234ABCD5678Event: BookIsbnChangedNewValue: 4321DCBA8765
Time to See Some CodeYou didn’t think I’d make you watch me type did you?A small taste of Greg Young’s course (with minor changes) – link at the end.
First up a domain objectnamespaceProgNetDemo{public classCatalogItem    {    }}The domain object
A Command MethodnamespaceProgNetDemo{public classCatalogItem    { public void Retire()      	{        	}    }}Now we add a behaviour
Some State To ChangenamespaceProgNetDemo{public classCatalogItem    {privatebool _retired; public void Retire()      	{        	}    }}Some state that will need to be mutated
Guard the MutationnamespaceProgNetDemo{public classCatalogItem    {privatebool _retired; public void Retire()      	{if (!_retired)thrownewInvalidOperationException();        	}    }}Protect the validity of the object!
Make the State Mutation A Domain EventnamespaceProgNetDemo{public classCatalogItem    {privatebool _retired; public void Retire()      	{if (!_retired)thrownewInvalidOperationException();ApplyEvent(new RetiredEvent(_id));        	}    }}State mutations are a 2 phase activity
Need an Identifier for the Aggregatepublic classCatalogItem{privatebool _retired;privateGuid_id; public void Retire()	{if (!_retired)thrownewInvalidOperationException();ApplyEvent(new RetiredEvent(_id));    	}}The event needs to know the Id of the Entity being updated
Create the EventpublicclassRetiredEvent{privatereadonlyGuid _id;publicRetiredEvent(Guid id)    {		_id = id;	}}Don’t need more state, in this case the name says it allNote it’s immutable
Need to be Able to Apply the Eventpublic classCatalogItem{privatebool _retired;privateGuid_id; public void Retire()	{if (!_retired)thrownewInvalidOperationException();ApplyEvent(new RetiredEvent(_id));    	}}Still need to write the code to apply the event.
Create a Base Classpublic abstract classAggregateRoot {protected voidApplyEvent(Event @event)	{	}}All Aggregate Roots will extend this classWe’ll need to handle more than just the one type of event!
Create the Event Typepublic classEvent { }Would want to move the AggregateId into this base class
Have our RetiredEvent Inherit From the Base TypepublicclassRetiredEvent : Event{privatereadonlyGuid _id;publicRetiredEvent(Guid id)    {		_id = id;	}}
Still Need to Mutate the Statepublic classCatalogItem : AggregateRoot{privatebool _retired;privateGuid_id; public void Retire()	{if (!_retired)thrownewInvalidOperationException();ApplyEvent(new RetiredEvent(_id));    	}}
Create a Method to Handle the State Change From the Eventpublic classCatalogItem : AggregateRoot{privatebool _retired;privateGuid_id; public void Retire()	{if (!_retired)thrownewInvalidOperationException();ApplyEvent(new RetiredEvent(_id));    	}private voidApplyRetiredEvent(RetiredEvent @event)	{		_retired = true;	}}This method will actually apply the event, that is mutate the state
Done!?public classCatalogItem : AggregateRoot{privatebool _retired;privateGuid_id; public void Retire()	{if (!_retired)thrownewInvalidOperationException();ApplyEvent(new RetiredEvent(_id));    	}private voidApplyRetiredEvent(RetiredEvent @event)	{		_retired = true;	}}
Need to Connect the Event with the Handlerpublic classCatalogItem : AggregateRoot{privatebool _retired;privateGuid_id;publicCatalogItem()	{RegisterHandler<RetiredEvent>(ApplyRetiredEvent);	} public void Retire()	{if (!_retired)thrownewInvalidOperationException();ApplyEvent(new RetiredEvent(_id));    	}private voidApplyRetiredEvent(RetiredEvent @event)	{		_retired = true;	}}This could be done following a convention insteadNote, this is the specific Event subtype – more later!
Allow Handlers to be Registeredpublic classAggregateRoot {protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)whereTEvent : Event	{	}protected voidApplyEvent(Event @event)	{	}}The delegate signature needs to be defined
Create the Delegate Signaturepublic delegate voidAppliesEvent<TEvent>(TEvent @event)where TEvent : Event;public classAggregateRoot {protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)whereTEvent : Event	{	}protected voidApplyEvent(Event @event)	{	}}
Need to Maintain the Registrypublic delegate voidAppliesEvent<TEvent>(TEvent @event)where TEvent : Event;public classAggregateRoot {private readonlyIDictionary<Type, Action<Event>> _handlerRegistry;protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)whereTEvent : Event	{var castHandler = 		        DelegateAdjuster.CastArgument<Event, TEvent>(e => handler(e));	}protected voidApplyEvent(Event @event)	{	}}Will keep a list of handlers for each type of event.We’re storing the Action<Event>, but we want to call an Action<RetiredEvent>
Delegate Adjuster:From Greg Young’s Blogpublic class DelegateAdjuster{    public static Action<TBase> CastArgument<TBase, TDerived>(Expression<Action<TDerived>> source) whereTDerived : TBase{        if (typeof(TDerived) ==typeof(TBase)){            return (Action<TBase>)((Delegate)source.Compile());}ParameterExpressionsourceParameter = Expression.Parameter(typeof(TBase),"source");varresult = Expression.Lambda<Action<TBase>>(Expression.Invoke(                source,Expression.Convert(sourceParameter, typeof(TDerived))),sourceParameter);        return result.Compile();}}
Need to Maintain the Registrypublic delegate voidAppliesEvent<TEvent>(TEvent @event)where TEvent : Event;public classAggregateRoot {private readonlyIDictionary<Type, Action<Event>> _handlerRegistry;protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)whereTEvent : Event	{var castHandler = 		        DelegateAdjuster.CastArgument<Event, TEvent>(e => handler(e));		 _handlerRegistry.Add(typeof(TEvent), castHandler);	}protected voidApplyEvent(Event @event)	{	}}Store the handler having downcast it
Need to Apply the Event Stillpublic classAggregateRoot {private readonlyIDictionary<Type, Action<Event>> _handlerRegistry;protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)whereTEvent : Event	{var castHandler = 		        DelegateAdjuster.CastArgument<Event, TEvent>(e => handler(e));		 _handlerRegistry.Add(typeof(TEvent), castHandler);	}protected voidApplyEvent(Event @event)	{Action<Event> handler;if (!_handlerRegistry.TryGetValue(@event.GetType(), out handler))		{throw newInvalidOperationException();		}		handler(@event);	}}If no handler for a mutation is registered, it’s a problemInvoke the handler
Need to Track Applied Eventspublic delegate voidAppliesEvent<TEvent>(TEvent @event)where TEvent : Event;public classAggregateRoot {private readonlyIDictionary<Type, Action<Event>> _handlerRegistry;private readonlyICollection<Event> _events;protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)whereTEvent : Event	{var castHandler = 		        DelegateAdjuster.CastArgument<Event, TEvent>(e => handler(e));		 _handlerRegistry.Add(typeof(TEvent), castHandler);	}protected voidApplyEvent(Event @event)	{Action<Event> handler;if (!_handlerRegistry.TryGetValue(@event.GetType(), out handler))		{throw newInvalidOperationException();		}		handler(@event);		_events.Add(@event);	}}Track the events that are being appliedMake sure that the events are added.
All done now???public classCatalogItem : AggregateRoot{privatebool _retired;privateGuid_id;publicCatalogItem()	{RegisterHandler<RetiredEvent>(ApplyRetiredEvent);	} public void Retire()	{if (!_retired)thrownewInvalidOperationException();ApplyEvent(new RetiredEvent(_id));    	}private voidApplyRetiredEvent(RetiredEvent @event)	{		_retired = true;	}}
Not Quite – pt. 1Need to Expose the events for persistence & publishingPut a GetChanges() method on the AggregateRoot base class
Not Quite – pt. 2Need to be able to rebuild the Aggregate from historyPut a LoadFromHistory(IEnumerable<Event> events) method on the AggregateRoot base classReapply the eventsDon’t track them as changes – overload apply eventprotected voidApplyEvent(Event @event, booltrackAsChange)if (add) _events.Add(@event);
Not Quite – pt. 3Possibly memento pattern for snapshotingSee Mark Nijhof’s blog & sample code on GitHubPossibly use a unit of work pattern for tracking changes

An Introduction To CQRS

  • 1.
    Introduction to CQRSApproaches to System Architecture@NeilRobbinsneil@computer.org
  • 2.
    Tonights show isbrought to by the following #tags#cqrs#glnetHopefully not#fail, #WTF, or #WasteOfTime
  • 3.
    What are wegoing to cover?Overview of the spaceCQS, Commands, Queries, DDDSimple CQRSDistributing CQRSEventsPub-SubEventual Consistency & Conflict ManagementEvent Sourcing & CQRSWhat & Why?Some Code – Phew!
  • 4.
  • 5.
    An interface shouldbe attractive!Simple
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
    ChangeCommand-Query Separation PrincipleFunctionsshould not produce abstract side effectsFrom: Object-Oriented Software Construction, 2nd Edition by Bertrand Meyer
  • 11.
    Command-Query Separation PrincipleAskinga question should not change the answerFrom: Object-Oriented Software Construction, 2nd Edition by Bertrand Meyer
  • 12.
    Command-Query Separation Principlepublicinterface IAmACqsExample{ void DoSomething(); void DoSomething(Argument arg); Result GetSomething(); Result GetSomething(Argument arg);}
  • 13.
  • 14.
    So what isCQRS?Started out as:DDDD (Domain Driven Design + Distributed)Architectural CQSNow:CommandQueryResponsibilitySegregation
  • 15.
    So what isCQRS? – Part DeuxA trinityCommandsQueriesIntent capturing UI
  • 16.
  • 17.
  • 18.
    What does acommand look like?CreateAnOrderForTheCustomerImperative
  • 19.
  • 20.
  • 21.
  • 22.
    Highly likely tosucceedCommand HandlingGet the decorators in!
  • 23.
    CQRS Meets DDDButit doesn’t have toCore domain, supporting domainsNeed not be everywhere, some can be transaction script with Anaemic objectsUbiquitous language can lead to a clean interfaceOrganising by aggregate root can make for simpler persistenceDomain object should never be put in an invalid state!
  • 24.
    Domain Objects: ForgetProperties! public class Event { public string Name { get; set; } public int Venue { get; set; } public DateTime Date { get; set; } public IEnumerable<Session> Sessions { get; set; } } public class Session { public PresentationPresentation { get; set; } public string Location { get; set; } public DateTime Time { get; set; } }
  • 25.
    Domain Objects: NoQueriesNo Queries on your domain objects!Yes, that definitely includes any getters!Nada! (well maybe one, we’ll see that much later)All querying will be done through the Query systemNo binding to domain objects
  • 26.
    Domain Objects: Controlyour mutationsOnly Command methods exposedThey don’t return things either!Controlled mutation of stateEnsures that aggregate roots do not enter into invalid statesEver! No IsValid() method (it’d be a query anyway)
  • 27.
  • 28.
    So what aboutQueries?They’re just queriesThey support the UI which should provide a Decision Support SystemGet quickly from the databaseImmutable objects
  • 29.
  • 30.
    Intentful UIShopping CartServiceCommandCommandRatings ServiceCommandCommandProduct Images ServiceCommandCommandCommandBuying Choices ServiceOffers ServiceBought Together Service
  • 31.
    Intentful UICaptures IntentAlignswith commandsWant to know WHY something changed, not just that it didDecide on an appropriate level of granularity, what’s important to your client/business?Customer address changedOrCustomer moved houseOrCustomer’s address was incorrect
  • 32.
    Intentful UIUses thequery infrastructureFor displayFor decision supportShould be a pit of successCommands should succeedWill contain logic, is typically rich
  • 33.
  • 34.
    Distributing CQRSIntroduces someissuesKeeping things in syncFaults become more likelyCan give some winsAvailabilityScalabilityMay/Should make some choicesCAP Theorum
  • 35.
    Introducing EventsSomething happenedPast-tenseCapturethe details of the changeSimple immutable data-structuresCan be fed into MapReduce jobsCan be consumed more widely across the enterpriseIntegrate with other systemsSend emailsUpdate DataMarts/Warehouses
  • 36.
  • 37.
    Keeping the QuerySide in SyncDon’t have to forego ConsistencyCan perform 2 Phase CommitsCan use quorumsButthere’s a price in the face of a faultwrite performance will degraderestrictive in the face of wide-geographical distributionCan use:RDBMS crazyness ;)PubSub
  • 38.
  • 39.
  • 40.
    Event Sourcing…there aretimes when we don't just want to see where we are,we also want to know how we got therehttp://martinfowler.com/eaaDev/EventSourcing.html2 Ways to persist changesSnapshot of current stateJust the change itself
  • 41.
    ID : 123Name: The Art of WarAuthor: Sun TzuISBN: 1234ABCD5678Event: BookIsbnChangedNewValue: 4321DCBA8765
  • 42.
    Time to SeeSome CodeYou didn’t think I’d make you watch me type did you?A small taste of Greg Young’s course (with minor changes) – link at the end.
  • 43.
    First up adomain objectnamespaceProgNetDemo{public classCatalogItem { }}The domain object
  • 44.
    A Command MethodnamespaceProgNetDemo{publicclassCatalogItem { public void Retire() { } }}Now we add a behaviour
  • 45.
    Some State ToChangenamespaceProgNetDemo{public classCatalogItem {privatebool _retired; public void Retire() { } }}Some state that will need to be mutated
  • 46.
    Guard the MutationnamespaceProgNetDemo{publicclassCatalogItem {privatebool _retired; public void Retire() {if (!_retired)thrownewInvalidOperationException(); } }}Protect the validity of the object!
  • 47.
    Make the StateMutation A Domain EventnamespaceProgNetDemo{public classCatalogItem {privatebool _retired; public void Retire() {if (!_retired)thrownewInvalidOperationException();ApplyEvent(new RetiredEvent(_id)); } }}State mutations are a 2 phase activity
  • 48.
    Need an Identifierfor the Aggregatepublic classCatalogItem{privatebool _retired;privateGuid_id; public void Retire() {if (!_retired)thrownewInvalidOperationException();ApplyEvent(new RetiredEvent(_id)); }}The event needs to know the Id of the Entity being updated
  • 49.
    Create the EventpublicclassRetiredEvent{privatereadonlyGuid_id;publicRetiredEvent(Guid id) { _id = id; }}Don’t need more state, in this case the name says it allNote it’s immutable
  • 50.
    Need to beAble to Apply the Eventpublic classCatalogItem{privatebool _retired;privateGuid_id; public void Retire() {if (!_retired)thrownewInvalidOperationException();ApplyEvent(new RetiredEvent(_id)); }}Still need to write the code to apply the event.
  • 51.
    Create a BaseClasspublic abstract classAggregateRoot {protected voidApplyEvent(Event @event) { }}All Aggregate Roots will extend this classWe’ll need to handle more than just the one type of event!
  • 52.
    Create the EventTypepublic classEvent { }Would want to move the AggregateId into this base class
  • 53.
    Have our RetiredEventInherit From the Base TypepublicclassRetiredEvent : Event{privatereadonlyGuid _id;publicRetiredEvent(Guid id) { _id = id; }}
  • 54.
    Still Need toMutate the Statepublic classCatalogItem : AggregateRoot{privatebool _retired;privateGuid_id; public void Retire() {if (!_retired)thrownewInvalidOperationException();ApplyEvent(new RetiredEvent(_id)); }}
  • 55.
    Create a Methodto Handle the State Change From the Eventpublic classCatalogItem : AggregateRoot{privatebool _retired;privateGuid_id; public void Retire() {if (!_retired)thrownewInvalidOperationException();ApplyEvent(new RetiredEvent(_id)); }private voidApplyRetiredEvent(RetiredEvent @event) { _retired = true; }}This method will actually apply the event, that is mutate the state
  • 56.
    Done!?public classCatalogItem :AggregateRoot{privatebool _retired;privateGuid_id; public void Retire() {if (!_retired)thrownewInvalidOperationException();ApplyEvent(new RetiredEvent(_id)); }private voidApplyRetiredEvent(RetiredEvent @event) { _retired = true; }}
  • 57.
    Need to Connectthe Event with the Handlerpublic classCatalogItem : AggregateRoot{privatebool _retired;privateGuid_id;publicCatalogItem() {RegisterHandler<RetiredEvent>(ApplyRetiredEvent); } public void Retire() {if (!_retired)thrownewInvalidOperationException();ApplyEvent(new RetiredEvent(_id)); }private voidApplyRetiredEvent(RetiredEvent @event) { _retired = true; }}This could be done following a convention insteadNote, this is the specific Event subtype – more later!
  • 58.
    Allow Handlers tobe Registeredpublic classAggregateRoot {protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)whereTEvent : Event { }protected voidApplyEvent(Event @event) { }}The delegate signature needs to be defined
  • 59.
    Create the DelegateSignaturepublic delegate voidAppliesEvent<TEvent>(TEvent @event)where TEvent : Event;public classAggregateRoot {protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)whereTEvent : Event { }protected voidApplyEvent(Event @event) { }}
  • 60.
    Need to Maintainthe Registrypublic delegate voidAppliesEvent<TEvent>(TEvent @event)where TEvent : Event;public classAggregateRoot {private readonlyIDictionary<Type, Action<Event>> _handlerRegistry;protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)whereTEvent : Event {var castHandler = DelegateAdjuster.CastArgument<Event, TEvent>(e => handler(e)); }protected voidApplyEvent(Event @event) { }}Will keep a list of handlers for each type of event.We’re storing the Action<Event>, but we want to call an Action<RetiredEvent>
  • 61.
    Delegate Adjuster:From GregYoung’s Blogpublic class DelegateAdjuster{ public static Action<TBase> CastArgument<TBase, TDerived>(Expression<Action<TDerived>> source) whereTDerived : TBase{ if (typeof(TDerived) ==typeof(TBase)){ return (Action<TBase>)((Delegate)source.Compile());}ParameterExpressionsourceParameter = Expression.Parameter(typeof(TBase),"source");varresult = Expression.Lambda<Action<TBase>>(Expression.Invoke( source,Expression.Convert(sourceParameter, typeof(TDerived))),sourceParameter); return result.Compile();}}
  • 62.
    Need to Maintainthe Registrypublic delegate voidAppliesEvent<TEvent>(TEvent @event)where TEvent : Event;public classAggregateRoot {private readonlyIDictionary<Type, Action<Event>> _handlerRegistry;protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)whereTEvent : Event {var castHandler = DelegateAdjuster.CastArgument<Event, TEvent>(e => handler(e)); _handlerRegistry.Add(typeof(TEvent), castHandler); }protected voidApplyEvent(Event @event) { }}Store the handler having downcast it
  • 63.
    Need to Applythe Event Stillpublic classAggregateRoot {private readonlyIDictionary<Type, Action<Event>> _handlerRegistry;protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)whereTEvent : Event {var castHandler = DelegateAdjuster.CastArgument<Event, TEvent>(e => handler(e)); _handlerRegistry.Add(typeof(TEvent), castHandler); }protected voidApplyEvent(Event @event) {Action<Event> handler;if (!_handlerRegistry.TryGetValue(@event.GetType(), out handler)) {throw newInvalidOperationException(); } handler(@event); }}If no handler for a mutation is registered, it’s a problemInvoke the handler
  • 64.
    Need to TrackApplied Eventspublic delegate voidAppliesEvent<TEvent>(TEvent @event)where TEvent : Event;public classAggregateRoot {private readonlyIDictionary<Type, Action<Event>> _handlerRegistry;private readonlyICollection<Event> _events;protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)whereTEvent : Event {var castHandler = DelegateAdjuster.CastArgument<Event, TEvent>(e => handler(e)); _handlerRegistry.Add(typeof(TEvent), castHandler); }protected voidApplyEvent(Event @event) {Action<Event> handler;if (!_handlerRegistry.TryGetValue(@event.GetType(), out handler)) {throw newInvalidOperationException(); } handler(@event); _events.Add(@event); }}Track the events that are being appliedMake sure that the events are added.
  • 65.
    All done now???publicclassCatalogItem : AggregateRoot{privatebool _retired;privateGuid_id;publicCatalogItem() {RegisterHandler<RetiredEvent>(ApplyRetiredEvent); } public void Retire() {if (!_retired)thrownewInvalidOperationException();ApplyEvent(new RetiredEvent(_id)); }private voidApplyRetiredEvent(RetiredEvent @event) { _retired = true; }}
  • 66.
    Not Quite –pt. 1Need to Expose the events for persistence & publishingPut a GetChanges() method on the AggregateRoot base class
  • 67.
    Not Quite –pt. 2Need to be able to rebuild the Aggregate from historyPut a LoadFromHistory(IEnumerable<Event> events) method on the AggregateRoot base classReapply the eventsDon’t track them as changes – overload apply eventprotected voidApplyEvent(Event @event, booltrackAsChange)if (add) _events.Add(@event);
  • 68.
    Not Quite –pt. 3Possibly memento pattern for snapshotingSee Mark Nijhof’s blog & sample code on GitHubPossibly use a unit of work pattern for tracking changes
  • 69.
    So what havewe done here?A lot of the qualities we have just introduced normally exist at an infrastructure levelTransaction/Redo LogsReplicationConflict ManagementBy bringing them into the application we gain great power
  • 70.
    Benefits of EventSourcingAlreadyWe can build new stores for new services, we have the full set of available information readyWe can rebuild stores for existing servicesWe use the same mechanism for our Query Stores as we use for any other service in the EnterpriseWe aren’t just restricted to storing the EventsCan send Emails based on themCan perform Complex Event Processing… The worlds your Oyster, you have the RAW MATERIAL
  • 71.
    Some Other Benefitsof Event SourcingAlsoA Very Provable Audit LogVery Simple Horizontal ScalingMore Granular Service BoundariesMore Explicit BoundariesCan Replay Events in Debugging ScenariosSuits Behaviour Based Testing & Outside-In Development
  • 72.
    What haven’t welooked at?Compensatory Actions & the Saga patternHow to persist the commandsRDBMS? DocumentDB? HBase/Cassandra/Riak? File System?Fit with an SOA approach (it’s very good)Extreme scaling (for load, for geographies, for minimal latency)Versioning eventsPotential impact on project managementTesting – suits Outside-In & Mockist –see #GOOS…
  • 73.
  • 74.
    NO SILVER BULLETSThecoding is the EASY BITDon’t need a grand frameworkThe thinking & conversations is the HARD BITAs with any DDD projectNeed to understand the tradeoffs being made, and why
  • 75.
    Referenced Material/LinksMe -@Neil Robbins – neil@computer.orgThis Presentation - http://bit.ly/cqrsatgldotnetGreg Youngs:Course – http://bit.ly/gregscourseBlog – http://bit.ly/gregyoungsblogUdiDahans:Course – http://bit.ly/udiscourseBlog – http://bit.ly/udisblogMark Nijhof’s sample codehttp://github.com/MarkNijhof/FohjinObject-Oriented Software Construction, 2nd Edition by Bertrand Meyer

Editor's Notes

  • #7 CQS Begins with Bertrand Meyer!Only commands are permitted to produce concrete side effects – in fact we expect them to!Queries should be side-effect free!Why?Getting a clear understanding of what even a simple unit of code does from its textual description (its name) is a difficult problem. One way in which this is solved is through referential transparencyAsking a question should not change the answer
  • #8 CQS Begins with Bertrand Meyer!Only commands are permitted to produce concrete side effects – in fact we expect them to!Queries should be side-effect free!Why?Getting a clear understanding of what even a simple unit of code does from its textual description (its name) is a difficult problem. One way in which this is solved is through referential transparency. Referential transparency allows the substitution of a value (eg 4) with a function whose result is 4. If asking for the result of a function changes the future result of a function then this substitutability is lost. This makes understanding the behaviour of a system much more difficult.Asking a question should not change the answer!But asking a question can change some things!Concrete side effects are ok if they’re not also abstract side effects. – Eg updating a cache, logging, etc… - but must not update the OBSERVABLE state of the object!Commands should not return values – better ways to do this, such as having a “how did it go” query.
  • #13 Which might seem a strange place to begin a talk on CQRS!Every button, link the lot – is a command of interest – though not all may be handled in the same way.
  • #16 Decorator patternProxys &amp; InterceptorsCould Aspect Weave – but I wouldn’tWho wants to see some code?
  • #18 Properties typically break encapsulation – they present the internal structure of an object as its public interface.Tell Don’t Ask.Why is it an IEnumerable&lt;Session&gt; anyway? What behaviour is required of this collection, delegate responsibility to the lowest level.Never public Get/Set anywhere!Interface should be in terms of behaviours, not data.Remember we’re trying to capture intent here, the domain object should be doing stuff, not having stuff done with it.
  • #24 Which might seem a strange place to begin a talk on CQRS!Every button, link the lot – is a command of interest – though not all may be handled in the same way.
  • #25 Which might seem a strange place to begin a talk on CQRS!Every button, link the lot – is a command of interest – though not all may be handled in the same way.Say goodbye to editable data grids and friends.Don’t just want to know that the customer record changed, want to know WHY the customer record changedNothing too knew here – but the question is whether to capture this as text (perhaps from a lookup), or as a part of the Type System.Capturing as a part of the Type System allows simpler specialisation of the command processing pipeline, and of the subsequent event raising,
  • #26 Which might seem a strange place to begin a talk on CQRS!Every button, link the lot – is a command of interest – though not all may be handled in the same way.Say goodbye to editable data grids and friends.Don’t just want to know that the customer record changed, want to know WHY the customer record changed
  • #31 So, CAP TheorumRDBMS crazyness == cross database queries/updates, replication, XML datatype &amp; secondary indexes, pub-sub from the database, and more…
  • #32 Maintaining the ViewState
  • #37 First we need a Domain object
  • #38 Now we give it a behaviour
  • #39 Now some state to mutate
  • #40 If the behaviour can’t be executed successfully then we are dealing with an abnormal situation.Validation should have already picked this up. It should be highly probably that a command will succeed.
  • #48 It’ll all compile now, but won’t do anything.
  • #50 Hell no!If we run this now, it’ll build, but our tests wouldn’t pass, as it wouldn’t actually do anything!
  • #51 Why split the state change into two parts? The creation of the event, and its application. Because we will reuse the Apply methods to rebuild the state of the entity.
  • #54 Need to track for the Entity which handlers for its events have been registered.
  • #55 Study in your own time, but it lets us deal with the consequences of a lack of variance when using generics prior to C#4.It
  • #57 But it might not be a problem in your specific situation – you get to decide!
  • #63 And with great power, comes great responsibility!