Loading more than one AR in a CommandHandler tested by a fixture

Hi,

I have a problem when trying to write a fixture for a command handler
dealing with (well, "loading" actually) more than one AR.

Let's say I have a StartSellingProductCommand, consisting of a Shop
id, a Product id, and a price.

The CommandHandler will load the two AR from their repositories, and
then invoke something like shop.startSelling(product, price).
So, Shop will collaborate with Product to check that it's actually ok
to sell it (just a simple business rule). Nothing guarantees me that
the Product instance being used is really the latest version
available, since it's another AR and therefore outside of the Shop's
consistency boundaries. But let's assume it's okay for me.
Btw, we see that the only AR really being manipulated (and ultimately
raising events, here the StartedSellingProductEvent) is the Shop.

Ok, I want to make a fixture to test that.

So I'm doing something like :

  @Test
  public void testStartSellingProduct () {
    StartSellingProductCommand cmd = new StartSellingProductCommand();
    cmd.shopId = fixture.getAggregateIdentifier().asString();
    cmd.productId = "PRODUCT-1";
    cmd.price = 500;

    fixture.given(new ShopRegisterdEvent("Paris shop"),
          new ProductLaunchedEvent("My great product"))

        .when(cmd)

        .expectEvents(new StartedSellingProductEvent("PRODUCT-1", 500));
  }

Thing is... when instanciating those events, I never need to (can ?)
give the ID of the AR which have generated them.
Here, I would need to say "OK, the ProductLaunchedEvent was generated
by the AR with the Id 'PRODUCT-1'", so that this AR could be actually
loaded from the repo.

When it comes to the "main" AR being manipulated by the handler tested
by the fixture (Shop), it's okay : it's the fixture that chooses the
AR Id, and then I can retrieve it using
fixture.getAggregateIdentifier().
But what about the other ARs which need to be loaded from their
repositories by the Command Handler ?
Who should choose their IDs ? And put them into the events ?

Or all this is just an invalid use case of a fixture ?

Not sure I'm being clear here. But any help is welcome :smiley:

Thanx,
Eric

Hi Eric,

in short: the provided test fixtures don’t work when you’re loading more than one aggregate. But there is a reason for it: it’s a bad practice.

And here is the longer answer.
Let’s have a look at what you’re doing: your loading an aggregate (from the command model) to check its state. That’s not what the command model is for. The command model should never expose its state other than by its behavior when processing commands. If you need state, do a query. You can design your query model specifically for the information needs of your components.

In the ideal case, your Shop aggregate should just “trust” the product being ready for selling. If it’s not ready for selling, don’t send the command.
If you really want to check its state in the command handler, you could do a query for product information in the query store and validate the command there. If valid, load and invoke your aggregate to “start selling”.

Your example triggers some other questions. If the information in product is relevant for the decision to sell or not, why not target the command at the product? Product.sellIn(ShopId).

To get back to the testing problem: if you do a query in your command handler, you can inject a mock or stub query component in your command hander.

Hope this helps.

Cheers,

Allard

Hi Allard,

Thanks for your reply.
I gotta admit I'm not too surprised by the content of your answer :wink:

* OK for the short answer. At least I know that my use case is just
not supported by the provided test fixtures, so I don't need to spend
more energy on trying to make that work.

* I get what you're saying about this use case being a bad practice
and therefore being unsupported by Axon.

But what i'm not sure about is : why is this a bad practice ? You're
saying that aggregates should not exposte *state*. I'm perfectly fine
with that, this is a common DDD principle (heck, this is a common OO
principle !). But is any non-void method considered as a method that
returns state ? Maybe this is just a semantics issue, but I'd tend to
think that an object can have state, not expose it, and still
communicate with other objects (answering method calls) by returning
values derived from their states. Well, basically, a "query" method.

So, are you saying that this is a bad practice to have a query method
in an aggregate ? I guess so, and I somhow understand... After all we
have a freakin Query/Read side for that ! But going through the Query
side for this seems a little bit overkill for me. Plus, since I'm
trying a DDD approach here, I'd like my business rules (that can live
inside entities, services or specifications) to operate on domain
objects, not on some vague string or double retrieved from the "other
side"...

Ok, you suggest I do things the other way around :
product.sellIn(ShopId). Yeah, that works if product knows everything
to decide if it's ok to being sold in the shop or not. But what if the
rule is based upon both product characteristics and shop
characteristics ?

I'm all for the "commands and aggregates should do stuff" thing, and
real objects (= anything other than a DTO) should not exposte state...
but sometimes, especially for business rules, it seems to be that some
objects should be able to answer some questions they're asked (as long
as the answer is not used after that to change their state, which is
an OO bad smell). But maybe full-blown CQRS is really stricter than
that, even if I'm not really seing the benefit here.

WDYT ?

Anyway, thanks again.

Eric

Hi Eric,

CQRS itself doesn’t stop you from doing many things. Nor does DDD. However, if you want full benefits of CQRS, there are some very simple rules and practices.

Loading 2 aggregates for the execution of a single command is one of the bad practices. The reason is quite straight forward, and can be found in the DDD definition of an Aggregate.

An aggregate defines a consistency boundary in your model. That means everything within the boundary is guaranteed to be consitent. Outside of the boundary, that guarantee is lost.
This is what Eric Evans literally says:
“Use the same aggregate boundaries to govern transactions and distribution. Within an aggregate boundary, apply consistency rules synchronously. Accross boundaries, handle updates asynchronously. Keep an aggregate together on one server. Allow different aggregates to be distributed among nodes.”
There is a good reason to do so: by letting go the consistency between entities in other aggregates, we make a clear separation between the aggregates. This separation helps us in terms of scalability and extensibilty. If you load 2 aggregates for a single command, you impose the absolute requirement that these aggregates are available on the same machine. If each shop can be combined with each product, this ultimately means that you cannot easily scale beyound a single machine.

So far, it’s purely a DDD issue. When CQRS is added to the mix, it provides a solution by providing a near-consistent query model for the aggregates to use.

The fact that the method you’re invoking on the Product has a void return value doesn’t mean it’s not a query method. If the method throws an exception, that’s also practically a return value. Also not that although I always use the term “the query model”, there is no such things. It’s actually “the query models” (plural). The shop bounded context could keep a product query model in which it keeps the information relevant for validation. That model is kept up-to-date by listening to events from the product context.

In the end, it’s up to you to decide whether you put product and shop in the same bounded context or in two different ones. But make sure your decision doesn’t bite you back when scalability/extensibility becomes an issue.

Isn’t modelling great fun! :wink:

Cheers,

Allard

Allard,

Ok, so the thing is that having aggregates to work together, synchronously, obviously put limitations to the scalability and extensibility of our architecture. Yep, makes perfect sense.

I had already read a lot - from Evans, on the blogosphere, etc. - about the 1 command / 1 AR thing… but getting a little reminder about all this certainly didn’t hurt. So, thanks a lot for your answer !

So far I mostly approached DDD and CQRS by going through their functional benefits, I guess that’s why I’ve not considered too much some of the non-functional gains (and the associated design constraints) they can come with. But this is getting clearer. And yeah, modeling is definitely fun :slight_smile:

Thanks again.

Eric

2011/8/22 Allard Buijze <buijze@gmail.com>