Modeling question - How to achieve validation that requires other aggregates

362 views
Skip to first unread message

Qudoos Chaudhry

unread,
Jul 19, 2016, 10:36:26 PM7/19/16
to DDD/CQRS
Hi, 

I have a requirement where an aggregate needs to have a list of another related aggregate to run some validation logic. 

Currently, the two aggregates are modelled as separate RootAggregates and linked by Id only (no navigation properties). Transactions only impact one aggregate at a time OR they are eventually consistent. 

As an example if I have a Customer and Order aggregate, the way this is modelled is that there is no Customer.Orders. Rather Order has a customer Id. 

If I want to have a rule that says look at the customer's old orders to give him a discount if a products were ordered before, how do I do that? Should I be running this kind of rule through the readmodels (feels dirty).  Should I have some other list on the Customer that has a subset of the data from the orders that I need for the validation? 

Thanks,
Qudoos

Hendry Luk

unread,
Jul 22, 2016, 1:00:55 AM7/22/16
to ddd...@googlegroups.com
This question gets asked a lot. You can search for "set based validation in cqrs" to find more discussions about this. Typical example is username uniqueness.
But the general consensus is that command-handler does not validate this. Instead the client validates this against the read model prior to issuing the command.
Of course, in an eventually-consistent environment, you may have a significant gap which allows you to break this validation, e.g. registering the same username twice. But the assumption is that the likelihood is small enough that you can simply produce an alert on the projection side (e.g. db unique constraint) that an admin will manually resolve, on the basis that it's cheaper than making the system fix every single scenario that can go wrong.
Having said that, I feel that the later point is a bit over sold. Manual intervention in a cqrs system is really easier said than done. In my personal experience, Cqrs is notoriously difficult to make manually overrides on: any change that you need to fix the data within your domain-model or the read model needs to be representable as an event that is understood by the system, which means that you need a process baked-in to your application to resolve this, that can be triggered by the admin when necessary. 
So yea, you'll still need to design and implement a proper resolution logic, which makes the cost-saving argument around after-the-fact manual intervention in cqrs a bit false.

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

Qudoos Chaudhry

unread,
Jul 22, 2016, 10:01:47 AM7/22/16
to DDD/CQRS
Thank you Hendry. 

I've seen quite a few questions on the uniqueness of the username and frankly that scenario is not complicated or even interesting, I've also read Greg's Set based validation post...many times...:)

I probably did a bad job of explaining my scenario. 

appreciate the response. 

Danil Suits

unread,
Jul 23, 2016, 1:13:26 PM7/23/16
to DDD/CQRS
If I want to have a rule that says look at the customer's old orders to give him a discount if a products were ordered before, how do I do that? Should I be running this kind of rule through the readmodels (feels dirty).  Should I have some other list on the Customer that has a subset of the data from the orders that I need for the validation?

What is the cost to the business if you get it wrong?

From what I have learned, the usual answer is "not very much", in which case the aggregate writing the discount uses a domain service to access a stale copy of the collection previously ordered products.  In other words, the aggregate uses the exact values of the command arguments, the exact values of its own state, and a "best guess" at the state that is not part of the aggregate.

If that's good enough, you stop.

If it's not good enough, then the next step is to detect an escalate errors via the read model.  So you put some code into the read model that notices that the discount code seems to be missing for an order that should have it, and escalates the issue.

If that's not good enough, then you also add a detector to watch for discounts that were applied, and shouldn't have been, and write an escalation protocol for those cases.

If that's not good enough, you look into improving the SLA of the domain service to reduce the probability that an inconsistent write will leak through.

If that's not good enough -- if you really do need exact -- then you accept the fact that you modeled the wrong aggregate boundaries, and prepare a rewrite.


Hendry Luk

unread,
Jul 24, 2016, 8:44:57 PM7/24/16
to ddd...@googlegroups.com
I think the same strategies (for username uniqueness) still apply in your case.
I.e. either:
1. you perform the validation by querying the order-history from the projected read-model on the client side prior to issuing the command, or..
2. you model a separate aggregate root that specifically keeps track of the product that has previously been ordered by a particular customer, which will be interrogated while handling the command.

Chris Sampson

unread,
Aug 1, 2016, 11:16:36 AM8/1/16
to ddd...@googlegroups.com

Have a CustomerDiscount aggregate identified by customerid. When ProductOrdered happens get the customerid from it and load CustomerDiscount. Pass the productid from the event to a method on the CustomerDiscount that will check if it has been passed before and if so output CustomerDiscountApplied


--

Ben Kloosterman

unread,
Aug 2, 2016, 5:53:02 AM8/2/16
to ddd...@googlegroups.com
On Wed, Jul 20, 2016 at 12:36 PM, Qudoos Chaudhry <abdul....@gmail.com> wrote:

As an example if I have a Customer and Order aggregate, the way this is modelled is that there is no Customer.Orders. Rather Order has a customer Id. 

If I want to have a rule that says look at the customer's old orders to give him a discount if a products were ordered before, how do I do that? Should I be running this kind of rule through the readmodels (feels dirty).  Should I have some other list on the Customer that has a subset of the data from the orders that I need for the validation? 

Why is this dirty , plenty of business logic in read side  ... run through the read model find what you want then issue commands for the change.. This is also how many business would work find who qualifies than pass the list to someone to implement the discounts., It also sounds like your missing some sort of discount concept. eg set customer to xyz discount policy. 

Ben

Qudoos Chaudhry

unread,
Aug 8, 2016, 12:53:32 PM8/8/16
to DDD/CQRS
Thanks Sammo, 

after thinking for it for a few days, this is the solution I came up with as well. 

I appreciate the rest of the responses too, they all make sense (now that I have thought a lot more about it). 
Reply all
Reply to author
Forward
0 new messages