Agile, Architecture, DDD and CQRS

Preview:

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

Agile & DDD & CQRS

Jeppe Cramon – Partner TigerTeam ApSAANUG konference – September 2013

We’re doing a cool new project…

So our first focus is technology

We’re building based on…

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

So are you building a technology solution?

What’s the architecture?

Data Access

Logic

UI

Show me something with business meaning please

Shipping

BillingOrder

Fullfillment

Sales ManagementReporting

So what are these boxes?

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

> 100.000 lines of code?

< 100 lines of code

SIZE MATTERS!

But how do we know which it is?

What has agile learned us?

Start small, quick feedback, do just enough, yagni

Ah, so no analysis or thinking?

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)

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

But what about TDD?

The last D in TDD is for development NOT design

Example: Let’s just start with a simple model

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

As time goes by…

And it gets more and more complicated

And finally we drown

ONE MODEL TO RULE THEM ALL

ONE MODEL TO FIND THEM

ONE MODEL TO BRING THEM ALL

AND IN THE DARKNESS BIND THEM

Beaware of your Architectural constraints

What are your non-functional (or cross functional)

requirements?

One big model = ONE DATABASE

=

The database determines our scalability

And databases often scale poorly

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

Do not design for locality and try to distribute

Design for distribution, take advantage of locality

Alternatively we can distribute anduse Read replicas

Master Slave SlaveSlaveSlave

Read

Update

You’re now eventually consistent

Or introduce caching…

Read

Update

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

?

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

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

SO WHY IS upfront Macro architecture important?

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

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

How can DDD help?

Ubiquitous Language &

Bounded Contexts

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?

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

Context map

Shipping

BillingOrder

Fullfillment

Sales

Management

Reporting

Macro vs micro

Shipping

BillingOrder

Fullfillment

Sales

Management

Reporting

Domain Architecture

MacroCommunication

Architecture

MicroArchitecture

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

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

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

Improving our internal architecture by sound design principles

CRUD

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

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

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

SO WHAT IS CQRS?

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

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

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

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

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

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

New UI paradigm

Going from CRUD to Task based UI

CRUD style

Going from CRUD to Task based UI

Task based style

Going from CRUD to Task based UI

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

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

Thin Layers of complexity

UI Data

Pro: Easy and simple

Con: Scales poorly for complex domains

Language of the

database

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

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

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

We need to department from traditional layered architecture

UI

Business Logic

Data Infr

ast

ruct

ure

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.

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

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

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

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

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

Product Catalog

Inventory

Shipping

Pricing

Sales

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

ONION ARCHITECUTURE

Circular architecture

Separation of Concerns

Task basedUI

Command

Domain ModelRead Model

Query

1. RealizationCommands and Queries

support very different usecases

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

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

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?

So how is this relevant in the real world?

Let’s look at a real-life scenario

Collaborative domains

1. Read 2. Read

4. Update3. Coffee time

5. Stale data

We’re working with stale data

Optimist

ic Lo

cking Exc

eption

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)

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)

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)

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

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

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

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

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

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

LET’S TAKE IT A STEP FURTHER

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

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

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

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; }}

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) );}

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

SOA / Integration

Commands, Queries and Events form natural integration interfaces

So integration is baked in from the beginning!

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!

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)

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

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.

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

CQRS in .NET with no Frameworks

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);}

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); }}

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)); }}

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; }}

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; } }

}

EventStore

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); }}

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; }}

Classical CQRS challenge

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

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);}

});}

}

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.)

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)

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

Questions?@jeppec on Twitterjeppe@tigerteam.dk

@TigerTeamDK on Twitterhttp://tigerteam.dk

Recommended