Batch of Commands

1,046 views
Skip to first unread message

MichaelK

unread,
Feb 6, 2012, 5:34:05 PM2/6/12
to DDD/CQRS
I am new to CQRS + ES and I very much like what I see but I struggle
with how one handles batch updates. Here is an example, and maybe a
bad one at that...

Lets say we have products and thus Product is our AR. All of this is
manged on website. I have a command to set price. All great so far.
Updating one products price is fairly simple since its done via the
web application.

Now our manager asks us to update all product prices by 2% for the new
year. How would one do that? Typcially in the SQL world we could apply
a script that updates all products that are stored in the table. In
CQRS now I need to send a command for each product SetPrice(productId,
newPrice)

One thought it to write utility tools that are used internally by the
team to manage these tasks, but my question is the pushback. If its in
a SQL table its so easy to update. (Comments people have already made)
Now for each task we almost need to write a "utility" to handle the
command. Just curious how others are dealing with this. I like the
control and trackability that CQRS / ES gives but speed of the raw SQL
approach is useful as well. Looking to defend my case of the
usefulness of CQRS / ES

ShuriK

unread,
Feb 6, 2012, 6:46:34 PM2/6/12
to DDD/CQRS
I think, you really need to call the MakeDiscount method for every
product, without it you can't then rebuild your read model and lose
the ES power.
It also can have sense from the audit and security point of view.

As for me, more intresting other questions here:
1. How can I get all products list in the domain model?
2. How can I do it in one transaction?

Caleb Vear

unread,
Feb 6, 2012, 7:00:58 PM2/6/12
to ddd...@googlegroups.com
1. You have a read model which lists all products or their ids
2. Why would you need it to all take place in one transaction?

MichaelK

unread,
Feb 6, 2012, 7:37:12 PM2/6/12
to DDD/CQRS
I have accepted that I will need to call MakeDiscount for each
product but how are people handling this? I am assuming the UI does
not have this bulk feature since it could be a one time thing or
something that is very rare. I always can throw together a quick
utility tool but that still takes time vs "UPDATE Products set...".
Just wondering how others address this "gap".

Caleb Vear

unread,
Feb 6, 2012, 7:45:55 PM2/6/12
to ddd...@googlegroups.com
I'm not sure that writing the utility program will really take much longer than the SQL statement.  I completely naive solution could look like this:

foreach(var product in ViewModelRepository.Get<ProductListViewModel>())
{
   bus.send(new IncreasePriceCommand(0.02));

Dylan Smith

unread,
Feb 7, 2012, 12:43:45 AM2/7/12
to ddd...@googlegroups.com
I would still just send one command from the UI that represents the batch update. Then either put the logic to loop through all products directly in the Command Handler, or have the Command Handler fire off a Saga that coordinates updating all Product AR's.

ashic

unread,
Feb 7, 2012, 3:00:32 AM2/7/12
to DDD/CQRS
A discount is an important business concept. It can be for various
reasons and a discount being applied doesn't changed the original
retail price (it applies a discount on top of a retail price).
Discounts can be of various types and may even be cumulative or
mutually exclusive. As such, depending on complexity, it may be worth
adding a new Aggregate (and if complex enough, have a Saga that
receives various discount events and fires command to various
Aggregates).

For your scenario, it seems you can simply create a
DiscountAppliedEvent directly from the command handler and have a
projection update the readmodel. You could also (specially if your
infrastructure requires it) create a new Discount Aggregate instance
(or have a singleton DiscountManager Aggregate) that simply creates
the event. The projection class would still simply do an update. The
point is that your product classes don't seem to need to know about
discounts. So don't make it a concern of the Product class. UI wise,
it's not a batch operation anymore :)

Does that help?

ShuriK

unread,
Feb 7, 2012, 3:29:39 AM2/7/12
to DDD/CQRS
During reading this post's I remembered about 3 or 5 episodes in my
practise when we used direct update with the simple SQL and that SQL
was incorrect... We had no events log.. So we spent a lot of time to
fix the effects...

Tom Janssens

unread,
Feb 7, 2012, 4:31:59 AM2/7/12
to ddd...@googlegroups.com
Usually when I see set-based operations, I assume the set's member identities should either be grouped in saga state, or in a separate AR.

Maybe you need the concept of a catalog (which would propagate the price changes to all the individual products) ?


Tom Janssens

unread,
Feb 7, 2012, 4:57:14 AM2/7/12
to ddd...@googlegroups.com
On a side note, in this case it might not be quite obvious, but what if your manager asks you to update different subsets of products multiple times ?

It is true that a single SQL statement is written/executed faster. However, upon updating you lose information (i.e. history).

Let us assume we do not use CQRS/event sourcing, and we do regular price updates of subsets of products, and there is a drop in sales for certain subsets.

Using CQRS the manager could figure out exactly where the price changes might have influenced the sales (by correlating the dates etc), and in which cases the sales dropped without a price change. That might provide your manager a competitive advantage and is exactly the power of CQRS+ES.

Using a CQRS/event sourcing approach, you can change/extend your understanding of the domain while still preserving historical behavior (i.e. events/data). You can apply your new insights to behavior from the past. This is hard/next to impossible when not using event sourcing IMO.

CQRS/DDD is not easy, because it actually requires you to think about the implications and consequences of your behavior (i.e. "update some table" is about content, while "index all prices by 2% for subcategory x" is more about the intent of your action). I can guarantee you that it will be harder in the beginning, as it requires you to actually think about transitions and be explicit, and you will notice you will discover a lot of edge cases (which you might have not discovered yourselves in a non CQRS-approach).

Michael Brown

unread,
Feb 7, 2012, 8:31:58 AM2/7/12
to ddd...@googlegroups.com
I've learned that if they ask for it once, they'll ask for it again. Do it right the first time and you get one click performance the next.

-----Original Message-----
From: ddd...@googlegroups.com [mailto:ddd...@googlegroups.com] On Behalf Of MichaelK
Sent: Monday, February 06, 2012 7:37 PM
To: DDD/CQRS
Subject: [DDD/CQRS] Re: Batch of Commands

Michael Brown

unread,
Feb 7, 2012, 10:30:32 AM2/7/12
to ddd...@googlegroups.com
Actually, when I modeled a domain for a POS system, I extracted Price into its own entity and Discount derived from it. Promotions were discounts that ran for a certain period. Along with the logic that applies the appropriate discounts to a Sale, that should be all you need to handle a global 2% discount.

-----Original Message-----
From: ddd...@googlegroups.com [mailto:ddd...@googlegroups.com] On Behalf Of MichaelK
Sent: Monday, February 06, 2012 7:37 PM
To: DDD/CQRS
Subject: [DDD/CQRS] Re: Batch of Commands

ashic

unread,
Feb 8, 2012, 10:06:28 AM2/8/12
to DDD/CQRS
I would think price and discounts would be orthogonal concepts. The
use of inheritance here would seem a bit awkward. While it could
definitely work, I don't think it's needed for a simple 2% global
discount.

Michael Brown

unread,
Feb 9, 2012, 9:59:23 AM2/9/12
to ddd...@googlegroups.com
I can't quite remember now why I designed it that way. It was a couple of years ago. I think it had something to do with being able to reuse my price matching logic (i.e. certain prices would run for a given amount of time under the promotion system, then expire). Leveraging that and adding additional logic for when a discount applied, I was able to calculate the price and discount for any line item with one call. I've got to pull that code back up to remember the details though.

ashic

unread,
Feb 9, 2012, 4:13:43 PM2/9/12
to DDD/CQRS
Sounds like a presentation concern?
Reply all
Reply to author
Forward
0 new messages