Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

An intro to cqrs

2,155 views

Published on

Published in: Business, Technology
  • Be the first to comment

An intro to cqrs

  1. 1. DDD South West 2An Introduction to CQRS approaches to system architecture<br />@NeilRobbins<br />neil@computer.org<br />
  2. 2. Aims<br />An overview of CQRS<br />UI<br />Command Handling<br />View Handling<br />How to write an Event Sourced DDD system<br />The command handling bit<br />
  3. 3. But first some code<br />To Visual Studio!<br />
  4. 4. What’s wrong with this?<br /> public class Event<br /> {<br /> public string Name { get; set; }<br /> public int Venue { get; set; }<br /> public DateTime Date { get; set; }<br /> public IEnumerable<Session> Sessions { get; set; }<br /> }<br /> public class Session<br /> {<br /> public PresentationPresentation { get; set; }<br /> public string Location { get; set; }<br /> public DateTime Time { get; set; }<br /> }<br />
  5. 5. But what about Queries?<br />They’re just queries<br />They support the UI which should provide a Decision Support System<br />
  6. 6. Intentful UI<br />Shopping Cart Service<br />Command<br />Command<br />Ratings Service<br />Command<br />Command<br />Product Images Service<br />Command<br />Command<br />Command<br />Buying Choices Service<br />Offers Service<br />Bought Together Service<br />
  7. 7. Intentful UI<br />Captures Intent<br />Aligns with commands<br />Uses the query infrastructure<br />For display<br />For decision support<br />Commands should succeed<br />Will contain logic, is typically rich<br />
  8. 8. Some Slides<br />Big picture & background stuff!<br />
  9. 9. FaçadeClient<br />Bus<br />Commands<br />Eventually<br />Consistent<br />Queries<br />Handler<br />
  10. 10. Subscriber<br />Subscriber<br />Publisher<br />Subscribers<br />Subscriber<br />
  11. 11. ID : 123<br />Name : The Art of War<br />Author: Sun Tzu<br />ISBN: 1234ABCD5678<br />Event: BookIsbnChanged<br />NewValue: 4321DCBA8765<br />
  12. 12. …there are times when we don't just want to see where we are,<br />we also want to know how we got there<br />http://martinfowler.com/eaaDev/EventSourcing.html<br />
  13. 13. Time to See Some Code<br />You didn’t think I’d make you watch me type did you?<br />
  14. 14. Coding for EventSourcing<br />namespaceProgNetDemo<br />{<br />public classCatalogItem<br /> {<br /> }<br />}<br />
  15. 15. A Command Method<br />namespaceProgNetDemo<br />{<br />public classCatalogItem<br /> {<br /> public void Retire()<br /> {<br /> }<br /> }<br />}<br />
  16. 16. Some State To Change<br />namespaceProgNetDemo<br />{<br />public classCatalogItem<br /> {<br />privatebool _retired;<br /> public void Retire()<br /> {<br /> }<br /> }<br />}<br />
  17. 17. Guard the Mutation<br />namespaceProgNetDemo<br />{<br />public classCatalogItem<br /> {<br />privatebool _retired;<br /> public void Retire()<br /> {<br />if (!_retired)<br />thrownewInvalidOperationException();<br /> }<br /> }<br />}<br />
  18. 18. Make the State Mutation A Domain Event<br />namespaceProgNetDemo<br />{<br />public classCatalogItem<br /> {<br />privatebool _retired;<br /> public void Retire()<br /> {<br />if (!_retired)<br />thrownewInvalidOperationException();<br />ApplyEvent(new RetiredEvent(_id));<br /> }<br /> }<br />}<br />
  19. 19. Need an Identifier for the Aggregate<br />public classCatalogItem<br />{<br />privatebool _retired;<br />privateGuid_id;<br /> public void Retire()<br /> {<br />if (!_retired)<br />thrownewInvalidOperationException();<br />ApplyEvent(new RetiredEvent(_id));<br /> }<br />}<br />
  20. 20. Create the Event<br />publicclassRetiredEvent<br />{<br />privatereadonlyGuid _id;<br />publicRetiredEvent(Guid id)<br /> {<br /> _id = id;<br /> }<br />}<br />
  21. 21. Need to be Able to Apply the Event<br />public classCatalogItem<br />{<br />privatebool _retired;<br />privateGuid_id;<br /> public void Retire()<br /> {<br />if (!_retired)<br />thrownewInvalidOperationException();<br />ApplyEvent(new RetiredEvent(_id));<br /> }<br />}<br />
  22. 22. Create a Base Class<br />public classAggregateRoot<br /> {<br />protected voidApplyEvent(Event @event)<br /> {<br /> }<br />}<br />We’ll need to handle more than just the one type of event!<br />
  23. 23. Create the Event Type<br />public classEvent { }<br />
  24. 24. Have our RetiredEvent Inherit From the Base Type<br />publicclassRetiredEvent : Event<br />{<br />privatereadonlyGuid _id;<br />publicRetiredEvent(Guid id)<br /> {<br /> _id = id;<br /> }<br />}<br />
  25. 25. Still Need to Mutate the State<br />public classCatalogItem : AggregateRoot<br />{<br />privatebool _retired;<br />privateGuid_id;<br /> public void Retire()<br /> {<br />if (!_retired)<br />thrownewInvalidOperationException();<br />ApplyEvent(new RetiredEvent(_id));<br /> }<br />}<br />
  26. 26. Create a Method to Handle the State Change From the Event<br />public classCatalogItem : AggregateRoot<br />{<br />privatebool _retired;<br />privateGuid_id;<br /> public void Retire()<br /> {<br />if (!_retired)<br />thrownewInvalidOperationException();<br />ApplyEvent(new RetiredEvent(_id));<br /> }<br />private voidApplyRetiredEvent(RetiredEvent @event)<br /> {<br /> _retired = true;<br /> }<br />}<br />
  27. 27. Done!?<br />public classCatalogItem : AggregateRoot<br />{<br />privatebool _retired;<br />privateGuid_id;<br /> public void Retire()<br /> {<br />if (!_retired)<br />thrownewInvalidOperationException();<br />ApplyEvent(new RetiredEvent(_id));<br /> }<br />private voidApplyRetiredEvent(RetiredEvent @event)<br /> {<br /> _retired = true;<br /> }<br />}<br />
  28. 28. Need to Connect the Event with the Handler<br />public classCatalogItem : AggregateRoot<br />{<br />privatebool _retired;<br />privateGuid_id;<br />publicCatalogItem()<br /> {<br />RegisterHandler<RetiredEvent>(ApplyRetiredEvent);<br /> }<br /> public void Retire()<br /> {<br />if (!_retired)<br />thrownewInvalidOperationException();<br />ApplyEvent(new RetiredEvent(_id));<br /> }<br />private voidApplyRetiredEvent(RetiredEvent @event)<br /> {<br /> _retired = true;<br /> }<br />}<br />
  29. 29. Allow Handlers to be Registered<br />public classAggregateRoot<br /> {<br />protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)<br />whereTEvent : Event<br /> {<br /> }<br />protected voidApplyEvent(Event @event)<br /> {<br /> }<br />}<br />
  30. 30. Create the Delegate Signature<br />public delegate voidAppliesEvent<TEvent>(TEvent @event)<br />where TEvent : Event;<br />public classAggregateRoot<br /> {<br />protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)<br />whereTEvent : Event<br /> {<br /> }<br />protected voidApplyEvent(Event @event)<br /> {<br /> }<br />}<br />
  31. 31. Need to Maintain the Registry<br />public delegate voidAppliesEvent<TEvent>(TEvent @event)<br />where TEvent : Event;<br />public classAggregateRoot<br /> {<br />private readonlyIDictionary<Type, Action<Event>> _handlerRegistry;<br />protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)<br />whereTEvent : Event<br /> {<br />var castHandler = <br /> DelegateAdjuster.CastArgument<Event, TEvent>(e => handler(e));<br /> }<br />protected voidApplyEvent(Event @event)<br /> {<br /> }<br />}<br />
  32. 32. Delegate Adjuster:From Greg’s Blog<br />public class DelegateAdjuster<br />{<br /> public static Action<TBase> CastArgument<TBase, TDerived>(Expression<Action<TDerived>> source) whereTDerived : TBase<br />{<br /> if (typeof(TDerived) ==typeof(TBase))<br />{<br /> return (Action<TBase>)((Delegate)source.Compile());<br />}<br />ParameterExpressionsourceParameter = Expression.Parameter(typeof(TBase),"source");<br />varresult = Expression.Lambda<Action<TBase>>(<br />Expression.Invoke(<br /> source,<br />Expression.Convert(sourceParameter, typeof(TDerived))),<br />sourceParameter);<br /> return result.Compile();<br />}<br />}<br />
  33. 33. Need to Maintain the Registry<br />public delegate voidAppliesEvent<TEvent>(TEvent @event)<br />where TEvent : Event;<br />public classAggregateRoot<br /> {<br />private readonlyIDictionary<Type, Action<Event>> _handlerRegistry;<br />protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)<br />whereTEvent : Event<br /> {<br />var castHandler = <br /> DelegateAdjuster.CastArgument<Event, TEvent>(e => handler(e));<br /> _handlerRegistry.Add(typeof(TEvent), castHandler);<br /> }<br />protected voidApplyEvent(Event @event)<br /> {<br /> }<br />}<br />
  34. 34. Need to Apply the Event Still<br />public delegate voidAppliesEvent<TEvent>(TEvent @event)<br />where TEvent : Event;<br />public classAggregateRoot<br /> {<br />private readonlyIDictionary<Type, Action<Event>> _handlerRegistry;<br />protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)<br />whereTEvent : Event<br /> {<br />var castHandler = <br /> DelegateAdjuster.CastArgument<Event, TEvent>(e => handler(e));<br /> _handlerRegistry.Add(typeof(TEvent), castHandler);<br /> }<br />protected voidApplyEvent(Event @event)<br /> {<br />Action<Event> handler;<br />if (!_handlerRegistry.TryGetValue(@event.GetType(), out handler))<br /> {<br />throw newInvalidOperationException();<br /> }<br /> handler(@event);<br /> }<br />}<br />
  35. 35. Need to Track Applied Events<br />public delegate voidAppliesEvent<TEvent>(TEvent @event)<br />where TEvent : Event;<br />public classAggregateRoot<br /> {<br />private readonlyIDictionary<Type, Action<Event>> _handlerRegistry;<br />private readonlyICollection<Event> _events;<br />protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler)<br />whereTEvent : Event<br /> {<br />var castHandler = <br /> DelegateAdjuster.CastArgument<Event, TEvent>(e => handler(e));<br /> _handlerRegistry.Add(typeof(TEvent), castHandler);<br /> }<br />protected voidApplyEvent(Event @event)<br /> {<br />Action<Event> handler;<br />if (!_handlerRegistry.TryGetValue(@event.GetType(), out handler))<br /> {<br />throw newInvalidOperationException();<br /> }<br /> handler(@event);<br /> _events.Add(@event);<br /> }<br />}<br />
  36. 36. All done now???<br />public classCatalogItem : AggregateRoot<br />{<br />privatebool _retired;<br />privateGuid_id;<br />publicCatalogItem()<br /> {<br />RegisterHandler<RetiredEvent>(ApplyRetiredEvent);<br /> }<br /> public void Retire()<br /> {<br />if (!_retired)<br />thrownewInvalidOperationException();<br />ApplyEvent(new RetiredEvent(_id));<br /> }<br />private voidApplyRetiredEvent(RetiredEvent @event)<br /> {<br /> _retired = true;<br /> }<br />}<br />Could use a convention & just make the AggregateRoot declare which events it produces<br />
  37. 37. Not Quite<br />Need to Expose the events for persistence & publishing<br />Put a GetChanges() method on the AggregateRoot base class<br />Need to be able to rebuild the Aggregate from history<br />Put a LoadFromHistory(IEnumerable<Event> events) method on the AggregateRoot base class<br />Reapply the events<br />Don’t track them as changes – overload apply event<br />protected voidApplyEvent(Event @event, booltrackAsChange)<br />if (add) _events.Add(@event);<br />Possibly memento pattern for snapshoting<br />See Mark Nijhof’s blog & sample code on GitHub<br />
  38. 38. Back to the Whiteboard!<br />Some more on Query Stores<br />
  39. 39. Some Other Benefits of Event Sourcing<br />Already<br />We can build new stores for new services, we have the full set of available information ready<br />We can rebuild stores for existing services<br />We use the same mechanism for our Query Stores as we use for any other service in the Enterprise<br />More Granular Service Boundaries<br />More Explicit Boundaries<br />We aren’t just restricted to storing the Events<br />Can send Emails based on them<br />Can perform Complex Event Processing<br />… The worlds your Oyster, you have the RAW MATERIAL<br />
  40. 40. Some Other Benefits of Event Sourcing<br />Also<br />A Very Provable Audit Log<br />Very Simple Horizontal Scaling<br />More Granular Service Boundaries<br />More Explicit Boundaries<br />Can Replay Events in Debugging Scenarios<br />Suits Behaviour Based Testing & Outside-In Development<br />
  41. 41. NO SILVER BULLETS<br />The coding is the EASY BIT<br />Don’t need a grand framework<br />The thinking & conversations is the HARD BIT<br />
  42. 42. Referenced Material/Links<br />Greg Youngs:<br />Course – http://bit.ly/gregscourse<br />Blog – http://bit.ly/gregyoungsblog<br />UdiDahans:<br />Course – http://bit.ly/udiscourse<br />Blog – http://bit.ly/udisblog<br />Mark Nijhof’s sample code<br />http://github.com/MarkNijhof/Fohjin<br />

×