Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Agile & DDD & CQRS
Jeppe Cramon – Partner TigerTeam ApS
AANUG 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
Billing
Order
Fullfillment
Sales
Management
Reporting
So what are these boxes?
• Multiple Applications
• Application
• Component
• Module
• Class
• Function
> 100.000 lines of ...
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,...
Competing forces at different stages of
development
To begin with
• Simplicity and homogenousity
• Ease of development
• A...
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...
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 and
use Read replicas
Master Slave SlaveSlaveSlave
You’re now eventually consistent
Or introduce caching…
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 consiste...
SO WHY IS upfront Macro architecture important?
Because – REFACTORING at the macro level is MUCH
more EXPENSIVE than at th...
Recommendation: Divide and concour
• Start from the outside and model business
capabilities
• Identify Business Domains
• ...
How can DDD help?
Ubiquitous Language
&
Bounded Contexts
Many perspectives on data
Customer
Retail System
Pricing
Inventory
Sales
Product
Unit Price
Promotional Price
Promotion En...
Smaller models & clear data ownership
Sales
Product
ProductID
Name
Description
Quantity Ordered
…
Sales
Inventory
Product
...
Context map
Shipping
Billing
Order
Fullfillment
Sales
Management
Reporting
Macro vs micro
Shipping
Billing
Order
Fullfillment
Sales
Management
Reporting
Domain
Architecture
Macro
Communication
Arch...
Macro vs micro architectures
• Macro focuses on
– Domain responsibilitiy and capability
• Separating unrelated or limited ...
Be aware of Conways law
Organizational structure and Architecture follow each other.
Think about that when building teams....
A bounded-context contains all parts related to it
DB Schema
Domain
Services
Application
Services
Domain
Model
Aggregates
...
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 man...
CQS
Separation of
functions that write
&
functions that read
Functions that write are called Command methods
and must not ...
CQS from a code perspective
public class CustomerService
{
// Commands
void MakeCustomerPreferred (CustomerId)
void Change...
SO WHAT IS CQRS?
CQRS is simply the creation of two objects
where there was previously only one
CQRS
In its simplest form
public class CustomerWriteService
{
// Commands
void MakeCustomerPreferred (CustomerId)
void Cha...
Commands
A Command’s primary goal is to capture USER INTENT
A Command supports a single usecase and targets a single
Aggre...
Terminology: Aggregates
Invoice
InvoiceLine
*
Account *
What:
• Cluster coherent Entities
and Value Objects, with
complex ...
Aggregate rules and invariants
• A single transaction only changes one Aggregate
• Aggregate to Aggregate references are b...
CQRS requires a New UI paradigme
Task-based/Inductive UI
• A depart from the traditional WHAT UI (CRUD)
• Task based UI’s ...
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 ...
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
UI Application Domain Data
Pro: Handles complex domains better by
Separating usecase and...
Aneamic Domain Model
The Good parts
• All Order logic is called in ONE
class
• Lower entry level for junior
developers
The...
We need a rich domain model
Aggregate with Order as Aggregate Root
• Order forms our Unit of consistency, which controls o...
We need to department from
traditional layered architecture
UI
Business Logic
Data
Infrastructure
Problems with the classical layered
architecture
• Each subsequent layers depends on the layers beneath it
• Typically eac...
Alternative visualization of a Layered architecture
Infrastructur
e
Data Access
Logic
Business Logic
User Interface
Tests
...
Clean Architecture / Onion Architecture
Domain
Model
(Entities,
Aggregates)
Use Cases
Controllers
External
Interfaces
DB
F...
Hexagonal Architecture
(Ports & Adapters)
A
Domain
Model
Application
Incoming
ports
Outgoing
ports
Adapters
Adapters
Use C...
Hexagonal Architecture example
Customer Master
Domain & Business
Logic
<<WebService>>
PrivateCustomerServic
e
<<WebService...
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
Task based
UI
Domain
ModelRead Model
1. Realization
Commands and Queries
support very different usecases
A single model cannot be
appropriate for reporting, searching
and transactional behavior
2. realization
Query results contain only data – no logic
• Give me the customer with his last 10 orders
• Give me the cus...
Things to think about in relation to
Queries
• Why take the pain of performing JOINS and/or
UNIONS of our nicely normalize...
So how is this relevant in the real world?
Let’s look at a real-life scenario
Collaborative domains
1. Read 2. Read
4. Update
3. Coffee time
5. Stale data
We’re working with stale data
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 least
120 ms ...
LET’S USE STALE DATA TO OUR
ADVANTAGE BY OFFLOADING THE
DATABASE BY USING OUR READ MODELS
UI Application Domain Write
mode...
ALL WE NEED IS A GOOD WAY TO
SYNCHRONIZE OUR WRITE AND READ
MODELS
UI Application Domain Write
model
Commands – Change dat...
Let’s make the implicit explicit!
Business Events
Which:
• Signal that something has happened
• Closely aligned to the Dom...
Commands & Events
Commands mutate Aggregate state which
results in one or more Events being
issued/published.
Command Even...
With Domain Events we now have a
mechanism to support our denormalized
View/Query models
Read model
Read model
Commands, Events and Query Models
Events
UI
Domain modelQuery model
”AcceptOrder”
command
”OrderAcce...
Messaging Architectures
Most CQRS implementations see Commands
and Events as (asynchronous) Messages
public class CreateCu...
CQRS Building blocks
Client
Commands
Command
Bus
Sends
Command
Handlers
Modify
Repositories
Read Write
Data
store
Event
Bu...
LET’S TAKE IT A STEP FURTHER
Event Sourcing
Entities/Aggregates track their own Domain Events
and derive state from them
OrderCreated ProductAdded Prod...
With Event Sourcing we have solved
auditing and bi-temporal history
Full CQRS
With EventSourcing
UI Domain
Event
Store
Commands – Change data
Commands Events
SQL DB Document DB Graph DB
UI D...
public class CustomerCommandHandler {
private Repository<Customer> customerRepository;
public CustomerCommandHandler(Repos...
Testing CQRS BDD style
@Test
public void a_signedup_customer_can_unsign() {
UUID customerId = UUID.randomUuid().toString()...
CQRS / BDD Report generated based on the
previous test
Scenario: A signed-up customer can unsign
Given a Customer with id ...
SOA / Integration
Commands, Queries and Events form natural
integration interfaces
So integration is baked in from the beg...
What about Scalability?
CAP Theorem
• Consistency: All nodes in the cluster see
exactly the same data at any point in time...
CAP theorem
Source: http://bit.ly/QUurnY
Strong  Weak  Eventual Consistency
ACID systems
are hard and
expensive to
scale...
BASE
Basically Available Soft-state Eventually consistent
Eventually Consistency levels:
• Causal consistency: This involv...
CQRS can give us BASE at the
architectural level through
asynchronous Commands and Events
This gives the business and IT
c...
What about Validation
• Happens in the UI before Commands are sent
• Typically validated using Read models
• Helps to ensu...
CQRS in .NET with no
Frameworks
IBus
public interface IBus : ICommandSender, IEventPublisher
{
void RegisterHandler<T>(Action<T> handler, DispatchType dis...
CommandHandler
public class ProjectCommandHandlers
{
private readonly EventStore _eventStore;
public ProjectCommandHandler...
Project Aggregate
public class Project: Aggregate<ProjectState>
{
private readonly ProjectState _state;
public Project(IEn...
Aggregate
public abstract class Aggregate<T> : IAggregate where T : IAggregateState
{
private readonly IList<DomainEvent> ...
Project Aggregate State
public class ProjectState : AggregateState
{
private Guid _aggregateId;
public string ProjectName ...
EventStore
EventStore
public abstract class EventStore : IEnumerable<IEvent>
{
public void AppendEventsToStream(Guid id, ICollection<...
Projections/EventHandlers
public class ProjectsList : CachedAggregatedView<Guid, ProjectListItem>, IProjectsList
{
public ...
Classical CQRS challenge
Set based operations
• How to ensure that no two persons have same
– Username
– Email address
– Mobile phone number
– Soci...
Axon has decent support for this through the
UnitOfWork - continued
public class PersonCommandHandler {
private void reser...
This is a 99,99% solution
• Reservation and Cancellation of UniqueKeys occurs in a
separate transaction (Requires_new)
– W...
Experiences using CQRS
• Some what overkill for this solution if there wasn’t a requirement for local
caches in the source...
Experiences using CQRS - continued
• Learning curve was harder for some
developers
– Breaking old bad habits
– Handling bu...
Questions?
@jeppec on Twitterjeppe@tigerteam.dk
@TigerTeamDK on Twitterhttp://tigerteam.dk
Upcoming SlideShare
Loading in …5
×

Agile, Architecture, DDD and CQRS

20,257 views

Published on

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.

Published in: Technology
  • Be the first to comment

Agile, Architecture, DDD and CQRS

  1. 1. Agile & DDD & CQRS Jeppe Cramon – Partner TigerTeam ApS AANUG konference – September 2013
  2. 2. We’re doing a cool new project… So our first focus is technology
  3. 3. We’re building based on… • Entity Framework • SQL server • RavenDB • Ninject • ASP MVC • AngularJS • ServiceStack
  4. 4. So are you building a technology solution? What’s the architecture? Data Access Logic UI
  5. 5. Show me something with business meaning please Shipping Billing Order Fullfillment Sales Management Reporting
  6. 6. So what are these boxes? • Multiple Applications • Application • Component • Module • Class • Function > 100.000 lines of code? < 100 lines of code SIZE MATTERS!
  7. 7. But how do we know which it is?
  8. 8. What has agile learned us?
  9. 9. Start small, quick feedback, do just enough, yagni
  10. 10. Ah, so no analysis or thinking?
  11. 11. 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)
  12. 12. 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 • ..
  13. 13. But what about TDD? The last D in TDD is for development NOT design
  14. 14. Example: Let’s just start with a simple model
  15. 15. 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
  16. 16. As time goes by…
  17. 17. And it gets more and more complicated
  18. 18. And finally we drown
  19. 19. ONE MODEL TO RULE THEM ALL ONE MODEL TO FIND THEM ONE MODEL TO BRING THEM ALL AND IN THE DARKNESS BIND THEM
  20. 20. Beaware of your Architectural constraints What are your non-functional (or cross functional) requirements?
  21. 21. One big model = ONE DATABASE =
  22. 22. The database determines our scalability
  23. 23. And databases often scale poorly To solve the performance problems for a few, you have to upgrade it all
  24. 24. Do not design for locality and try to distribute Design for distribution, take advantage of locality
  25. 25. Alternatively we can distribute and use Read replicas Master Slave SlaveSlaveSlave You’re now eventually consistent
  26. 26. Or introduce caching… Now you’re both eventual consistent and lack a synchronization mechanism? ?
  27. 27. Why spend a lot of money on designing an ACID compliant system after which you insert a cache to make it eventual consistent?
  28. 28. SO WHY IS upfront Macro architecture important? Because – REFACTORING at the macro level is MUCH more EXPENSIVE than at the micro level
  29. 29. 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
  30. 30. How can DDD help?
  31. 31. Ubiquitous Language & Bounded Contexts
  32. 32. Many perspectives on data Customer Retail System Pricing Inventory Sales Product Unit Price Promotional Price Promotion End Date Stock Keeping Unit (SKU) Quantity On Hand (QOH) Location Code Price Quantity Ordered Name The lifecycle for Data is VERY important! Forecasting?
  33. 33. Smaller models & clear data ownership Sales Product ProductID Name Description Quantity Ordered … Sales Inventory Product ProductID SKU QOH Location Code … Inventory Pricing Product ProductID Unit Price Promotional Price … Pricing DDD: Bounded Context SOA: Service Retail System Shared Entity identity
  34. 34. Context map Shipping Billing Order Fullfillment Sales Management Reporting
  35. 35. Macro vs micro Shipping Billing Order Fullfillment Sales Management Reporting Domain Architecture Macro Communication Architecture Micro Architecture
  36. 36. 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
  37. 37. 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
  38. 38. A bounded-context contains all parts related to it DB Schema Domain Services Application Services Domain Model Aggregates , Entities, Value Objects, Events Integration Endpoints (REST, SOAP, Pub/Sub) User Interface Silo
  39. 39. Improving our internal architecture by sound design principles
  40. 40. CRUD
  41. 41. 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
  42. 42. 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
  43. 43. CQS from a code perspective public 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
  44. 44. SO WHAT IS CQRS? CQRS is simply the creation of two objects where there was previously only one
  45. 45. CQRS In 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
  46. 46. Commands A 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
  47. 47. Terminology: Aggregates Invoice InvoiceLine * Account * What: • 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
  48. 48. 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
  49. 49. CQRS requires a New UI paradigme Task-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
  50. 50. New UI paradigm
  51. 51. Going from CRUD to Task based UI CRUD style
  52. 52. Going from CRUD to Task based UI Task based style
  53. 53. Going from CRUD to Task based UI Task based style dialog interaction when pressing the Assign Todo button – capturing the users intent
  54. 54. This means we need to look at what architecture structure that fit this approach
  55. 55. Thin Layers of complexity UI Data Pro: Easy and simple Con: Scales poorly for complex domains Language of the database
  56. 56. Language of a Layered application UI Application Domain Data Pro: Handles complex domains better by Separating usecase and domain logic Con: Increasing complexity. Risk of Anemic Domain model combined with transaction-script. Time Complexity Language of DTO’s Language of getters and setters Language of the DB
  57. 57. 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
  58. 58. 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
  59. 59. We need to department from traditional layered architecture UI Business Logic Data Infrastructure
  60. 60. 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.
  61. 61. Alternative visualization of a Layered architecture Infrastructur e Data Access Logic Business Logic User Interface Tests DB File WebServic e 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
  62. 62. Clean Architecture / Onion Architecture Domain Model (Entities, Aggregates) Use Cases Controllers External Interfaces DB FileWebServic e Enterprise Business Rule Application Business Rules Interface Adapters Frameworks & Drivers
  63. 63. Hexagonal Architecture (Ports & Adapters) A Domain Model Application Incoming ports Outgoing ports Adapters Adapters Use Case boundary Message transformation occurs in the Adapter
  64. 64. Hexagonal Architecture example Customer Master Domain & Business Logic <<WebService>> PrivateCustomerServic e <<WebService>> SearchPrivateCustomer Service <<WebService>> AddressService <<WebService>> PrivateCustomerEventR etrievalService <<OSB:WebService >> PrivateCustomerEve ntReceiverService Customer Support UI Event Publishe r JPABased EventStore HornetQ CustomerVie wCustomerVie wCustomerVie w Oracle Oracle Mail/SMS sender Customer Master Subdomain/context
  65. 65. Each Service/Business-Domain is its own Hexagons/Onions Product Catalog Inventory Shipping Pricing Sales
  66. 66. LET’S HAVE A LOOK AT A DIFFERENT WAY OF BUILDING A SOLUTION THAT SUPPORTS THE ONION ARCHITECUTURE
  67. 67. Circular architecture Task based UI Domain ModelRead Model
  68. 68. 1. Realization Commands and Queries support very different usecases
  69. 69. A single model cannot be appropriate for reporting, searching and transactional behavior
  70. 70. 2. realization Query 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
  71. 71. 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?
  72. 72. So how is this relevant in the real world? Let’s look at a real-life scenario
  73. 73. Collaborative domains 1. Read 2. Read 4. Update 3. Coffee time 5. Stale data
  74. 74. We’re working with stale data
  75. 75. 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 least 120 ms stale + Thinking time + thinking time (1000-1500 ms)
  76. 76. LET’S USE STALE DATA TO OUR ADVANTAGE BY OFFLOADING THE DATABASE BY USING OUR READ MODELS UI Application Domain Write model Commands – Change data UI Application Read models Queries – Ask for data (no side effects)
  77. 77. ALL WE NEED IS A GOOD WAY TO SYNCHRONIZE OUR WRITE AND READ MODELS UI Application Domain Write model Commands – Change data UI Application Read models Queries – Ask for data (no side effects)
  78. 78. Let’s make the implicit explicit! Business Events Which: • 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
  79. 79. 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
  80. 80. With Domain Events we now have a mechanism to support our denormalized View/Query models
  81. 81. Read model Read model Commands, Events and Query Models Events UI Domain modelQuery model ”AcceptOrder” command ”OrderAccepted ” event ”Find all Accepted Orders” Query Commands are Imperative: DoStuff Events are Past tense: StuffDone
  82. 82. Messaging Architectures Most CQRS implementations see Commands and Events as (asynchronous) Messages public 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
  83. 83. CQRS Building blocks Client Commands Command Bus Sends Command Handlers Modify Repositories Read Write Data store Event Bus Command Services Event Handlers Events Read store Query HandlersQuery Results Queries Query Services Events Domain
  84. 84. LET’S TAKE IT A STEP FURTHER
  85. 85. Event Sourcing Entities/Aggregates track their own Domain Events and derive state from them OrderCreated ProductAdded ProductAdded ProductRemoved ProductAdded OrderAccepte d Time 07:39 Time 07:40 Time 07:41 Time 07:45 Time 07:46 Time 07:50
  86. 86. With Event Sourcing we have solved auditing and bi-temporal history
  87. 87. Full CQRS With EventSourcing UI Domain Event Store Commands – Change data Commands Events SQL DB Document DB Graph DB UI Data Queries – Ask for data Events Query Build Our single source of truth
  88. 88. 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; } }
  89. 89. Testing CQRS BDD style @Test public 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) ); }
  90. 90. 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
  91. 91. SOA / Integration Commands, Queries and Events form natural integration interfaces So integration is baked in from the beginning!
  92. 92. 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!
  93. 93. 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)
  94. 94. 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
  95. 95. 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 scale our solution - instead of trying to buy a bigger database server.
  96. 96. 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
  97. 97. CQRS in .NET with no Frameworks
  98. 98. 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); }
  99. 99. CommandHandler public 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); } }
  100. 100. 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)); } }
  101. 101. Aggregate public 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; } }
  102. 102. Project Aggregate State public 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; } } }
  103. 103. EventStore
  104. 104. 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); } }
  105. 105. Projections/EventHandlers public 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; } }
  106. 106. Classical CQRS challenge
  107. 107. 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
  108. 108. 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() { @Override public void onRollback(Throwable failureCause) { uniquePersonKeysRepository.cancelReservationOfUniqueKeys(userName, emailAddress, socialSecurityNumber, mobilePhoneNumber, actorId); } }); } }
  109. 109. 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.)
  110. 110. 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)
  111. 111. 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
  112. 112. Questions? @jeppec on Twitterjeppe@tigerteam.dk @TigerTeamDK on Twitterhttp://tigerteam.dk

×