An Introduction To CQRS

8,973 views
8,797 views

Published on

An introduction to the CQRS (Command Query Responsibility Segregation) style of application architecture. Also touching on CQS (command query separation), Event Sourcing, Pub-Sub, and a little DDD.

Published in: Technology
0 Comments
25 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
8,973
On SlideShare
0
From Embeds
0
Number of Embeds
16
Actions
Shares
0
Downloads
0
Comments
0
Likes
25
Embeds 0
No embeds

No notes for slide
  • 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
  • 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.
  • 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.
  • Decorator patternProxys & InterceptorsCould Aspect Weave – but I wouldn’tWho wants to see some code?
  • 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<Session> 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.
  • 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.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,
  • 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
  • So, CAP TheorumRDBMS crazyness == cross database queries/updates, replication, XML datatype & secondary indexes, pub-sub from the database, and more…
  • Maintaining the ViewState
  • First we need a Domain object
  • Now we give it a behaviour
  • Now some state to mutate
  • 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.
  • It’ll all compile now, but won’t do anything.
  • Hell no!If we run this now, it’ll build, but our tests wouldn’t pass, as it wouldn’t actually do anything!
  • 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.
  • Need to track for the Entity which handlers for its events have been registered.
  • 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
  • But it might not be a problem in your specific situation – you get to decide!
  • And with great power, comes great responsibility!
  • An Introduction To CQRS

    1. 1. Introduction to CQRS Approaches to System Architecture<br />@NeilRobbins<br />neil@computer.org<br />
    2. 2. Tonights show is brought to by the following #tags<br />#cqrs<br />#glnet<br />Hopefully not<br />#fail, #WTF, or #WasteOfTime<br />
    3. 3. What are we going to cover?<br />Overview of the space<br />CQS, Commands, Queries, DDD<br />Simple CQRS<br />Distributing CQRS<br />Events<br />Pub-Sub<br />Eventual Consistency & Conflict Management<br />Event Sourcing & CQRS<br />What & Why?<br />Some Code – Phew!<br />
    4. 4. Introducing CQS<br />
    5. 5. An interface should be attractive!<br /><ul><li>Simple
    6. 6. Easy to learn
    7. 7. Easy to remember
    8. 8. Withstand the tests of
    9. 9. Time
    10. 10. Change</li></li></ul><li>Command-Query Separation Principle<br />Functions should not produce abstract side effects<br />From: Object-Oriented Software Construction, 2nd Edition by Bertrand Meyer<br />
    11. 11. Command-Query Separation Principle<br />Asking a question should not change the answer<br />From: Object-Oriented Software Construction, 2nd Edition by Bertrand Meyer<br />
    12. 12. Command-Query Separation Principle<br />public interface IAmACqsExample<br />{<br /> void DoSomething();<br /> void DoSomething(Argument arg);<br /> Result GetSomething();<br /> Result GetSomething(Argument arg);<br />}<br />
    13. 13. Introducing CQRS<br />
    14. 14. So what is CQRS?<br />Started out as:<br />DDDD (Domain Driven Design + Distributed)<br />Architectural CQS<br />Now:<br />Command<br />Query<br />Responsibility<br />Segregation<br />
    15. 15. So what is CQRS? – Part Deux<br />A trinity<br />Commands<br />Queries<br />Intent capturing UI<br />
    16. 16. Simple CQRS<br />FaçadeClient<br />Logically distributable<br />Commands<br />Fully<br />Consistent<br />Queries<br />
    17. 17. Commands in CQRS<br />
    18. 18. What does a command look like?<br />CreateAnOrderForTheCustomer<br /><ul><li>Imperative
    19. 19. Specific
    20. 20. Clearly represent client intent
    21. 21. Never past tense
    22. 22. Highly likely to succeed</li></li></ul><li>Command Handling<br />Get the decorators in!<br />
    23. 23. CQRS Meets DDD<br />But it doesn’t have to<br />Core domain, supporting domains<br />Need not be everywhere, some can be transaction script with Anaemic objects<br />Ubiquitous language can lead to a clean interface<br />Organising by aggregate root can make for simpler persistence<br />Domain object should never be put in an invalid state!<br />
    24. 24. Domain Objects: Forget Properties!<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 />
    25. 25. Domain Objects: No Queries<br />No Queries on your domain objects!<br />Yes, that definitely includes any getters!<br />Nada! (well maybe one, we’ll see that much later)<br />All querying will be done through the Query system<br />No binding to domain objects<br />
    26. 26. Domain Objects: Control your mutations<br />Only Command methods exposed<br />They don’t return things either!<br />Controlled mutation of state<br />Ensures that aggregate roots do not enter into invalid states<br />Ever! No IsValid() method (it’d be a query anyway)<br />
    27. 27. Queries in CQRS<br />
    28. 28. So what about Queries?<br />They’re just queries<br />They support the UI which should provide a Decision Support System<br />Get quickly from the database<br />Immutable objects<br />
    29. 29. UI in CQRS<br />
    30. 30. 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 />
    31. 31. Intentful UI<br />Captures Intent<br />Aligns with commands<br />Want to know WHY something changed, not just that it did<br />Decide on an appropriate level of granularity, what’s important to your client/business?<br />Customer address changed<br />Or<br />Customer moved house<br />Or<br />Customer’s address was incorrect<br />
    32. 32. Intentful UI<br />Uses the query infrastructure<br />For display<br />For decision support<br />Should be a pit of success<br />Commands should succeed<br />Will contain logic, is typically rich<br />
    33. 33. Distributing CQRS<br />
    34. 34. Distributing CQRS<br />Introduces some issues<br />Keeping things in sync<br />Faults become more likely<br />Can give some wins<br />Availability<br />Scalability<br />May/Should make some choices<br />CAP Theorum<br />
    35. 35. Introducing Events<br />Something happened<br />Past-tense<br />Capture the details of the change<br />Simple immutable data-structures<br />Can be fed into MapReduce jobs<br />Can be consumed more widely across the enterprise<br />Integrate with other systems<br />Send emails<br />Update DataMarts/Warehouses<br />
    36. 36. FaçadeClient<br />Bus<br />Commands<br />Eventually<br />Consistent<br />Queries<br />Handler<br />
    37. 37. Keeping the Query Side in Sync<br />Don’t have to forego Consistency<br />Can perform 2 Phase Commits<br />Can use quorums<br />But<br />there’s a price in the face of a fault<br />write performance will degrade<br />restrictive in the face of wide-geographical distribution<br />Can use:<br />RDBMS crazyness ;)<br />PubSub<br />
    38. 38. Subscriber<br />Subscriber<br />Publisher<br />Subscribers<br />Subscriber<br />
    39. 39. Event Sourcing & CQRS<br />
    40. 40. Event Sourcing<br />…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 />2 Ways to persist changes<br />Snapshot of current state<br />Just the change itself<br />
    41. 41. ID : 123<br />Name : The Art of War<br />Author: Sun Tzu<br />ISBN: 1234ABCD5678<br />Event: BookIsbnChanged<br />NewValue: 4321DCBA8765<br />
    42. 42. Time to See Some Code<br />You didn’t think I’d make you watch me type did you?<br />A small taste of Greg Young’s course (with minor changes) – link at the end.<br />
    43. 43. First up a domain object<br />namespaceProgNetDemo<br />{<br />public classCatalogItem<br /> {<br /> }<br />}<br />The domain object<br />
    44. 44. A Command Method<br />namespaceProgNetDemo<br />{<br />public classCatalogItem<br /> {<br /> public void Retire()<br /> {<br /> }<br /> }<br />}<br />Now we add a behaviour<br />
    45. 45. Some State To Change<br />namespaceProgNetDemo<br />{<br />public classCatalogItem<br /> {<br />privatebool _retired;<br /> public void Retire()<br /> {<br /> }<br /> }<br />}<br />Some state that will need to be mutated<br />
    46. 46. 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 />Protect the validity of the object!<br />
    47. 47. 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 />State mutations are a 2 phase activity<br />
    48. 48. 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 />The event needs to know the Id of the Entity being updated<br />
    49. 49. Create the Event<br />publicclassRetiredEvent<br />{<br />privatereadonlyGuid _id;<br />publicRetiredEvent(Guid id)<br /> {<br /> _id = id;<br /> }<br />}<br />Don’t need more state, in this case the name says it all<br />Note it’s immutable<br />
    50. 50. 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 />Still need to write the code to apply the event.<br />
    51. 51. Create a Base Class<br />public abstract classAggregateRoot<br /> {<br />protected voidApplyEvent(Event @event)<br /> {<br /> }<br />}<br />All Aggregate Roots will extend this class<br />We’ll need to handle more than just the one type of event!<br />
    52. 52. Create the Event Type<br />public classEvent { }<br />Would want to move the AggregateId into this base class<br />
    53. 53. 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 />
    54. 54. 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 />
    55. 55. 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 />This method will actually apply the event, that is mutate the state<br />
    56. 56. 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 />
    57. 57. 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 />This could be done following a convention instead<br />Note, this is the specific Event subtype – more later!<br />
    58. 58. 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 />The delegate signature needs to be defined<br />
    59. 59. 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 />
    60. 60. 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 />Will keep a list of handlers for each type of event.<br />We’re storing the Action<Event>, but we want to call an Action<RetiredEvent><br />
    61. 61. Delegate Adjuster:From Greg Young’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 />
    62. 62. 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 />Store the handler having downcast it<br />
    63. 63. Need to Apply the Event Still<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 />If no handler for a mutation is registered, it’s a problem<br />Invoke the handler<br />
    64. 64. 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 />Track the events that are being applied<br />Make sure that the events are added.<br />
    65. 65. 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 />
    66. 66. Not Quite – pt. 1<br />Need to Expose the events for persistence & publishing<br />Put a GetChanges() method on the AggregateRoot base class<br />
    67. 67. Not Quite – pt. 2<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 />
    68. 68. Not Quite – pt. 3<br />Possibly memento pattern for snapshoting<br />See Mark Nijhof’s blog & sample code on GitHub<br />Possibly use a unit of work pattern for tracking changes<br />
    69. 69. So what have we done here?<br />A lot of the qualities we have just introduced normally exist at an infrastructure level<br />Transaction/Redo Logs<br />Replication<br />Conflict Management<br />By bringing them into the application we gain great power<br />
    70. 70. 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 />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 />
    71. 71. 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 />
    72. 72. What haven’t we looked at?<br />Compensatory Actions & the Saga pattern<br />How to persist the commands<br />RDBMS? DocumentDB? HBase/Cassandra/Riak? File System?<br />Fit with an SOA approach (it’s very good)<br />Extreme scaling (for load, for geographies, for minimal latency)<br />Versioning events<br />Potential impact on project management<br />Testing – suits Outside-In & Mockist –see #GOOS<br />…<br />
    73. 73. In Conclusion<br />
    74. 74. 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 />As with any DDD project<br />Need to understand the tradeoffs being made, and why<br />
    75. 75. Referenced Material/Links<br />Me - @Neil Robbins – neil@computer.org<br />This Presentation - http://bit.ly/cqrsatgldotnet<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 />Object-Oriented Software Construction, 2nd Edition by Bertrand Meyer<br />

    ×