CQRS - Read side needs to communicate with the Domain

470 views
Skip to first unread message

Gary

unread,
Apr 19, 2011, 5:13:09 PM4/19/11
to DDD/CQRS
How would you deal with a request on the read side that has to
communicate with domain objects to answer a question? We are building
a ecommerce application and one of our scenarios is product browse/
product details with discounts. Discounts are part of our core domain
so evaluating a price must communicate with the domain to derive a
price. So the read side would load the product details from the read
data store but needs to set the price field to what the domain says
the price should be at that point in time.

Chris Nicola

unread,
Apr 19, 2011, 5:29:52 PM4/19/11
to ddd...@googlegroups.com
The information you are querying from your domain should exist in some read-model somewhere.  Discounts may be part of your core domain but their information should live in a DiscountModel somewhere on the read side.  You shouldn't need to "query" against your domain model if you are applying CQRS.

Greg Young

unread,
Apr 19, 2011, 5:35:36 PM4/19/11
to ddd...@googlegroups.com
Couldnt you just have the domain generate out the discounts when they
are created/prices change?

--
Les erreurs de grammaire et de syntaxe ont été incluses pour m'assurer
de votre attention

Greg Young

unread,
Apr 19, 2011, 5:39:51 PM4/19/11
to ddd...@googlegroups.com
There is one possible place where a trade off occurs. Let's imagine
there are 400 schemes of discounts each with its own strategy + data.
Then doing a denormalization of each on everything would be extremely
expensive on the read side (eg: this falls into the special category
of a "what-if" question). More often than not though we can avoid
these situations.

--

Dan Malcolm

unread,
Apr 20, 2011, 4:51:19 AM4/20/11
to DDD/CQRS
Life would be simpler if it were "more often than not" but e-commerce
discount mechanisms often include context-dependent rules like "if
they've got any kind of camera and they've got a battery and they've
got the voucher code BATTERY50, then discount the battery by 50%".
This is "calculating" as opposed to "querying" and can't be done by
filtering on a prepared-in-advance read model. There are quite a few
similar scenarios to this like calculating shipping costs based on
weight / volume / product type rules.

One solution would be to isolate discount calculation within a
separate Discounts bounded context containing Discount domain objects
that can be set up with rules and discount logic. This functionality
would then be exposed via a simple API, e.g.
DiscountService.CalculateDiscounts(Basket basket). This would use the
Discount objects to calculate any discounts that could be applied and
return an object containing the results of the calculation.

We're going to have the usual issues with lazy-loading and pre-fetch
paths when we load the Discount objects to do the calculation though,
so we could look at how applying CQRS within the Discounts BC might
help us. One approach:

- The Discount domain objects contain functionality for adding /
editing rules and discounts and for validating state. However they
don't know how to actually calculate discounts - they're more like a
"schema" for the discount
- A separate "calculation-optimised" read model (e.g. C# object graph)
contains logic used to perform the calculation according to the
discount "schemas". This is kept in sync with edits to Discount domain
objects, the entire object-graph is serialized as XML / json and is
stored in a cache.
- The DiscountService retrieves the "calculation-optimised" read model
from a cache and gets on with its business without ever hitting a
database. It's really fast.

The read-model-with-behaviour idea is interesting, as most CQRS
example scenarios involve generating static behaviour-free data in
read-models for display in the view. There seem to be plenty of other
applications!

Dan

AlexY

unread,
Apr 20, 2011, 3:45:36 PM4/20/11
to ddd...@googlegroups.com
What if these discounts are customer based? You cannot pre-render all possible discounts for all customers all the time. This is a problem that I'm going to be facing soon and I'm not sure how to address it yet. In my case I have to alter lots of data per customer, so it will be a problem to tackle.

Greg Young

unread,
Apr 20, 2011, 4:17:27 PM4/20/11
to ddd...@googlegroups.com
This is precisely the kinds of situations I discussed when mentioning
trade offs in my previous email. In these situations its quite common
to bring logic into the read layer (or even have it use the domain
model).

--

Tom Janssens

unread,
Apr 21, 2011, 4:53:34 AM4/21/11
to ddd...@googlegroups.com
I've had a similar problem in the past, but IMHO it is easy to fix; you build a simple rule engine on the viewside, which gets populated by your domain objects; here is how I did it (KISS):

(VM=viewmodel):
class VMArticleFilter
{
   VMArticle Lowerbounds;
   VMArticle Higherbounds;
}

class VMCustomerFilter
{
   VMCustomer Lowerbounds;
   VMCustomer Higherbounds;
}

I then filter the VMArticle table by applying all the non-null fields from the filters as criteria ("and" if from the same filter, "or" for multiple filters) 

You could probably extrapolate this and build a rule class like this
class VMRuleSet
{
  List<VMArticleFilter> ArticleFilters;
  List<VMCustomerFilter> CustomerFilters;
  decimal DiscountPct;
  decimal DiscountAmount;
}

Reply all
Reply to author
Forward
0 new messages