an intro to cqrs

42
DDD South West 2 An Introduction to CQRS approaches to system architecture @NeilRobbins [email protected]

Upload: neil-robbins

Post on 29-Jun-2015

1.830 views

Category:

Business


6 download

TRANSCRIPT

Page 1: An intro to cqrs

DDD South West 2An Introduction to CQRS approaches to system architecture

@[email protected]

Page 2: An intro to cqrs

Aims

An overview of CQRS UI Command Handling View Handling

How to write an Event Sourced DDD system The command handling bit

Page 3: An intro to cqrs

But first some code

To Visual Studio!

Page 4: An intro to cqrs

What’s wrong with this?

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 Presentation Presentation { get; set; } public string Location { get; set; } public DateTime Time { get; set; } }

Page 5: An intro to cqrs

But what about Queries?

They’re just queries They support the UI which should

provide a Decision Support System

Page 6: An intro to cqrs

Intentful UIShopping

Cart Service

Buying Choices Service

Offers Service

Bought Together Service

Ratings Service

Product Images Service

Command

Command

Command

Command

Command

CommandCommand

Page 7: An intro to cqrs

Intentful UI

Captures Intent Aligns with commands

Uses the query infrastructure For display For decision support▪ Commands should succeed

Will contain logic, is typically rich

Page 8: An intro to cqrs

Some Slides

Big picture & background stuff!

Page 9: An intro to cqrs

Commands

Queries

Faça

de\C

lient

Handler

Bus

EventuallyConsistent

Page 10: An intro to cqrs

Publisher

Subscribers

Subscriber

Subscriber

Subscriber

Page 11: An intro to cqrs

ID : 123Name : The Art of WarAuthor: Sun TzuISBN: 1234ABCD5678

Event: BookIsbnChangedNewValue: 4321DCBA8765

Page 12: An intro to cqrs

…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

Page 13: An intro to cqrs

Time to See Some Code

You didn’t think I’d make you watch me type did you?

Page 14: An intro to cqrs

Coding for EventSourcing

namespace ProgNetDemo{ public class CatalogItem { }}

Page 15: An intro to cqrs

A Command Method

namespace ProgNetDemo{ public class CatalogItem { public void Retire() { } }}

Page 16: An intro to cqrs

Some State To Change

namespace ProgNetDemo{ public class CatalogItem {

private bool _retired;

public void Retire() { } }}

Page 17: An intro to cqrs

Guard the Mutation

namespace ProgNetDemo{ public class CatalogItem {

private bool _retired;

public void Retire() {

if (!_retired)throw new InvalidOperationException();

} }}

Page 18: An intro to cqrs

Make the State Mutation A Domain Event

namespace ProgNetDemo{ public class CatalogItem {

private bool _retired;

public void Retire() {

if (!_retired)throw new InvalidOperationException();

ApplyEvent(new RetiredEvent(_id)); } }}

Page 19: An intro to cqrs

Need an Identifier for the Aggregate

public class CatalogItem{

private bool _retired; private Guid _id;

public void Retire(){

if (!_retired)throw new InvalidOperationException();

ApplyEvent(new RetiredEvent(_id)); }}

Page 20: An intro to cqrs

Create the Event

public class RetiredEvent{

private readonly Guid _id;

public RetiredEvent(Guid id) {

_id = id;}

}

Page 21: An intro to cqrs

Need to be Able to Apply the Event

public class CatalogItem{

private bool _retired; private Guid _id;

public void Retire(){

if (!_retired)throw new InvalidOperationException();

ApplyEvent(new RetiredEvent(_id)); }}

Page 22: An intro to cqrs

Create a Base Class

public class AggregateRoot { protected void ApplyEvent(Event

@event){}

}

We’ll need to handle more than

just the one type of event!

Page 23: An intro to cqrs

Create the Event Type

public class Event { }

Page 24: An intro to cqrs

Have our RetiredEvent Inherit From the Base Type

public class RetiredEvent : Event{

private readonly Guid _id;

public RetiredEvent(Guid id) {

_id = id;}

}

Page 25: An intro to cqrs

Still Need to Mutate the Statepublic class CatalogItem : AggregateRoot{

private bool _retired; private Guid _id;

public void Retire(){ if (!_retired)throw new InvalidOperationException();ApplyEvent(new RetiredEvent(_id));

}}

Page 26: An intro to cqrs

Create a Method to Handle the State Change From the Event

public class CatalogItem : AggregateRoot{

private bool _retired; private Guid _id;

public void Retire(){

if (!_retired)throw new InvalidOperationException();

ApplyEvent(new RetiredEvent(_id)); }

private void ApplyRetiredEvent(RetiredEvent @event){

_retired = true;}

}

Page 27: An intro to cqrs

Done!?

public class CatalogItem : AggregateRoot{

private bool _retired; private Guid _id;

public void Retire(){

if (!_retired)throw new InvalidOperationException();

ApplyEvent(new RetiredEvent(_id)); }

private void ApplyRetiredEvent(RetiredEvent @event){

_retired = true;}

}

Page 28: An intro to cqrs

Need to Connect the Event with the Handler

public class CatalogItem : AggregateRoot{

private bool _retired; private Guid _id;

public CatalogItem(){

RegisterHandler<RetiredEvent>(ApplyRetiredEvent);}

public void Retire(){

if (!_retired)throw new InvalidOperationException();

ApplyEvent(new RetiredEvent(_id)); }

private void ApplyRetiredEvent(RetiredEvent @event){

_retired = true;}

}

Page 29: An intro to cqrs

Allow Handlers to be Registered public class AggregateRoot { protected void RegisterHandler<TEvent>(AppliesEvent<TEvent>

handler)where TEvent : Event

{}

protected void ApplyEvent(Event @event){}

}

Page 30: An intro to cqrs

Create the Delegate Signaturepublic delegate void AppliesEvent<TEvent>(TEvent @event)

where TEvent : Event; public class AggregateRoot { protected void

RegisterHandler<TEvent>(AppliesEvent<TEvent> handler)where TEvent : Event

{}

protected void ApplyEvent(Event @event){}

}

Page 31: An intro to cqrs

Need to Maintain the Registrypublic delegate void AppliesEvent<TEvent>(TEvent @event)

where TEvent : Event; public class AggregateRoot { private readonly IDictionary<Type, Action<Event>> _handlerRegistry;

protected void RegisterHandler<TEvent>(AppliesEvent<TEvent> handler)where TEvent : Event{var castHandler = DelegateAdjuster.CastArgument<Event, TEvent>(e => handler(e));}

protected void ApplyEvent(Event @event){}

}

Page 32: An intro to cqrs

Delegate Adjuster:From Greg’s Blog

public class DelegateAdjuster{ public static Action<TBase> CastArgument<TBase,

TDerived>(Expression<Action<TDerived>> source) where TDerived : TBase { if (typeof(TDerived) == typeof(TBase)) { return (Action<TBase>)((Delegate)source.Compile());

} ParameterExpression sourceParameter = Expression.Parameter(typeof(TBase),

"source"); var result = Expression.Lambda<Action<TBase>>( Expression.Invoke( source, Expression.Convert(sourceParameter, typeof(TDerived))), sourceParameter); return result.Compile(); }}

Page 33: An intro to cqrs

Need to Maintain the Registrypublic delegate void AppliesEvent<TEvent>(TEvent @event)

where TEvent : Event; public class AggregateRoot { private readonly IDictionary<Type, Action<Event>> _handlerRegistry;

protected void RegisterHandler<TEvent>(AppliesEvent<TEvent> handler)where TEvent : Event

{var castHandler = DelegateAdjuster.CastArgument<Event, TEvent>(e => handler(e)); _handlerRegistry.Add(typeof(TEvent), castHandler);

}

protected void ApplyEvent(Event @event){}

}

Page 34: An intro to cqrs

Need to Apply the Event Stillpublic delegate void AppliesEvent<TEvent>(TEvent @event)

where TEvent : Event; public class AggregateRoot { private readonly IDictionary<Type, Action<Event>> _handlerRegistry;

protected void RegisterHandler<TEvent>(AppliesEvent<TEvent> handler)where TEvent : Event

{var castHandler = DelegateAdjuster.CastArgument<Event, TEvent>(e => handler(e)); _handlerRegistry.Add(typeof(TEvent), castHandler);

}

protected void ApplyEvent(Event @event){

Action<Event> handler;if (!_handlerRegistry.TryGetValue(@event.GetType(), out handler)){

throw new InvalidOperationException();}handler(@event);

}}

Page 35: An intro to cqrs

Need to Track Applied Eventspublic delegate void AppliesEvent<TEvent>(TEvent @event)

where TEvent : Event; public class AggregateRoot { private readonly IDictionary<Type, Action<Event>> _handlerRegistry;

private readonly ICollection<Event> _events;

protected void RegisterHandler<TEvent>(AppliesEvent<TEvent> handler)where TEvent : Event

{var castHandler = DelegateAdjuster.CastArgument<Event, TEvent>(e => handler(e)); _handlerRegistry.Add(typeof(TEvent), castHandler);

}

protected void ApplyEvent(Event @event){

Action<Event> handler;if (!_handlerRegistry.TryGetValue(@event.GetType(), out handler)){

throw new InvalidOperationException();}handler(@event);_events.Add(@event);

}}

Page 36: An intro to cqrs

All done now???

public class CatalogItem : AggregateRoot{

private bool _retired; private Guid _id;

public CatalogItem(){

RegisterHandler<RetiredEvent>(ApplyRetiredEvent);}

public void Retire(){

if (!_retired)throw new InvalidOperationException();

ApplyEvent(new RetiredEvent(_id)); }

private void ApplyRetiredEvent(RetiredEvent @event){

_retired = true;}

}

Could use a convention & just

make the AggregateRoot

declare which events it produces

Page 37: An intro to cqrs

Not Quite

Need to Expose the events for persistence & publishing Put a GetChanges() method on the AggregateRoot base

class 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 void ApplyEvent(Event @event, bool trackAsChange)

if (add) _events.Add(@event);

Possibly memento pattern for snapshoting See Mark Nijhof’s blog & sample code on GitHub

Page 38: An intro to cqrs

Back to the Whiteboard!

Some more on Query Stores

Page 39: An intro to cqrs

Some Other 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 More Granular Service Boundaries More Explicit Boundaries 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

Page 40: An intro to cqrs

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

Page 41: An intro to cqrs

NO SILVER BULLETS

The coding is the EASY BIT Don’t need a grand framework

The thinking & conversations is the HARD BIT

Page 42: An intro to cqrs

Referenced Material/Links

Greg Youngs: Course – http://bit.ly/gregscourse Blog – http://bit.ly/gregyoungsblog

Udi Dahans: Course – http://bit.ly/udiscourse Blog – http://bit.ly/udisblog

Mark Nijhof’s sample code http://github.com/MarkNijhof/Fohjin