SlideShare a Scribd company logo
1 of 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

More Related Content

Similar to 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
 

Similar to 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
 

Recently uploaded

New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxNavinnSomaal
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersRaghuram Pandurangan
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningLars Bell
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 

Recently uploaded (20)

New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptx
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information Developers
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
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
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine Tuning
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 

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

Editor's Notes

  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!