Axon and external customization

I’ve been reading the excellent reference manual on command handler topic.
It seems to me that there can only be one command handler for every command type.

If domain event = applying state changes.
IIUC, the event handler must not throw any error and instead just apply state changes.

How are we to support custom logic hook to perform additional business logic?
For example, if we have Aggregate Root A that reference Aggregate Root B.
And the condition for Aggregate Root B is only remove-able iff no other aggregate
referenced the removed instance.

A possible work around is to create a ValidationEvent -|> ApplicationEvent,
but this seems problematic. Wdyt?

Regards,
Edward Yakop

Edward,

I am not sure I understand the problem correctly. Are you talking about a scenario where you need to update business logic in one of your aggregates?

Cheers,

Allard

No.

Basically, Due to business requirement,
I found the best way to handle unknown external customization is to use this pattern.

Regards,
Edward Yakop

When it comes to state changes, the aggregate defines your consistency boundary. An aggregate by itself is always in a consistent state. Two aggregates are eventually consistent. You use events to notify the system (i.e. all components) that some state has changed.

A specific type of listener might be interested in some events to notify another aggregate (via a command). In that case, the event listener will generate and send a command. Such a listener may be quite simple (catch event and dispatch command), or may become more complex (based on specific occurrences of events send specific events). If you detect a conflict, you should take corrective action.

Basically, you could say that transactions within an aggregate are ACID. Outside of the aggregate boundary, transactions are BASE.

In the near future, I will start the implementation of Saga support (still scheduled for 0.7). A saga is basically a component that manages the transactions between two or more aggregates. They go beyond the simple “catch-and-dispatch” and will allow you to manage complex processes.

Does this answer your question?

Allard

It does answer my question, except for the part that with the current mechanism it will be too late.

There are several use cases that’s been presented:

  • If we have a simple rule that every aggregate root can be deleted as long as no other aggregate referenced it.
    The main reasoning is if a record is mistakenly added by user, the user will be able to rectify the problem by removing it.
    Summary: Need a mechanism for unknown external party to participate in validation before domain event is dispatched.
  • Command handler can’t 100% handle the command
    Imagine if the application requires to ImportJobsFromFile, due to application legacy, there are several file format, and
    the importer needs to be pluggable (E.g. by dropping additional jars into classpath).
    How would this tackled by the current mechanism?
    Note: ImportJobsFromFile#getStream():Stream;

In both cases, I could always use “Application Event”, but
Event is supposed to be immutable and it’s supposed to be describing something that already happened.

Regards,
Edward Yakop

Edward,

about your first point: it really comes down to requirements analysis and domain modeling. Why do you want to prevent deletion of a record? Is it because users make mistakes? Is it because you might throw away aggregates that you might need? You should try to find out the underlying reason for preventing someone to delete an aggregate. And if this relationship does seem to be so important, you might be putting an aggregate boundary in the wrong place.

If you really want to prevent deletion of an aggregate that has references, you could either require the client to check for references (using the query database), or make sure that the aggregate (that is not to be deleted) keeps track of the incoming references. This would require a saga-like implementation (i.e. an event listener that notifies the aggregate of 'incoming references). Your domain expert’s language should give away some hints about this.

In the end, it’s about applying DDD properly and choosing consistency boundaries wisely.

About your second point: I don’t see how this is any different than when you would not be using CQRS. You would just have a component that reads from the external file (using the pluggable system you’re referring to) and sends commands into your system.

Cheers,

Allard

Allard,

Reply inline.

Edward,

about your first point: it really comes down to requirements analysis and domain modeling. Why do you want to prevent deletion of a record? Is it because users make mistakes? Is it because you might throw away aggregates that you might need? You should try to find out the underlying reason for preventing someone to delete an aggregate. And if this relationship does seem to be so important, you might be putting an aggregate boundary in the wrong place.

If you really want to prevent deletion of an aggregate that has references, you could either require the client to check for references (using the query database), or make sure that the aggregate (that is not to be deleted) keeps track of the incoming references. This would require a saga-like implementation (i.e. an event listener that notifies the aggregate of 'incoming references). Your domain expert’s language should give away some hints about this.

If we have a product where it can be sold by modules. We would need a system where business logic can be pluggable.
Let say for example, we’re trying to model a laboratory, where job might comes in into the lab.
If the product customer is a commercial laboratory, they will want to buy an invoice module.
I think it’s fair to say that job and invoice are two separate bounded context.

Without the invoice module, when job is cancelled, we would want to remove the job from the system.
But when there’s an invoice module, the system shouldn’t allow job to be removed, if there’s an associated financial informations (e.g. unpaid invoice).

I guess, I’ll just have to use application event for event collaboration.

In the end, it’s about applying DDD properly and choosing consistency boundaries wisely.

I agree. I think it would be interesting to hear how would the other solve this issue.
Unfortunately, the application becomes more complex due to sales decision.

About your second point: I don’t see how this is any different than when you would not be using CQRS. You would just have a component that reads from the external file (using the pluggable system you’re referring to) and sends commands into your system.

I agree, although if I can make the same philosophy applied everywhere in the system. E.g. using command and events, it would make the system feels consistent.

Regards,
Edward Yakop