SlideShare uma empresa Scribd logo
1 de 68
Introduction to CQRS Approaches to System Architecture @NeilRobbins neil@computer.org
Tonights show is brought to by the following #tags #cqrs #glnet Hopefully not #fail, #WTF, or #WasteOfTime
What are we going to cover? Overview of the space CQS, Commands, Queries, DDD Simple CQRS Distributing CQRS Events Pub-Sub Eventual Consistency & Conflict Management Event Sourcing & CQRS What & Why? Some Code – Phew!
Introducing CQS
An interface should be attractive! ,[object Object]
Easy to learn
Easy to remember
Withstand the tests of
Time
Change,[object Object]
Command-Query Separation Principle Asking a question should not change the answer From: Object-Oriented Software Construction, 2nd Edition by Bertrand Meyer
Command-Query Separation Principle public 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 CQS Now: Command Query Responsibility Segregation
So what is CQRS? – Part Deux A trinity Commands Queries Intent capturing UI
Simple CQRS Façadelient Logically distributable Commands Fully Consistent Queries
Commands in CQRS
What does a command look like? CreateAnOrderForTheCustomer ,[object Object]
Specific
Clearly represent client intent
Never past tense
Highly likely to succeed,[object Object]
CQRS Meets DDD But it doesn’t have to Core domain, supporting domains Need not be everywhere, some can be transaction script with Anaemic objects Ubiquitous language can lead to a clean interface Organising by aggregate root can make for simpler persistence Domain 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 Queries No 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 system No binding to domain objects
Domain Objects: Control your mutations Only Command methods exposed They don’t return things either! Controlled mutation of state Ensures that aggregate roots do not enter into invalid states Ever! No IsValid() method (it’d be a query anyway)
Queries in CQRS
So what about Queries? They’re just queries They support the UI which should provide a Decision Support System Get quickly from the database Immutable objects
UI in CQRS
Intentful UI Shopping Cart Service Command Command Ratings Service Command Command Product Images Service Command Command Command Buying Choices Service Offers Service Bought Together Service
Intentful UI Captures Intent Aligns with commands Want to know WHY something changed, not just that it did Decide on an appropriate level of granularity, what’s important to your client/business? Customer address changed Or Customer moved house Or Customer’s address was incorrect
Intentful UI Uses the query infrastructure For display For decision support Should be a pit of success Commands should succeed Will contain logic, is typically rich
Distributing CQRS
Distributing CQRS Introduces some issues Keeping things in sync Faults become more likely Can give some wins Availability Scalability May/Should make some choices CAP Theorum
Introducing Events Something happened Past-tense Capture the details of the change Simple immutable data-structures Can be fed into MapReduce jobs Can be consumed more widely across the enterprise Integrate with other systems Send emails Update DataMarts/Warehouses
Façadelient Bus Commands Eventually Consistent Queries Handler
Keeping the Query Side in Sync Don’t have to forego Consistency Can perform 2 Phase Commits Can use quorums But there’s a price in the face of a fault write performance will degrade restrictive in the face of wide-geographical distribution Can use: RDBMS crazyness ;) PubSub
Subscriber Subscriber Publisher Subscribers Subscriber
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 there http://martinfowler.com/eaaDev/EventSourcing.html 2 Ways to persist changes Snapshot of current state Just the change itself
ID : 123 Name : The Art of War Author: Sun Tzu ISBN: 1234ABCD5678 Event: BookIsbnChanged NewValue: 4321DCBA8765
Time to See Some Code You 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 object namespaceProgNetDemo { public classCatalogItem     {     } } The domain object
A Command Method namespaceProgNetDemo { public classCatalogItem     {  public void Retire()       	{         	}     } } Now we add a behaviour
Some State To Change namespaceProgNetDemo { public classCatalogItem     { privatebool _retired;  public void Retire()       	{         	}     } } Some state that will need to be mutated
Guard the Mutation namespaceProgNetDemo { public classCatalogItem     { privatebool _retired;  public void Retire()       	{ if (!_retired) thrownewInvalidOperationException();         	}     } } Protect the validity of the object!
Make the State Mutation A Domain Event namespaceProgNetDemo { 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 Aggregate public 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 Event publicclassRetiredEvent { privatereadonlyGuid _id; publicRetiredEvent(Guid id)     { 		_id = id; 	} } Don’t need more state, in this case the name says it all Note it’s immutable
Need to be Able to Apply the Event public 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 Class public abstract classAggregateRoot  { protected voidApplyEvent(Event @event) 	{ 	} } All Aggregate Roots will extend this class We’ll need to handle more than just the one type of event!
Create the Event Type public classEvent { } Would want to move the AggregateId into this base class
Have our RetiredEvent Inherit From the Base Type publicclassRetiredEvent : Event { privatereadonlyGuid _id; publicRetiredEvent(Guid id)     { 		_id = id; 	} }
Still Need to Mutate the State public 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 Event public 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 Handler 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; 	} } This could be done following a convention instead Note, this is the specific Event subtype – more later!
Allow Handlers to be Registered public classAggregateRoot  { protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler) whereTEvent : Event 	{ 	} protected voidApplyEvent(Event @event) 	{ 	} } The delegate signature needs to be defined
Create the Delegate Signature public 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 Registry public 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 Blog public 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 Registry public 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 Still 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) 	{ 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 problem Invoke the handler
Need to Track Applied Events public 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 applied Make 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. 1 Need to Expose the events for persistence & publishing Put a GetChanges() method on the AggregateRoot base class
Not Quite – pt. 2 Need to be able to rebuild the Aggregate from history Put a LoadFromHistory(IEnumerable<Event> events) method on the AggregateRoot base class Reapply the events Don’t track them as changes – overload apply event protected voidApplyEvent(Event @event, booltrackAsChange) if (add) _events.Add(@event);
Not Quite – pt. 3 Possibly memento pattern for snapshoting See Mark Nijhof’s blog & sample code on GitHub Possibly use a unit of work pattern for tracking changes

Mais conteúdo relacionado

Semelhante a An Introduction To CQRS

MongoDB Stitch Tutorial
MongoDB Stitch TutorialMongoDB Stitch Tutorial
MongoDB Stitch TutorialMongoDB
 
Developing ASP.NET Applications Using the Model View Controller Pattern
Developing ASP.NET Applications Using the Model View Controller PatternDeveloping ASP.NET Applications Using the Model View Controller Pattern
Developing ASP.NET Applications Using the Model View Controller Patterngoodfriday
 
谷歌 Scott-lessons learned in testability
谷歌 Scott-lessons learned in testability谷歌 Scott-lessons learned in testability
谷歌 Scott-lessons learned in testabilitydrewz lin
 
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
[NDC 2019] Functions 2.0: Enterprise-Grade ServerlessKatyShimizu
 
[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade Serverless[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade ServerlessKatyShimizu
 
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo Ali Parmaksiz
 
CQRS and what it means for your architecture
CQRS and what it means for your architectureCQRS and what it means for your architecture
CQRS and what it means for your architectureRichard Banks
 
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014FalafelSoftware
 
Breaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit TestingBreaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit TestingSteven Smith
 
Dropping ACID - Building Scalable Systems That Work
Dropping ACID - Building Scalable Systems That WorkDropping ACID - Building Scalable Systems That Work
Dropping ACID - Building Scalable Systems That WorkChris Patterson
 
Reactive programming every day
Reactive programming every dayReactive programming every day
Reactive programming every dayVadym Khondar
 
Microservices with .Net - NDC Sydney, 2016
Microservices with .Net - NDC Sydney, 2016Microservices with .Net - NDC Sydney, 2016
Microservices with .Net - NDC Sydney, 2016Richard Banks
 
Symfony2 - from the trenches
Symfony2 - from the trenchesSymfony2 - from the trenches
Symfony2 - from the trenchesLukas Smith
 
Inversion Of Control
Inversion Of ControlInversion Of Control
Inversion Of ControlChad Hietala
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsJeff Durta
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsJeff Durta
 
Imagine a world without mocks
Imagine a world without mocksImagine a world without mocks
Imagine a world without mockskenbot
 

Semelhante a An Introduction To CQRS (20)

CQRS and Event Sourcing
CQRS and Event SourcingCQRS and Event Sourcing
CQRS and Event Sourcing
 
MongoDB Stitch Tutorial
MongoDB Stitch TutorialMongoDB Stitch Tutorial
MongoDB Stitch Tutorial
 
Developing ASP.NET Applications Using the Model View Controller Pattern
Developing ASP.NET Applications Using the Model View Controller PatternDeveloping ASP.NET Applications Using the Model View Controller Pattern
Developing ASP.NET Applications Using the Model View Controller Pattern
 
谷歌 Scott-lessons learned in testability
谷歌 Scott-lessons learned in testability谷歌 Scott-lessons learned in testability
谷歌 Scott-lessons learned in testability
 
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
[NDC 2019] Functions 2.0: Enterprise-Grade Serverless
 
[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade Serverless[NDC 2019] Enterprise-Grade Serverless
[NDC 2019] Enterprise-Grade Serverless
 
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
 
CQRS and what it means for your architecture
CQRS and what it means for your architectureCQRS and what it means for your architecture
CQRS and what it means for your architecture
 
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
 
Breaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit TestingBreaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit Testing
 
Dropping ACID - Building Scalable Systems That Work
Dropping ACID - Building Scalable Systems That WorkDropping ACID - Building Scalable Systems That Work
Dropping ACID - Building Scalable Systems That Work
 
JavaScript Refactoring
JavaScript RefactoringJavaScript Refactoring
JavaScript Refactoring
 
Reactive programming every day
Reactive programming every dayReactive programming every day
Reactive programming every day
 
Microservices with .Net - NDC Sydney, 2016
Microservices with .Net - NDC Sydney, 2016Microservices with .Net - NDC Sydney, 2016
Microservices with .Net - NDC Sydney, 2016
 
Symfony2 - from the trenches
Symfony2 - from the trenchesSymfony2 - from the trenches
Symfony2 - from the trenches
 
Inversion Of Control
Inversion Of ControlInversion Of Control
Inversion Of Control
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applications
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applications
 
Clean Architecture @ Taxibeat
Clean Architecture @ TaxibeatClean Architecture @ Taxibeat
Clean Architecture @ Taxibeat
 
Imagine a world without mocks
Imagine a world without mocksImagine a world without mocks
Imagine a world without mocks
 

Último

Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraDeakin University
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions
 
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024BookNet Canada
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptxLBM Solutions
 
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsSnow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsHyundai Motor Group
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
Science&tech:THE INFORMATION AGE STS.pdf
Science&tech:THE INFORMATION AGE STS.pdfScience&tech:THE INFORMATION AGE STS.pdf
Science&tech:THE INFORMATION AGE STS.pdfjimielynbastida
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
 
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxMaking_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxnull - The Open Security Community
 
Unlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsUnlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsPrecisely
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphNeo4j
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Neo4j
 

Último (20)

Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning era
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food Manufacturing
 
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
The transition to renewables in India.pdf
The transition to renewables in India.pdfThe transition to renewables in India.pdf
The transition to renewables in India.pdf
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptx
 
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsSnow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
Science&tech:THE INFORMATION AGE STS.pdf
Science&tech:THE INFORMATION AGE STS.pdfScience&tech:THE INFORMATION AGE STS.pdf
Science&tech:THE INFORMATION AGE STS.pdf
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
 
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxMaking_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
 
Unlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsUnlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power Systems
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024
 

An Introduction To CQRS

  • 1. Introduction to CQRS Approaches to System Architecture @NeilRobbins neil@computer.org
  • 2. Tonights show is brought to by the following #tags #cqrs #glnet Hopefully not #fail, #WTF, or #WasteOfTime
  • 3. What are we going to cover? Overview of the space CQS, Commands, Queries, DDD Simple CQRS Distributing CQRS Events Pub-Sub Eventual Consistency & Conflict Management Event Sourcing & CQRS What & Why? Some Code – Phew!
  • 5.
  • 10.
  • 11. Command-Query Separation Principle Asking a question should not change the answer From: Object-Oriented Software Construction, 2nd Edition by Bertrand Meyer
  • 12. Command-Query Separation Principle public interface IAmACqsExample { void DoSomething(); void DoSomething(Argument arg); Result GetSomething(); Result GetSomething(Argument arg); }
  • 14. So what is CQRS? Started out as: DDDD (Domain Driven Design + Distributed) Architectural CQS Now: Command Query Responsibility Segregation
  • 15. So what is CQRS? – Part Deux A trinity Commands Queries Intent capturing UI
  • 16. Simple CQRS Façadelient Logically distributable Commands Fully Consistent Queries
  • 18.
  • 22.
  • 23. CQRS Meets DDD But it doesn’t have to Core domain, supporting domains Need not be everywhere, some can be transaction script with Anaemic objects Ubiquitous language can lead to a clean interface Organising by aggregate root can make for simpler persistence Domain object should never be put in an invalid state!
  • 24. 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; } }
  • 25. Domain Objects: No Queries No 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 system No binding to domain objects
  • 26. Domain Objects: Control your mutations Only Command methods exposed They don’t return things either! Controlled mutation of state Ensures that aggregate roots do not enter into invalid states Ever! No IsValid() method (it’d be a query anyway)
  • 28. So what about Queries? They’re just queries They support the UI which should provide a Decision Support System Get quickly from the database Immutable objects
  • 30. Intentful UI Shopping Cart Service Command Command Ratings Service Command Command Product Images Service Command Command Command Buying Choices Service Offers Service Bought Together Service
  • 31. Intentful UI Captures Intent Aligns with commands Want to know WHY something changed, not just that it did Decide on an appropriate level of granularity, what’s important to your client/business? Customer address changed Or Customer moved house Or Customer’s address was incorrect
  • 32. Intentful UI Uses the query infrastructure For display For decision support Should be a pit of success Commands should succeed Will contain logic, is typically rich
  • 34. Distributing CQRS Introduces some issues Keeping things in sync Faults become more likely Can give some wins Availability Scalability May/Should make some choices CAP Theorum
  • 35. Introducing Events Something happened Past-tense Capture the details of the change Simple immutable data-structures Can be fed into MapReduce jobs Can be consumed more widely across the enterprise Integrate with other systems Send emails Update DataMarts/Warehouses
  • 36. Façadelient Bus Commands Eventually Consistent Queries Handler
  • 37. Keeping the Query Side in Sync Don’t have to forego Consistency Can perform 2 Phase Commits Can use quorums But there’s a price in the face of a fault write performance will degrade restrictive in the face of wide-geographical distribution Can use: RDBMS crazyness ;) PubSub
  • 38. Subscriber Subscriber Publisher Subscribers Subscriber
  • 40. Event Sourcing …there are times when we don't just want to see where we are, we also want to know how we got there http://martinfowler.com/eaaDev/EventSourcing.html 2 Ways to persist changes Snapshot of current state Just the change itself
  • 41. ID : 123 Name : The Art of War Author: Sun Tzu ISBN: 1234ABCD5678 Event: BookIsbnChanged NewValue: 4321DCBA8765
  • 42. Time to See Some Code You 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 a domain object namespaceProgNetDemo { public classCatalogItem { } } The domain object
  • 44. A Command Method namespaceProgNetDemo { public classCatalogItem { public void Retire() { } } } Now we add a behaviour
  • 45. Some State To Change namespaceProgNetDemo { public classCatalogItem { privatebool _retired; public void Retire() { } } } Some state that will need to be mutated
  • 46. Guard the Mutation namespaceProgNetDemo { public classCatalogItem { privatebool _retired; public void Retire() { if (!_retired) thrownewInvalidOperationException(); } } } Protect the validity of the object!
  • 47. Make the State Mutation A Domain Event namespaceProgNetDemo { public classCatalogItem { privatebool _retired; public void Retire() { if (!_retired) thrownewInvalidOperationException(); ApplyEvent(new RetiredEvent(_id)); } } } State mutations are a 2 phase activity
  • 48. Need an Identifier for the Aggregate public 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 Event publicclassRetiredEvent { privatereadonlyGuid _id; publicRetiredEvent(Guid id) { _id = id; } } Don’t need more state, in this case the name says it all Note it’s immutable
  • 50. Need to be Able to Apply the Event public 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 Base Class public abstract classAggregateRoot { protected voidApplyEvent(Event @event) { } } All Aggregate Roots will extend this class We’ll need to handle more than just the one type of event!
  • 52. Create the Event Type public classEvent { } Would want to move the AggregateId into this base class
  • 53. Have our RetiredEvent Inherit From the Base Type publicclassRetiredEvent : Event { privatereadonlyGuid _id; publicRetiredEvent(Guid id) { _id = id; } }
  • 54. Still Need to Mutate the State public classCatalogItem : AggregateRoot { privatebool _retired; privateGuid_id; public void Retire() { if (!_retired) thrownewInvalidOperationException(); ApplyEvent(new RetiredEvent(_id)); } }
  • 55. Create a Method to Handle the State Change From the Event public 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 Connect the Event with the Handler 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; } } This could be done following a convention instead Note, this is the specific Event subtype – more later!
  • 58. Allow Handlers to be Registered public classAggregateRoot { protected voidRegisterHandler<TEvent>(AppliesEvent<TEvent> handler) whereTEvent : Event { } protected voidApplyEvent(Event @event) { } } The delegate signature needs to be defined
  • 59. Create the Delegate Signature public 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 Maintain the Registry public 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 Greg Young’s Blog public 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 Maintain the Registry public 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 Apply the Event Still 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) { 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 problem Invoke the handler
  • 64. Need to Track Applied Events public 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 applied Make sure that the events are added.
  • 65. 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; } }
  • 66. Not Quite – pt. 1 Need to Expose the events for persistence & publishing Put a GetChanges() method on the AggregateRoot base class
  • 67. Not Quite – pt. 2 Need to be able to rebuild the Aggregate from history Put a LoadFromHistory(IEnumerable<Event> events) method on the AggregateRoot base class Reapply the events Don’t track them as changes – overload apply event protected voidApplyEvent(Event @event, booltrackAsChange) if (add) _events.Add(@event);
  • 68. Not Quite – pt. 3 Possibly memento pattern for snapshoting See Mark Nijhof’s blog & sample code on GitHub Possibly use a unit of work pattern for tracking changes
  • 69. So what have we done here? A lot of the qualities we have just introduced normally exist at an infrastructure level Transaction/Redo Logs Replication Conflict Management By bringing them into the application we gain great power
  • 70. Benefits of Event Sourcing Already We can build new stores for new services, we have the full set of available information ready We can rebuild stores for existing services We use the same mechanism for our Query Stores as we use for any other service in the Enterprise We aren’t just restricted to storing the Events Can send Emails based on them Can perform Complex Event Processing … The worlds your Oyster, you have the RAW MATERIAL
  • 71. Some Other Benefits of Event Sourcing Also A Very Provable Audit Log Very Simple Horizontal Scaling More Granular Service Boundaries More Explicit Boundaries Can Replay Events in Debugging Scenarios Suits Behaviour Based Testing & Outside-In Development
  • 72. What haven’t we looked at? Compensatory Actions & the Saga pattern How to persist the commands RDBMS? 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 events Potential impact on project management Testing – suits Outside-In & Mockist –see #GOOS …
  • 74. NO SILVER BULLETS The coding is the EASY BIT Don’t need a grand framework The thinking & conversations is the HARD BIT As with any DDD project Need to understand the tradeoffs being made, and why
  • 75. Referenced Material/Links Me - @Neil Robbins – neil@computer.org This Presentation - http://bit.ly/cqrsatgldotnet Greg Youngs: Course – http://bit.ly/gregscourse Blog – http://bit.ly/gregyoungsblog UdiDahans: Course – http://bit.ly/udiscourse Blog – http://bit.ly/udisblog Mark Nijhof’s sample code http://github.com/MarkNijhof/Fohjin Object-Oriented Software Construction, 2nd Edition by Bertrand Meyer

Notas do Editor

  1. 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
  2. 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.
  3. 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.
  4. Decorator patternProxys &amp; InterceptorsCould Aspect Weave – but I wouldn’tWho wants to see some code?
  5. 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.
  6. 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.
  7. 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,
  8. 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
  9. So, CAP TheorumRDBMS crazyness == cross database queries/updates, replication, XML datatype &amp; secondary indexes, pub-sub from the database, and more…
  10. Maintaining the ViewState
  11. First we need a Domain object
  12. Now we give it a behaviour
  13. Now some state to mutate
  14. 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.
  15. It’ll all compile now, but won’t do anything.
  16. Hell no!If we run this now, it’ll build, but our tests wouldn’t pass, as it wouldn’t actually do anything!
  17. 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.
  18. Need to track for the Entity which handlers for its events have been registered.
  19. 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
  20. But it might not be a problem in your specific situation – you get to decide!
  21. And with great power, comes great responsibility!