DDD / CQRS aggregate modelling, read models and bounded contexts communication

791 views
Skip to first unread message

Egor Chikunov

unread,
Aug 25, 2015, 6:55:48 PM8/25/15
to DDD/CQRS
Hello everyone
I've spent some time adapting ddd + cqrs with eventsourcing, read the Blue book, CQRS Journey and Vaughn Vernon's "Implementing Domain-Driven Design" but some areas are still in the dark.

We're writing factory production optimization and management system. I split it to these bounded contexts:
  • Production - everything related to planning, recipes to produce SKU, factories, production lanes etc 
  • Orders - requests and orders management
  • Forecasting - sales forecasting, operations to analyze and transform that forecast
  • Stocks - everything related to stocks, shipments, product distribution etc
  • Integration - integration point with the client's CRM, responsible for passing validated data to the system
  • UI - integration point with users of the system. Web Api to communicate with other parts
and a couple others.

1. There are 2 channels of communication with system: things that user does and things that come from integration. Does it make sense to couple them together?
For instance, now every synchronization with the client's CRM I generate commands based on the new data and send them to our system. 

Pros: 
  • Exactly same command handlers for synchronization with the client's CRM and for users of the system
  • No need to think about separate path for synchronization processing and maintain it
Cons:
  • Users changes come one at the time generating single command while synchronization changes always produce a lot of commands so performance is somewhat slow. I improved that situation a little by merging multiple commands into one "envelope" but i can't merge events because event handlers expect to see separate events.
  • There is no way to tell that synchronization is over because events are coming separately although they were produced by single synchronization 
2. Let's say that we've got multiple entries with information about stocks of some SKU on some DateTime moment. That's history data. Is it right to model it as separate aggregates? What is the best way to handle history series?

3. Let's consider that stocks data from previous question was modeled as aggregate like this:
class StockHistory : AggregateBase
{
Guid SkuId
DateTime Moment
Quantity Quantity
}
so history will consist of the array of that aggregates.
a. What if I need to group it by days? Should I do that in the read model? 
b. If i group it in the read model what to do if some other context is interested in daily stocks? Is it OK for read model to publish events?

4. In the first iteration of development we tried to make every bounded context to collect all necessary information in its own read model based on other BCs events. However it led to large amount of code duplication in event handlers across multiple contexts. For instance, multiple contexts are interested in daily stocks of SKU.
So we make every BC to have its own read model that is responsible for building projections based only on its events and UI read model that collects all events necessary for ui. Is it right approach? 


Thanks

Vadim Platonov

unread,
Aug 26, 2015, 4:39:08 AM8/26/15
to DDD/CQRS
Starting with the last question:


4. In the first iteration of development we tried to make every bounded context to collect all necessary information in its own read model based on other BCs events. However it led to large amount of code duplication in event handlers across multiple contexts. For instance, multiple contexts are interested in daily stocks of SKU.
So we make every BC to have its own read model that is responsible for building projections based only on its events and UI read model that collects all events necessary for ui. Is it right approach? 

You need to figure this out for yourself. In the daily stocks example - if you feel there's too much duplication, create a separate read model of daily stocks which accommodates all of the other BCs.

1. There are 2 channels of communication with system: things that user does and things that come from integration. Does it make sense to couple them together?
For instance, now every synchronization with the client's CRM I generate commands based on the new data and send them to our system. 
Pros: 
  • Exactly same command handlers for synchronization with the client's CRM and for users of the system
  • No need to think about separate path for synchronization processing and maintain it
Cons:
  • Users changes come one at the time generating single command while synchronization changes always produce a lot of commands so performance is somewhat slow. I improved that situation a little by merging multiple commands into one "envelope" but i can't merge events because event handlers expect to see separate events.
  • There is no way to tell that synchronization is over because events are coming separately although they were produced by single synchronization 
The difference between user actions and synchronization is that user actions may fail due to validation and synchronization shouldn't (in the general case). This implies that synchronization can just write events into your streams without going through command handlers. You could create a separate scheduled job that would query the legacy store, construct the needed events and push them to the streams.

2. Let's say that we've got multiple entries with information about stocks of some SKU on some DateTime moment. That's history data. Is it right to model it as separate aggregates? What is the best way to handle history series?

You need aggregates when you have invariants to protect. If you don't - it's just a read model. 

Egor Chikunov

unread,
Aug 26, 2015, 5:53:42 AM8/26/15
to DDD/CQRS
The difference between user actions and synchronization is that user actions may fail due to validation and synchronization shouldn't (in the general case). This implies that synchronization can just write events into your streams without going through command handlers. You could create a separate scheduled job that would query the legacy store, construct the needed events and push them to the streams.

I dont understand how is that possible as command handlers there to ensure some business logic. For instance we could have some rules that should be checked on some action, or maybe calculations. I mean you have to duplicate processing logic in two places if you're going to translate external data straight into the event store even if all of that data is valid.
Still it doesnt change pros and cons as issues with event handlers on read side are still there.
 
You need aggregates when you have invariants to protect. If you don't - it's just a read model. 


But if event store on the write side is the only source of truth, read model should be able to reconstruct at any moment. So to populate a read model I have to have some aggregate on write side.

Ben Kloosterman

unread,
Aug 26, 2015, 5:57:19 AM8/26/15
to ddd...@googlegroups.com
In most cases validations runs before creating commands  eg query read model.



--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Egor Chikunov

unread,
Aug 26, 2015, 6:17:34 AM8/26/15
to DDD/CQRS

In most cases validations runs before creating commands  eg query read model.

Then you have to duplicate validation logic on every client of your system.
For instance there are multiple sources of commands: web api, client's CRM, desktop application. There is no difference for the system what was the source of CreateFactory command as applying rules are the same. Validating ability to send command before sending will require every client to implement that validation.

Secondly, Its not about validation. Lets consider that all incoming data is perfectly valid and we managed to wrote it into event store. 
But what about read side projections? I mean whole point of my question is about overcoming the cons:
1. How to tell that read models processed all of the synchronization data? We've got some heavy calculations that need to run after every synchronization
2. Is it possible to somehow merge all of that events that were published from synchronization to gain performance on read side? 

Peter Hageus

unread,
Aug 26, 2015, 6:40:51 AM8/26/15
to ddd...@googlegroups.com
1. Your projections/readmodels run continuously, regardless of source of events.
2. Yes, you can buffer based on time of number, depends on your performance characteristics. 

/Peter

Ben Kloosterman

unread,
Aug 26, 2015, 7:02:16 AM8/26/15
to ddd...@googlegroups.com
On Wed, Aug 26, 2015 at 8:17 PM, Egor Chikunov <chikun...@gmail.com> wrote:

In most cases validations runs before creating commands  eg query read model.

Then you have to duplicate validation logic on every client of your system.
 
Correct , and this is not unique to CQRS many systems require screens to have this logic duplicated as well as the back end service.

However the CQRS domain is minimal it just as the code to make changes (and what is required to make the decisions) .  This prevents the mess you get in most systems , where your infrastructure code and querying pollutes the domain.

re read model processing that tend to run very fast in fact because there is a single writer and less db contention its often much quicker than a traditional CRUD /OR  system. Its rare that there are heavy calculations here ( I haven't read the earlier tread)  they are normally run before events are created( and before commands return) . However as Peter says its your choice , and its the oldest choice you can have low latency low throughput ( with less consistency issues )  or batch for higher write throughput.

Ben
Reply all
Reply to author
Forward
0 new messages