@Aggregate: business validation on the handler or listener?

Hello everybody,

PS: Sorry about my english. :frowning:

Im developing my first Axon application and I have two questions about design.

Lets suppose I have a domain model like this:

class ServiceType(val id: ServiceTypeId, val name String) - simple POJO

After publishing my ServiceTypeCreatedEvent views models will persist that event: 1) postgres, 2) Elasticsearch

  1. Talking about a POST API for that domain, most REST apps will call a @Service that will persist with a @Repository and return that @Entity in JSON Representation with the new ID generated by JPA sequence. Since everything is synchronous it works. I understand that CQRS/Axon its not synchronous. After publishing the event, I should forget about it, right? But I need to return at least the ID so my client can use a GET API later to retrieve that record.

So, to address that problem I decide to return at least one ID created on the Command and passed to the event:

public ServiceTypeId() {
    this.id = IdentifierFactory.getInstance().generateIdentifier();
}

Does this seem appropriate in this scenario?

  1. look this:

@Aggregate
public class ServiceType {

    @AggregateIdentifier
    private ServiceTypeId id;
    private String name;

    @CommandHandler
    public ServiceType(CreateServiceTypeCommand cmd) {
        // business validation here or not?

        apply(ServiceTypeCreatedEvent.of(cmd.getId(), cmd.getName()));
    }

    @EventSourcingHandler
    private void on(ServiceTypeCreatedEvent event) {
        this.id = event.getId();
        this.name = event.getName();
    }

}

I watched some videos that I should “validate” my command before use the method apply. Those videos just validate if the attributes are null or something like that. But lets suppose I have a business rule that does not allow duplicated names.

Should I do this type of validation here on the @Aggregate before applying the event? for that, my @Aggregate will have to know the ServiceTypeJPARepository. Im not sure if Im breaking some cqrs/axon design here.

If I do that, I know I will not be storing a event that will be for sure rejected by all my Listeners. That looks good to me because when I replay the events I dont have a ‘invalid’ event to process. Still looks weird to me have a specific view model implementation (in this case jpa/postgres) here in this aggregate. I guess I could call a regular @Service but it’s still the same, right?

so store/apply this ‘wrong’ event and keep my @Aggregate clean letting the Listeners deal with this validation or put that business validation on the Listeners side?

Hi Mike,
there are many options for this type of validation. You can do that in HandlerInterceptor (interceptor called after command dispatch), or you can do that on external commandHandler (means that you have separate component in which you handle your command, invoke validation and then when validation is ok load the aggregate from repository and invoke the method on aggregate ) or finally you can inject some “validator” to your Aggregate and do the validation in it. The latest will hold the lock in repository, so other commands won’t access your aggregate.

Hi Mike/LukĂĄĹĄ,

  1. What I’ve done in previous projects was to not have the creation of the aggregateIdentifier in the aggregate itself but in the service layer which is publishing the command. The ID would thus already be a parameter on the command, and thus would already be in the service where you publish the ‘CreateAggregateCommand’.
    What however is (I’d say) nicer, is to use the result of your @CommandHandler annotated function. The result of command handling functions can be retrieved from the CompletableFuture the CommandGateway returns to you upon sending a command. For aggregate constructors the return value isn’t the aggregate itself, but the @AggregateIdentifier annotated field within that Aggregate.

  2. Like Lukáš already points out, there are several ways of doing validation. Depending on what kind of validation you’re doing I’d have different suggestions.
    For the repository look up on the name you suggest I’d probably solve it with a HandlerInterceptor or either have a check in the service which sends to command to see if it’s okay.
    If you’d do validations on a different level, like if you’re allowed to withdraw 100 euro/dollars from a bank account (aggregate), I would just write that asserting in the command handling function, prior to applying the event.
    The third option Lukáš gives, to inject a “validator” is also reasonable, but however be careful with what you inject.
    Like Lukáš already describes you’re locking the aggregate when handling a command, so it would be unwise to have your “validator” do database look ups.
    I’d say an injected validator on your command handling function should be considered a state less domain service, or a finite state machine.

Hope this helps.

Cheers, Steven

Hi LukĂĄĹĄ and Steven

I really appreciate the answers. Thank very much and sorry about my english again. I’m still learning.

Before everything, you are saying that we agree that I should not apply/store this Event at all because I know it has a Business error (I can’t have duplicate names in my domain logic) and all my Listeners will reject this event later.
So looks like it’s a good practice only store valid commands regardless if is just a simple null check or a more complex domain business validation. Right? keep my Store Domain table very clean and have more performance on replays.

  1. What I’ve done in previous projects was to not have the creation of the aggregateIdentifier in the aggregate itself but in the service layer which is publishing the command.

So let me get this straight, are you adding a Service layer between your Controller and the @Aggregate itself, right? If is that the case I could use that same @Service to create my ID and to Validate my ‘unique’ name.

  1. Controller receives the POST
  2. Send the post payload to a Service
  3. This services knows 1 or N repositories to validate this payload and to generated the UNIQUE ID.
  4. If is valid, build the command and sendTo CommandGateway.

Now I dont have to worry about any validation on my Aggregate. My @CommandHandler will just ‘AggregateLifecyle.apply’ the event.

That seams reasonable to me. But thinking about DDD (lets be clear Im not a expert in DDD at all), my Aggregate will not become too anemic? Looks like to me if I do that, Im breaking some DDD rule and coming back to Service Driven Design. It’s no the case?

If my Aggregate calls a services (that knows the repositories) injected by spring, its not more consist with DDD?

Something like this:

@NoArgsConstructor
@Aggregate
public class ServiceType {

    @AggregateIdentifier
    private ServiceTypeId id;
    private String name;

    @Autowired
    ServiceTypeValidator validator;

    @CommandHandler
    public ServiceType(CreateServiceTypeCommand cmd) {
        validator.valid(cmd); // throwns BusinessException()
        apply(ServiceTypeCreatedEvent.of(cmd.getId(), cmd.getName()));
    }

For the repository look up on the name you suggest I’d probably solve it with a HandlerInterceptor or either have a check in the service which sends to command to see if it’s okay.

I’m going to study that right now. Still, I think I will have the same question: where to inject the Service or the Repository directly to validate my business rule?

Again, really really thank you.

Hi Mike,

First, when you’d like to inject/autowires services which are serviced through Spring, you can just add them as parameters to your message handling function like so:

@NoArgsConstructor
@Aggregate
public class ServiceType {

    @AggregateIdentifier
    private ServiceTypeId id;
    private String name;

    @CommandHandler
    public ServiceType(CreateServiceTypeCommand cmd,  ServiceTypeValidator validator) {
        validator.valid(cmd); // throwns BusinessException()
        apply(ServiceTypeCreatedEvent.of(cmd.getId(), cmd.getName()));
    }

Axon has internals which are called ParameterResolvers, for which a SpringBeanParameterResolver is in place. The ParameterResolver will resolve the bean you need right into your function.

Second, what I tried to point out is that it depends what’s the beter solution.
If the validator you’re describing has to perform repository calls, I don’t think it should be blocking your aggregate. So validators of that form could for example be placed in a Service between your controller and the aggregate.
If the validator does not do any outside calls but only based on the Aggregate it’s state, then you should definitely include it in your command handling functions.

Hoping this helps!

Cheers,

Steven

First, when you’d like to inject/autowires services which are serviced through Spring, you can just add them as parameters to your message handling function like so:

    @CommandHandler
    public ServiceType(CreateServiceTypeCommand cmd,  ServiceTypeValidator validator) {
        validator.valid(cmd); // throwns BusinessException()
        apply(ServiceTypeCreatedEvent.of(cmd.getId(), cmd.getName()));
    }

Axon has internals which are called ParameterResolvers, for which a SpringBeanParameterResolver is in place. The ParameterResolver will resolve the bean you need right into your function.

Second, what I tried to point out is that it depends what’s the beter solution.
If the validator you’re describing has to perform repository calls, I don’t think it should be blocking your aggregate. So validators of that form could for example be placed in a Service between your controller and the aggregate.
If the validator does not do any outside calls but only based on the Aggregate it’s state, then you should definitely include it in your command handling functions.

Hi Steven, ty so much. Really.

It’s so good have another opinion.

best tip eve: inject thrown command parameter

Ty ty ty

Hi Mike,

No problem! Glad to have been of help :slight_smile:

Cheers,
Steven