Intro to Event Sourcing & CQRS


Rich Knight

Modelling Data

Traditional


Order
Id Guid
CustomerId Guid
ShippingAddress String
Placed Bool
Shipped Bool
OrderLine
Id Guid
OrderId Guid
ProductId Int
Quantity Int

Event Sourced


Order Created

 → 

Shipping Address Set

 → 

3 Lines Added

 → 

Line Removed

 → 

Order Placed

 → 

Order Shipped

Current State

Object Oriented

                            

class Order {
    
    Guid Id;
    Guid CustomerId;
    String ShippingAddress;
    Bool OrderPlaced;
    Bool OrderShipped;
    List<OrderLine> Lines;

    public void Apply(OrderCreated e) {
        Id = e.Id;
        CustomerId = e.CustomerId;
    }

    public void Apply(LineAdded e) {
        Lines.Add(new OrderLine(e.OrderLineId, /* ... */);
    }

    //etc
}

Functional



Given a function apply

apply :: State -> Event -> State

Current state is a left fold over previous events

foldl apply EmptyState events


Problem

Show me all orders placed in the last week

Command Query Separation (CQS)


Return something OR mutate state. Never both.


    //Command
void UpdateShippingAddress(string newAddress) {
    if(_shipped)
        throw new InvalidOperationException();

    _shippingAddress = newAddress;
}

//Query - No mutation allowed!
int GetNumberOfLines() {
    return _lines.count();
}
                        

CQRS

Command Query Responsibility Segregation


  • One data model to service queries
  • Another model to handle our business logic
  • Very often these are completely different
Diagram of an ES/CQRS application

Technical Advantages


  • Time travel
  • Performance
  • Scaling
  • Resilience
  • Backups
  • Testability
  • Reduces accidental complexity
              
Given(
  new OrderCreated(id, ...),
  new ShippingAddressChanged(id, "123 Something Road, Somewhere")
)
.When(
  new AddLineToOrder(id, ...)
)
.Expect(
  new OrderLineAdded(id, ...)
);
                       
              
Given(
  new OrderCreated(id, ...),
  new OrderDeleted(id)
)
.When(
  new AddLineToOrder(id, ...)
)
.Expect<InvalidOperationException>();
                       

Business Advantages


  • Talking the same language
  • Flexibility
  • Auditing

Resources