Bulk loading and generic loading of abstract AggregateRoots

I have a use-case where I create/configure multiple aggregate roots of multiple types all inheriting from an abstract type.

i.e.

`
AggregateRoot

^
AbstractField

Hi Wiliam,

that’s actually pretty essy to do. Just implement an AggregateFactory that creates an empty instance of one of the implementations, based on the creation event.
Then, configure the repository to use that aggregate factory.

Cheers,

Allard

I’m sorry Allard I’m not sure if I completely follow your suggestion. If you’re suggesting creating a custom AggregateFactory then injecting that in to an EventSourcingRepository, I still don’t see how this would work because the EventSourcingRepository needs to know the aggregate type to load the aggregates’ events - indeed, the aggregate type forms part of the SQL query to fetch events when using a JPA event store

Just configure the abstract superclass as the type for the repository. The actual aggregate returned by the repository is decided by the AggregateFactory, so that may be any subclass of that type.

Cheers,

Allard

I’m sorry Allard I am still newbing out with this. If the “aggregateType” forms part of the SQL used to load events, how can I ever load by generic type - Axon mandates that I provide a concrete type so that it can form proper SQL.

May I please see some pseudo-code to achieve this?

Many thanks

Hi William,

Axon doesn’t mandate you to provide a concrete type. If you use an abstract type, it will work fine. The Event Store doesn’t store the aggregate’s exact type, it stores the type identifier, which happens to default to the class’ simple name. You can add any value. The reason the type identifier is there, is to avoid conflicts when two aggregates of different types happen to have the same identifier.

Here is the code for an aggregate factory (I recommend always extending AbstractAggregateFactory, so that loading snapshots is done properly):

public class MyAggregateFactory extends AbstractAggregateFactory {

@Override
protected EventSourcedAggregateRoot doCreateAggregate(Object aggregateIdentifier, DomainEventMessage firstEvent) {
if (CreateSomeAggregate.class.isAssignableFrom(firstEvent.getPayloadType())) {
return new SomeAggregate();
} else if (CreateAnotherAggregate.class.isAssignableFrom(firstEvent.getPayloadType())) {
return new AnotherAggregate();
}
}

@Override
public String getTypeIdentifier() {
// typically, you’d return the Class’ simple name here, but it may be anything.
return “SomeIdentifier”;
}

@Override
public Class getAggregateType() {
return EventSourcedAggregateRoot.class;
}
}

Note that this AggregateFactory has a generic type of “EventSourcedAggregateRoot”.Typically, you’d use your own abstract supertype here, such as the “Field” type you mentioned earlier.

Hope this helps shed some light on the matter.
Cheers,

Allard

Worked perfectly. I have uploaded my example here if anyone wants to see: https://github.com/williamboxhall/cqrs-axon-form-builder/blob/master/src/main/java/org/example/write/domain/FormSubmission.java

I did have to give my FieldRepository a concrete type though, axon didn’t like it when I tried to wire it in to the @CommandHandler using the Repository or EventSourcingRepository type. It says it cannot resolve parameter 2.

Hi William,

now I see what you mean with the need for a concrete type. I thought you refered to the type parameter in EventSourcingRepository<…this one…>. In @CommandHandler and @EventHandler annotated methods, you can use Spring beans if there is exactly 1 matching bean. The Repository interface typically matches with a larger number of beans.

Looking at your example, there is a small remark I’d like to make. Strictly speaking, “isValid” is a query. Queries do not belong in a Command Model. So in this case, you’d generally have a validation mechanism that uses a query database to find out if input is valid. With your current approach, performance is likely to be poor, as the default behavior is to pessimistically lock a loaded aggregate. If a forms is submitted at the same time by two users (or two different forms with the same field, if possible), the submissions will have to wait until the other has released the lock. Worst case you can also get a deadlock. Fortunately, deadlocks are detected, but they will cause an exception to be thrown.

Cheers,

Allard

I was wondering about this. I figured that because the command requires a query to perform its logic, then that should live on the write side.

How would you suggest I access the read-side database from the write-side eventsourced aggregate root? I guess the query/view would live on a cluster that lives on the same host as the command? I’m a bit fuzzy on this

Hi William,

a query model can live on the “write side”. If there is information you need to perform a command, but that information is not included in the command itself, you’d want to do a query for it. The query model is optimized to deliver that data as fast as possible. That means it is modelled after the command handler’s data need, but is also placed “near” those objects.

Hope that helps.
Cheers,

Allard

I am having some trouble with this conceptually. I totally understand that this is a query from the point of view that we need to read information about fields as part of the form submission.

But it seems intuitive and correct to me that I model the BUSINESS RULES around validation inside the field domain objects themselves. It would feel wrong if I put that business logic in my views. I’m not really sure how to reconcile this, do you have any wisdom for me on understanding this?

Cheers

Hi William,

I never spoke about the “views” doing the logic. That’s probably where the itch is. The query models don’t only serve the views. They may also serve other components of your application, including the command handlers.

In an ideal world, your aggregates inside the command model would be able to validate incoming commands.
Sometimes, however, you just need more information that you can reasonably capture inside one (group of) object(s). One example that pops up regularly is “set validation”. You sometimes want to make sure only one instance exists with a certain property. To find you if one already exists, the query model is the fastest, since it can easily be optimized to find this information. The command model should be optimized for loading by ID only.
Another example is validation that requires information from more than one (type of) aggregate. In that case, simply validate against a query model before loading the aggregate. Obviously, you don’t want this logic executed in the view. Therefore, you do it inside the CommandHandler method.

So whenever you find yourself in the need of loading more than one aggregate for a single command, always ask yourself (at least) these questions:

  • am I not secretly doing a query?
  • should the 2 aggregates be merged into one?
  • should I split my command into 2 separate commands?

Hope this clarifies things a bit.
Cheers,

Allard

Hi Allard,

However i’ve always heard that use query from read side inside a command handler is a bad practice. Do you suggest to use a query inside command handler rather than load several aggregates (without modification) ?
Do all informations provided by the read side must not be already in the command itself ?

Hi Baptiste,

pragmatism is a practice that’s overrules (almost) all bad practices ;-). If your commands grow over 10k because you’re adding everything potentially necessary to it, it’d consider it a bad practice. Instead, I would use a CommandHandler object that receives a command, does a query to enrich the command’s data and then loads and calls the aggregate with the enriched information. By applying this over of activity, the lock on the aggregate is a short as possible and the commands also stay small. The query model should be optimized to provide this information as fast as possible.
Loading an aggregate without modification is a serious bad practice. So bad that even pragmatism has difficulties overruling it. That would mean you’re using the command model to ask questions (a.k.a queries). The whole idea of CQRS is that you distinguish between commands and queries.

Cheers,

Allard

I understand. So, all public methods in your aggregates make an action and return void ?

Yes, they do. Except for one case, to be honest: login. That method returns a status indicating whether or nog login was succesful. “Normal” return values are cheaper than exceptions. And someone typing in a wrong password is something you can expect, not something eceptional…