An intro to cqrs

2,094 views

Published on

Published in: Business, Technology
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,094
On SlideShare
0
From Embeds
0
Number of Embeds
57
Actions
Shares
0
Downloads
24
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide
  • I’m afraid my job title has the word architect in it – so we’re off to an ivory tower for this bit 
  • Boundaries are Explicit Acknowledge potential cost (perf & dev) of going between services: geographical cost trust boundaries execution environments
  • 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.
  • 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.
  • 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.
  • Maintaining the ViewState
  • Capturing all state changes made to an object as Event ObjectsAdvantages:Rebuild state from eventsTemporal Queries – rebuild state to a point in timeDebugging – replay events that led to a problemSnapshotsDisadvantagesCan look a bit magical
  • 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 />

    ×