112
Agile & DDD & CQRS Jeppe Cramon – Partner TigerTeam ApS AANUG konference – September 2013

Agile, Architecture, DDD and CQRS

Embed Size (px)

DESCRIPTION

Each new project starts by choosing technology and framework first. After this they might start looking at what kind of a problem to solve. Unless we are in familiar territory, that is in my opinion quite dangerous. After the selection the technology we try, in the best agile spirit to break problems down into small manageable sizes, for example through TDD. Most often this is done at the expense of our architecture, which ends up being messy and inflexible. TDD works at the micro scale, where as our architecture works on a macro scale. It is significantly more expensive to refactor an architecture than refactor a "unit". In this talk I will show how I see Agile and Domain Driven Design (DDD) work hand in hand to give us the simplest possible solution, by focusing on the domain, our non/cross functional constraints and how this affects our architecture. Often this will involve the identification of several loosely coupled domain-areas that each can be solved with different a architecture. During the talk, I will cover the basic principles of DDD such as Bounded Contexts, Aggregates and various architecture partial solution principles Layered Architecture, Hexagonal (Ports & Adapters) / Onion Architecture, CQRS.

Citation preview

Page 1: Agile, Architecture, DDD and CQRS

Agile & DDD & CQRS

Jeppe Cramon – Partner TigerTeam ApSAANUG konference – September 2013

Page 2: Agile, Architecture, DDD and CQRS

We’re doing a cool new project…

So our first focus is technology

Page 3: Agile, Architecture, DDD and CQRS

We’re building based on…

• Entity Framework• SQL server• RavenDB• Ninject• ASP MVC• AngularJS• ServiceStack

Page 4: Agile, Architecture, DDD and CQRS

So are you building a technology solution?

What’s the architecture?

Data Access

Logic

UI

Page 5: Agile, Architecture, DDD and CQRS

Show me something with business meaning please

Shipping

BillingOrder

Fullfillment

Sales ManagementReporting

Page 6: Agile, Architecture, DDD and CQRS

So what are these boxes?

• Multiple Applications• Application• Component• Module• Class• Function

> 100.000 lines of code?

< 100 lines of code

SIZE MATTERS!

Page 7: Agile, Architecture, DDD and CQRS

But how do we know which it is?

Page 8: Agile, Architecture, DDD and CQRS

What has agile learned us?

Page 9: Agile, Architecture, DDD and CQRS

Start small, quick feedback, do just enough, yagni

Page 10: Agile, Architecture, DDD and CQRS

Ah, so no analysis or thinking?

Page 11: Agile, Architecture, DDD and CQRS

Recommendation: set yourself up for success

• Create a cross functional team with Architect, Developers, Business Analyst, …

• Spend from a few days to a few weeks, depending on project scope/size, and focus on– Carving out requirements– Write stories– Do small spikes– Estimate

• Also known as:– All hands on deck (Coplien)– Inception phase (Unified Process)

Page 12: Agile, Architecture, DDD and CQRS

Competing forces at different stages of development

To begin with• Simplicity and homogenousity• Ease of development• As much as possible close together• Simple prototype cases… as time progresses …• More complex scenarios• Multiple teams• Modularization• Decoupling• Autonomy• ..

Beware of Y

AGNI

too ear

ly

Page 13: Agile, Architecture, DDD and CQRS

But what about TDD?

The last D in TDD is for development NOT design

Page 14: Agile, Architecture, DDD and CQRS

Example: Let’s just start with a simple model

Page 15: Agile, Architecture, DDD and CQRS

One Domain Model to Rule them All?

Combining subdomains into one big domain model increases complexity as

everything tends to be connected to and depend on everything else

Page 16: Agile, Architecture, DDD and CQRS

As time goes by…

Page 17: Agile, Architecture, DDD and CQRS

And it gets more and more complicated

Page 18: Agile, Architecture, DDD and CQRS

And finally we drown

Page 19: Agile, Architecture, DDD and CQRS

ONE MODEL TO RULE THEM ALL

ONE MODEL TO FIND THEM

ONE MODEL TO BRING THEM ALL

AND IN THE DARKNESS BIND THEM

Page 20: Agile, Architecture, DDD and CQRS

Beaware of your Architectural constraints

What are your non-functional (or cross functional)

requirements?

Page 21: Agile, Architecture, DDD and CQRS

One big model = ONE DATABASE

=

Page 22: Agile, Architecture, DDD and CQRS

The database determines our scalability

Page 23: Agile, Architecture, DDD and CQRS

And databases often scale poorly

To solve the performance problems for a few, you have to upgrade it all

Page 24: Agile, Architecture, DDD and CQRS

Do not design for locality and try to distribute

Design for distribution, take advantage of locality

Page 25: Agile, Architecture, DDD and CQRS

Alternatively we can distribute anduse Read replicas

Master Slave SlaveSlaveSlave

Read

Update

You’re now eventually consistent

Page 26: Agile, Architecture, DDD and CQRS

Or introduce caching…

Read

Update

Now you’re both eventual consistent and lack a synchronization mechanism?

?

Page 27: Agile, Architecture, DDD and CQRS

Why spend a lot of money on designing an ACID compliant system

after which you insert a cache to make it eventual consistent?

Page 28: Agile, Architecture, DDD and CQRS

SO WHY IS upfront Macro architecture important?

Because – REFACTORING at the macro level is MUCH more EXPENSIVE than at the micro level

Page 29: Agile, Architecture, DDD and CQRS

Recommendation: Divide and concour

• Start from the outside and model business capabilities

• Identify Business Domains• Design your Macro Architecture around this• Determine communication and interaction

principles• Ensure common monitoring and logging

facilities

Page 30: Agile, Architecture, DDD and CQRS

How can DDD help?

Page 31: Agile, Architecture, DDD and CQRS

Ubiquitous Language &

Bounded Contexts

Page 32: Agile, Architecture, DDD and CQRS

Many perspectives on data

Customer

Retail System

Pricing

Inventory

Sales

Product

Unit PricePromotional PricePromotion End Date

Stock Keeping Unit (SKU)Quantity On Hand (QOH)Location Code

PriceQuantity OrderedName

The lifecycle for Data is VERY important!

Forecasting?

Page 33: Agile, Architecture, DDD and CQRS

Smaller models & clear data ownership

SalesProduct

ProductIDNameDescriptionQuantity Ordered…

Sales

InventoryProduct

ProductIDSKUQOHLocation Code… Inventory

PricingProduct

ProductIDUnit PricePromotional Price…

Pricing

DDD:Bounded Context

SOA:Service

Retail System

Shared Entity identity

Page 34: Agile, Architecture, DDD and CQRS

Context map

Shipping

BillingOrder

Fullfillment

Sales

Management

Reporting

Page 35: Agile, Architecture, DDD and CQRS

Macro vs micro

Shipping

BillingOrder

Fullfillment

Sales

Management

Reporting

Domain Architecture

MacroCommunication

Architecture

MicroArchitecture

Page 36: Agile, Architecture, DDD and CQRS

Macro vs micro architectures• Macro focuses on

– Domain responsibilitiy and capability• Separating unrelated or limited related related business concerns• Focus on ensuring a stable and consistent domain models• ”Build for replacement” instead of ”Build for reuse”

– Integration Protocols– UI Integration– Data formats– Logging and Monitoring

• Micro focuses on– Programming languages – Frameworks– Tools– Database– Patterns (e.g. Layers)– DRY– YAGNI

Differ

ent l

ifecy

cles

Page 37: Agile, Architecture, DDD and CQRS

Be aware of Conways law

Organizational structure and Architecture follow each other.Think about that when building teams. Center them around

business capabilities/bounded contexts

Page 38: Agile, Architecture, DDD and CQRS

A bounded-context contains all parts related to it

DB Schema

Domain Services

Application Services

DomainModel

Aggregates, Entities,

Value Objects, Events

Integration Endpoints

(REST, SOAP,

Pub/Sub)

User Interface

Silo

Page 39: Agile, Architecture, DDD and CQRS

Improving our internal architecture by sound design principles

Page 40: Agile, Architecture, DDD and CQRS

CRUD

Page 41: Agile, Architecture, DDD and CQRS

IT exists to support the business by support various usecases

Usecases can be categorized as either• ”User” intent to manipulate information• ”User” intent to find and read information

We already support this in OO at a Property level:• Setter (or mutator) methods Manipulate data• Getter (or accessor) methods Read data

Let’s raise it to a higher Level

Page 42: Agile, Architecture, DDD and CQRS

CQS Separation of

functions that write &

functions that read

Functions that write are called Command methods and must not return a value

Functions that read are called Query methods and must have no side effects

Page 43: Agile, Architecture, DDD and CQRS

CQS from a code perspectivepublic class CustomerService{ // Commands void MakeCustomerPreferred (CustomerId) void ChangeCustomerLocale(CustomerId, NewLocale) void CreateCustomer(Customer) void EditCustomerDetails(CustomerDetails)

// Queries Customer GetCustomer(CustomerId) CustomerSet GetCustomersWithName(Name) CustomerSet GetPreferredCustomers()}

From: https://gist.github.com/1964094

Page 44: Agile, Architecture, DDD and CQRS

SO WHAT IS CQRS?

CQRS is simply the creation of two objects where there was previously only one

Page 45: Agile, Architecture, DDD and CQRS

CQRSIn its simplest form

public class CustomerWriteService{ // Commands void MakeCustomerPreferred (CustomerId) void ChangeCustomerLocale(CustomerId, NewLocale) void CreateCustomer(CreateCustomer) void EditCustomerDetails(CustomerDetails)}

public class CustomerReadService{ // Queries Customer GetCustomer(CustomerId) CustomerSet GetCustomersWithName(Name) CustomerSet GetPreferredCustomers()}

From: https://gist.github.com/1964094

Page 46: Agile, Architecture, DDD and CQRS

CommandsA Command’s primary goal is to capture USER INTENT

A Command supports a single usecase and targets a single Aggregate

Commands are stated in the imperative:– DoStuff– CreateCustomer– ShipOrder– AddComment– DeleteComment

Page 47: Agile, Architecture, DDD and CQRS

Terminology: AggregatesWhat:• Cluster coherent Entities

and Value Objects, with complex associations into Aggregates with well defined boundaries.

• Choose one entity to be root and control access to objects inside the boundary through the root.

• External objects hold references to the root

• Aggregates only refer to other aggregates by identity (their id)

Motivation:Control invariants and consistency through the aggregate root.Enables: Loading schemes, coarse grained locking, consistency/transactional boundaries for Distributed DDD

Root

Page 48: Agile, Architecture, DDD and CQRS

Aggregate rules and invariants• A single transaction only changes one Aggregate• Aggregate to Aggregate references are by Id only• An Aggregate must ensure Consistency using at least Optimistic

Concurrency, so we can spot overlapping updates• Using Event Sourcing for your Aggregate can allow even more fine

grained Concurrency Control by merging overlapping changes that are not in conflict

• At the cost of extreme scalability it will be preferable if an Aggregate can track and guarantee complete ordering of all changes to it self (e.g. provide a sequential Event sequence as a simple counter)

• Business Transactions involving more than 1 aggregate must be implemented as a Process (Choreographed or Orchestrated). Otherwise we limit our scalability tremendously

Page 49: Agile, Architecture, DDD and CQRS

CQRS requires a New UI paradigmeTask-based/Inductive UI

• A depart from the traditional WHAT UI (CRUD)• Task based UI’s focuses on HOW the user wants

to use the application• Guides users through the work process• The UI becomes an intrinsic part of the design• The UI design directly affects our commands

and thereby our transactional boundaries• Should be preferably use asynchronous

Commands* See New UI Paradigm slide deck for more

Page 50: Agile, Architecture, DDD and CQRS

New UI paradigm

Page 51: Agile, Architecture, DDD and CQRS

Going from CRUD to Task based UI

CRUD style

Page 52: Agile, Architecture, DDD and CQRS

Going from CRUD to Task based UI

Task based style

Page 53: Agile, Architecture, DDD and CQRS

Going from CRUD to Task based UI

Task based style dialog interaction when pressing the Assign Todo button – capturing the users intent

Page 54: Agile, Architecture, DDD and CQRS

This means we need to look at what architecture structure that fit this approach

Page 55: Agile, Architecture, DDD and CQRS

Thin Layers of complexity

UI Data

Pro: Easy and simple

Con: Scales poorly for complex domains

Language of the

database

Page 56: Agile, Architecture, DDD and CQRS

Language of a Layered application

UIApplicatio

nDomain Data

Pro: Handles complex domains better by Separating usecase and domain logic

Con: Increasing complexity. Risk of AnemicDomain model combined with transaction-script.

Time

Tran

sacti

on sc

ript

Real domain model

Com

plex

ity

Language of DTO’s

Language of getters

and setters

Language of the DB

Page 57: Agile, Architecture, DDD and CQRS

Aneamic Domain Model

The Good parts• All Order logic is

called in ONE class• Lower entry level for

junior developers

The bad parts• Hard to test• Hard to maintain• Code dupliction• An exposed domain model is like

walking around without clothes – everyone can access your private parts and violate your invariants

Page 58: Agile, Architecture, DDD and CQRS

We need a rich domain model

Aggregate with Order as Aggregate Root

• Order forms our Unit of consistency, which controls our invariants• Order and OrderLines are created, updated and deleted together

Page 59: Agile, Architecture, DDD and CQRS

We need to department from traditional layered architecture

UI

Business Logic

Data Infr

ast

ruct

ure

Page 60: Agile, Architecture, DDD and CQRS

Problems with the classical layered architecture

• Each subsequent layers depends on the layers beneath it• Typically each layer also depends on some kind of infrastructure layer• This creates a very tights coupling of which the biggest is that

– UI and Business Logic depends on data access logic (transitive)– UI can’t function if Business Logic isn’t there– Business Logic can’t function if Data access logic isn’t there– This makes the data access logic our sour point– Because Business Logic and the Domain model directly depend on the Data

access logic + infrastructure you can be sure to have Data access logic creeping in everywhere

– This makes changing the data access logic very hard and expensive– If coupling prevents easily changing parts of the system, then the normal

choice is to let the system fall behind and this is how legacy systems become stale, and eventually they are rewritten.

Page 61: Agile, Architecture, DDD and CQRS

Alternative visualization of a Layered architecture

Infrastructure

Data Access Logic

Business Logic

User Interface

Tests

DB File

WebService

The application is built around data access and other infrastructure. Because of this coupling, when data access, web services, etc. change, the business logic layer will also have to change

Page 62: Agile, Architecture, DDD and CQRS

Clean Architecture / Onion Architecture

Domain Model

(Entities, Aggregate

s)

Use Cases

Controllers

Dev

ices

External Interfaces

DB

FileWebService

Pre

sente

rs

Gateways

Web

DB UI

Enterprise Business RulesApplication Business RulesInterface Adapters

Frameworks & Drivers

Page 63: Agile, Architecture, DDD and CQRS

Hexagonal Architecture (Ports & Adapters)

ADomain

Model

ApplicationTr

igge

r da

taAdm

inis

trat

i

onN

otification

s

Database

Test

GUI

http

app-to-

app

Test

Wire feed

http feed

Mock

Telephon

e

Email

Answerin

g

Machine

Mock Database

Database

Incoming ports

Outgoing ports

AdaptersAdapters

Use Case boundary

Message transformation occurs in the

Adapter

Page 64: Agile, Architecture, DDD and CQRS

Hexagonal Architecture example

Customer MasterDomain &

Business Logic

<<WebService>>PrivateCustomerServ

ice

<<WebService>>SearchPrivateCusto

merService

<<WebService>>AddressService

<<WebService>>PrivateCustomerEventRetrievalService

<<OSB:WebService>>

PrivateCustomerEventReceiverServi

ce

Customer Support UI

EventPublish

er

JPABasedEventStore

HornetQ

CustomerViewCustomerVi

ewCustomerView Oracle

Oracle

Mail/SMS

sender

Customer Master Subdomain/context

Page 65: Agile, Architecture, DDD and CQRS

Each Service/Business-Domain is its own Hexagons/Onions

Product Catalog

Inventory

Shipping

Pricing

Sales

Page 66: Agile, Architecture, DDD and CQRS

LET’S HAVE A LOOK AT A DIFFERENT WAY OF BUILDING A SOLUTION THAT SUPPORTS THE

ONION ARCHITECUTURE

Page 67: Agile, Architecture, DDD and CQRS

Circular architecture

Separation of Concerns

Task basedUI

Command

Domain ModelRead Model

Query

Page 68: Agile, Architecture, DDD and CQRS

1. RealizationCommands and Queries

support very different usecases

Page 69: Agile, Architecture, DDD and CQRS

A single model cannot be appropriate for reporting, searching and transactional behavior

Page 70: Agile, Architecture, DDD and CQRS

2. realizationQuery results contain only data – no logic

• Give me the customer with his last 10 orders• Give me the customer with total sum of orders

for the last year• Give me the complete order• Give me the shipping status for the order• Give me the customers last review

So why go through the domain layer?UI Application Data

Page 71: Agile, Architecture, DDD and CQRS

Things to think about in relation to Queries

• Why take the pain of performing JOINS and/or UNIONS of our nicely normalized Relational Model?

• Why not having a completely denormalized data model (or View) to support our Query?

• When we completely denormalize our data, do we really need a relational database or could we use a Key/Value- or Document Store?

• Do we really need our Read Data to be to the microsecond consistent with our Write data?

Page 72: Agile, Architecture, DDD and CQRS

So how is this relevant in the real world?

Let’s look at a real-life scenario

Page 73: Agile, Architecture, DDD and CQRS

Collaborative domains

1. Read 2. Read

4. Update3. Coffee time

5. Stale data

Page 74: Agile, Architecture, DDD and CQRS

We’re working with stale data

Optimist

ic Lo

cking Exc

eption

Page 75: Agile, Architecture, DDD and CQRS

We’re always working with stale data!

20 ms

1 ms

100 ms

100 ms100 ms

1 ms

20 ms

At this point our data is at least120 ms stale

+Thinking time

+ thinking time (1000-1500 ms)

Page 76: Agile, Architecture, DDD and CQRS

LET’S USE STALE DATA TO OUR ADVANTAGE BY OFFLOADING THE DATABASE BY USING OUR READ

MODELS

UIApplicatio

nDomain Write

model

Commands – Change data

UIApplicatio

nRead

models

Queries – Ask for data (no side effects)

Page 77: Agile, Architecture, DDD and CQRS

ALL WE NEED IS A GOOD WAY TO SYNCHRONIZE OUR WRITE AND READ MODELS

UIApplicatio

nDomain Write

model

Commands – Change data

UIApplicatio

nRead

models

Queries – Ask for data (no side effects)

Page 78: Agile, Architecture, DDD and CQRS

Let’s make the implicit explicit!

Business EventsWhich:• Signal that something has happened• Closely aligned to the Domain Model• Are handled by a messaging system• They are in the past tense:

– CustomerBilled– ParcelShipped– CustomerCreated– ReviewCreated– CommentAdded– CommentDeleted

Page 79: Agile, Architecture, DDD and CQRS

Commands & Events

Commands mutate Aggregate state which results in one or more Events being

issued/published.

Command Event(s)

AcceptOrder OrderAccepted

ShipOrder OrderShipped

AddComment CommentAdded

QuarantineReview ReviewQuarantined

UnquarantineReview ReviewUnquarantined

Page 80: Agile, Architecture, DDD and CQRS

With Domain Events we now have a mechanism to support our denormalized View/Query models

Page 81: Agile, Architecture, DDD and CQRS

Read modelRead model

Commands, Events and Query Models

Commands

Events

Queries

UI

Domain modelQuery model

”AcceptOrder”

command

”OrderAccepted”event

”Find all Accepted Orders”Query

Commands are Imperative: DoStuffEvents are Past tense: StuffDone

Page 82: Agile, Architecture, DDD and CQRS

Messaging ArchitecturesMost CQRS implementations see Commands and

Events as (asynchronous) Messagespublic class CreateCustomer{ public final Guid CustomerId; public final Name CustomerName; …}

public class CustomerCreated{ public final Guid CustomerId; public final DateTime CreationDateTime; public final Name CustomerName;}

Command

Event

Difference:

INTENT

Page 83: Agile, Architecture, DDD and CQRS

CQRS Building blocks

Client

CommandsCommand

Bus

Sends

Command Handlers

Modify

Repositories

Read Write

DatastoreEvent

Bus

Publish Events

EventBus

Command Services

Event HandlersEvents

Read storeQuery Facade

Query HandlersQuery Results

Queries

Query Services

Events

Domain

Page 84: Agile, Architecture, DDD and CQRS

LET’S TAKE IT A STEP FURTHER

Page 85: Agile, Architecture, DDD and CQRS

Event Sourcing

Entities/Aggregates track their own Domain Events and derive state from them

OrderCreated ProductAdded

ProductAdded

ProductRemoved

ProductAdded

OrderAccepted

Time

07:39

Time

07:40

Time

07:41

Time

07:45

Time

07:46

Time

07:50

Page 86: Agile, Architecture, DDD and CQRS

With Event Sourcing we have solved auditing and bi-temporal history

Page 87: Agile, Architecture, DDD and CQRS

Full CQRSWith EventSourcing

UI Domain

EventStore

Commands – Change data

Commands Events

SQL DB Document DB Graph DB

UI Data

Queries – Ask for data

EventsQuery Build

Our single source of truth

Page 88: Agile, Architecture, DDD and CQRS

public class CustomerCommandHandler { private Repository<Customer> customerRepository;

public CustomerCommandHandler(Repository<Customer> customerRepository) { this.customerRepository = customerRepository; }

@CommandHandler public void handle(UnsignCustomer cmd) { Customer customer = repository.load(cmd.getCustomerId()); customer.unsign(); }}

public class Customer { private boolean signedUp;

public void unsign() { if (signedUp) { apply(new CustomerUnsignedEvent()); } } @EventHandler private void handle(CustomerUnsignedEvent event) { signedUp = false; }}

Page 89: Agile, Architecture, DDD and CQRS

Testing CQRS BDD style@Testpublic void a_signedup_customer_can_unsign() { UUID customerId = UUID.randomUuid().toString();

FixtureConfiguration fixture = Fixtures.newGivenWhenThenFixture(); fixture.registerAnnotatedCommandHandler( new CustomerCommandHandler(fixture.createGenericRepository(Customer.class)) ); fixture.setAggregateIdentifier(customerId);

fixture .given( TestFactory.customerCreatedEvent(customerId), TestFactory.customerSignedUpEvent(customerId) ) .when( TestFactory.unsignCustomerCommand(customerId) ) .expectEvents( TestFactory.customerUnsignedEvent(customerId) );}

Page 90: Agile, Architecture, DDD and CQRS

CQRS / BDD Report generated based on the previous test

Scenario: A signed-up customer can unsign

Given a Customer with id ”abc1234” that has been created and a customer with id ”abc1234” that is signed-up

When a request for Customer with id ”abcd1234” to be unsigned is received

Then the Customer with id ”abcd1234” is unsigned

Page 91: Agile, Architecture, DDD and CQRS

SOA / Integration

Commands, Queries and Events form natural integration interfaces

So integration is baked in from the beginning!

Page 92: Agile, Architecture, DDD and CQRS

What about Scalability?CAP Theorem• Consistency: All nodes in the cluster see

exactly the same data at any point in time• Availability: Failure of a node does not render

the entire system inoperative• Partition tolerance: Nodes can still function

when communication with other groups of nodes is lost

You can’t have all three!

Page 93: Agile, Architecture, DDD and CQRS

CAP theorem

Source: http://bit.ly/QUurnY

Strong Weak Eventual Consistency

ACID systems are hard and expensive to

scale

Latency concerns

Unless you use Pessimistic

Locking – all data is stale

(and eventual consistent

when delivered to the user)

Page 94: Agile, Architecture, DDD and CQRS

BASE Basically Available Soft-state Eventually consistent

Eventually Consistency levels:• Causal consistency: This involves a signal being sent from

between application session indicating that a change has occurred. From that point on the receiving session will always see the updated value.

• Read your own writes: In this mode of consistency, a session that performs a change to the database will immediately see that change, even if other sessions experience a delay.

• Monotonic consistency: In this mode, A session will never see data revert to an earlier point in time. Once we read a value, we will never see an earlier value.

See http://www.allthingsdistributed.com/2008/12/eventually_consistent.html for more

Page 95: Agile, Architecture, DDD and CQRS

CQRS can give us BASE at the architectural level through

asynchronous Commands and Events

This gives the business and IT control over where to spend

money to scaleour solution - instead of trying

to buy a bigger database server.

Page 96: Agile, Architecture, DDD and CQRS

What about Validation

• Happens in the UI before Commands are sent• Typically validated using Read models• Helps to ensure that commands don’t often

fail • Validates simple things (values ranges, unique

keys/values)• Doesn’t enforce business logic rules

Page 97: Agile, Architecture, DDD and CQRS

CQRS in .NET with no Frameworks

Page 98: Agile, Architecture, DDD and CQRS

IBus

public interface IBus : ICommandSender, IEventPublisher{

void RegisterHandler<T>(Action<T> handler, DispatchType dispatchType) where T : IMessage;}

public interface ICommandSender{

void Send<T>(T command) where T : Command;}

public interface IEventPublisher{

void Publish(IEnumerable<Event> events);}

Page 99: Agile, Architecture, DDD and CQRS

CommandHandlerpublic class ProjectCommandHandlers{ private readonly EventStore _eventStore;

public ProjectCommandHandlers(EventStore eventStore, IBus bus) { _eventStore = eventStore; bus.RegisterHandler<CreateNewProject>(Handle, DispatchType.ThreadPooled); bus.RegisterHandler<ChangeProjectAnnotations>(Handle, DispatchType.ThreadPooled); }

public void Handle(CreateNewProject cmd) { if (_eventStore.HasAggregateWithId(cmd.AggregateId)) throw new ProjectAlreadyCreatedException(cmd.AggregateId);

var project = new Project(cmd.AggregateId, cmd.Name, cmd.Description);

_eventStore.AppendEventsToStream(cmd.AggregateId, project.EventsOccured); }

public void Handle(ChangeProjectAnnotations cmd) { var project = new Project(_eventStore.LoadEventStream(cmd.AggregateId).Events); project.ChangeProjectAnnotations(cmd.Annotations); _eventStore.AppendEventsToStream(cmd.AggregateId, project.EventsOccured); }}

Page 100: Agile, Architecture, DDD and CQRS

Project Aggregate

public class Project: Aggregate<ProjectState>{ private readonly ProjectState _state;

public Project(IEnumerable<DomainEvent> events) { _state = new ProjectState(events); }

public Project(Guid aggregateId, string name, string description) { _state = new ProjectState(); ApplyEvent(new NewProjectCreated(aggregateId, name, description)); }

public void ChangeProjectAnnotations(Annotations annotations) { ApplyEvent(new ProjectAnnotationsChanged(AggregateId, annotations)); }}

Page 101: Agile, Architecture, DDD and CQRS

Aggregatepublic abstract class Aggregate<T> : IAggregate where T : IAggregateState{ private readonly IList<DomainEvent> _eventsOccurred = new List<DomainEvent>(); public abstract Guid AggregateId { get; }

public IList<DomainEvent> EventsOccured { get { return _eventsOccurred; } }

protected void ApplyEvent(DomainEvent @event) { @event.EventSequenceNumber = CreateNextEventSequenceNumber(); @event.UserId = UserContext.Instance.LoggedInUserId; _eventsOccurred.Add(@event); State.ApplyEvent(@event); }

/// <summary> /// Creates a new EventSequenceNumber (has sideeffects) /// </summary> /// <returns>the new sequence number</returns> private long CreateNextEventSequenceNumber() { return State.LastEventSequenceNumber + 1; }

protected abstract T State { get; }}

Page 102: Agile, Architecture, DDD and CQRS

Project Aggregate Statepublic class ProjectState : AggregateState{ private Guid _aggregateId; public string ProjectName { get; private set; } public string Description { get; private set; } public Annotations Annotations { get; private set; }

public ProjectState() {}

public ProjectState(IEnumerable<DomainEvent> events) { foreach (var @event in events) { ApplyEvent(@event); } }

public void Apply(NewProjectCreated pce) { _aggregateId = pce.AggregateId; ProjectName = pce.Name; Description = pce.Description; }

public void Apply(ProjectAnnotationsChanged pac) { Annotations = pac.Annotations; }

public override Guid AggregateId { get { return _aggregateId; } }

}

Page 103: Agile, Architecture, DDD and CQRS

EventStore

Page 104: Agile, Architecture, DDD and CQRS

EventStore

public abstract class EventStore : IEnumerable<IEvent>{ public void AppendEventsToStream(Guid id, ICollection<DomainEvent> events) { InternalAppendEventsToStream(id, IGNORE_VERSION_AND_APPEND, events); _eventPublisher.Publish(events); }}

Page 105: Agile, Architecture, DDD and CQRS

Projections/EventHandlerspublic class ProjectsList : CachedAggregatedView<Guid, ProjectListItem>, IProjectsList{ public ProjectsList(IBus bus, ILocalRepository repository) : base(repository) { bus.RegisterHandler<NewProjectCreated>(Handle, DispatchType.Direct); bus.RegisterHandler<ProjectAnnotationsChanged>(Handle, DispatchType.Direct); }

protected void Handle(NewProjectCreated e) { var projectListItem = new ProjectListItem(e.AggregateId, e.Name, e.Description); projectListItem.LastEventSequenceNumber = e.EventSequenceNumber; projectListItem.IsAvailableLocally = true; projectListItem.LastSavedByUserId = e.UserId; projectListItem.OwnerOrganizationId = e.OwnerOrganizationId; this[e.AggregateId] = projectListItem; }

protected void Handle(ProjectAnnotationsChanged e) { ProjectListItem item = this[e.AggregateId]; item.LastEventSequenceNumber = e.EventSequenceNumber; item.Annotations = e.Annotations; item.IsAvailableLocally = true; item.LastSavedByUserId = e.UserId; this[e.AggregateId] = item; }}

Page 106: Agile, Architecture, DDD and CQRS

Classical CQRS challenge

Page 107: Agile, Architecture, DDD and CQRS

Set based operations

• How to ensure that no two persons have same– Username– Email address– Mobile phone number– Social security number

• Tough choice between Consistency/Eventual consistency – performance and how much you trust your clients

Page 108: Agile, Architecture, DDD and CQRS

Axon has decent support for this through the UnitOfWork - continued

public class PersonCommandHandler {private void reserveUniqueKeys(String userNameToReserve, String emailAddressToReserve,

String socialSecurityNumberToReserver, String mobilePhoneNumberToReserver, String actorId, final UnitOfWork unitOfWork) {

ReservationControl reservationControl =uniquePersonKeysRepository.reservereUniqueKeys(

userNameToReserve,emailAddressToReserve,socialSecurityNumberToReserver,mobilePhoneNumberToReserver,aktoerId);

if (reservationControl.isReservationOk()) {duringErrorCancelReservationsOfUniqueKeys(

userNameToReserve,emailAddressToReserve,socialSecurityNumberToReserver,mobilePhoneNumberToReserver,aktoerId,unitOfWork);

}else {

throw new UniquePersonKeysAreAlreadyTakenException(userNameToReserve,emailAddressToReserve,socialSecurityNumberToReserver,mobilePhoneNumberToReserver,reservationControl);

}}

private void duringErrorCancelReservationsOfUniqueKeys(final String userName, final String emailAddress, final String socialSecurityNumber, final String mobilePhoneNumber, final String

actorId, final UnitOfWork unitOfWork) {

unitOfWork.registerListener(new UnitOfWorkListenerAdapter() {@Overridepublic void onRollback(Throwable failureCause) {

uniquePersonKeysRepository.cancelReservationOfUniqueKeys(userName, emailAddress,

socialSecurityNumber, mobilePhoneNumber, actorId);}

});}

}

Page 109: Agile, Architecture, DDD and CQRS

This is a 99,99% solution

• Reservation and Cancellation of UniqueKeys occurs in a separate transaction (Requires_new)– We could potentially used Nested Transactions using JDBC

SavePoints but not all database supports this• Therefore, if the creation process fails for some reason

we need to cancel our unique key reservations• This can fail for the same reason that the creation

process failed, in which case our reservation persist• So there’s no way around handling clean up periodically

(either based on timers / triggers / events / etc.)

Page 110: Agile, Architecture, DDD and CQRS

Experiences using CQRS• Some what overkill for this solution if there wasn’t a requirement for local

caches in the source systems• Forced us to focus on the domain and follow proper modeling practices

– Commands are fantastic for capturing user intent• Clean code and architecture by focusing on a single aspect at a time

– Formal separation of “bounded contexts” – Driven by consistency boundaries– Loose coupling through events – Model per view/usecase

• Refactoring and learning friendly• Very easy to test• Task based UI makes for a very understandable UI compared to CRUD (but also

takes more time to develop)

Page 111: Agile, Architecture, DDD and CQRS

Experiences using CQRS - continued

• Learning curve was harder for some developers– Breaking old bad habits– Handling business logic in Aggregate event

handlers is NO NO• Eventual consistency is tricky – don’t go

overboard

Page 112: Agile, Architecture, DDD and CQRS

Questions?@jeppec on [email protected]

@TigerTeamDK on Twitterhttp://tigerteam.dk