Domanda

For example, when you submit a Register form, you have to check in the Domain Model (WriteModel in CQRS) that it is in a valid state (example, email address syntax, age, etc).

Then you create a Command, and send it to a Command Bus.

I understand that Commands should not return anything.

So how do you handle an error beyond Command Bus? (For example, a user registered 1 second before with the same username/email).

How do you know that command failed, and how do you know the error?

È stato utile?

Soluzione

I understand that Commands should not return anything.

That's one view, but it's not entirely set in stone. Consider writes (PUT, POST, DELETE) in HTTP -- all of these messages are commands, in the sense that they are messages with request that the resource change state, and yet they all return responses.

So how do you handle an error beyond Command Bus? (For example, a user registered 1 second before with the same username/email).

How do you know that command failed, and how do you know the error?

So in a case where you are communicating directly with the command handler, a returned message is a perfectly reasonable way to acknowledge that the command has been received and processed.

If you are using a piece of middleware, like a bus, that prevents you from communicating directly with the target, then I would suggest that you look to asynchronous messaging patterns -- how do you get the command handler to send a message back to the caller?

One idea is to subscribe to the outcome of the command; this borrows from some of the ideas in Hohpe's Enterprise Integration Patterns. The basic idea is that, since the client is familiar with the command message that was sent, it is well positioned to subscribe to any new messages published as a consequence of the command message. The command handler, after saving the data to the book of record, publishes events announcing that the change was successful, and the client subscribes to those events -- recognizing the correct events by considering the coincidence of various identifiers in the message (causation id, correlation id, and so on).

Alternative approaches are a bit more direct. One would be to include in the message a callback, which can be invoked by the command handler after the message is successfully handled.

An very similar alternative is to reserve space in the command message for the command handler to write the acknowledgement -- since the client already has the command message in question, the circuit is already complete. Think "promise" or "completable future". The message tells the command handler where to write the acknowledgement; doing so signals to the client (countdown latch) that the acknowledgement is available.

And of course, you have the additional option of removing the middleware that seems to be getting in the way of doing the right thing simply.

For example, a user registered 1 second before with the same username/email

If you are handling user registration idempotently, that wouldn't necessarily be an error -- repeating messages until a response is observed is a common way to ensure at least once delivery.

Altri suggerimenti

For example, when you submit a Register form, you have to check in the Domain Model (WriteModel in CQRS) that it is in a valid state (example, email address syntax, age, etc)

There are many types of validation. The validation when you check for email address syntax and age format is a type of validation that a Command can do. This is not really a Domain concern. It may seem like that because some domain experts would tell you those specifications but you should do this kind of validation in the Command creation. In fact, the general idea is to do the validation as soon as possible because after a command is created and send to a BUS it is more complicated to take actions.

So how do you handle an error beyond Command Bus? (For example, a user registered 1 second before with the same username/email).

This kind of validation is discussed a lot in the CQRS community, from the beginning of CQRS. Where to do it? It is very debated. I personally use the following approach: Before the command is sent to the BUS, I mark the username/email as taken in a centralized manner (i.e. a unique index constraint on username/email). After that I send the command. The drawback is that, if the command fails, for a short period of time that username is taken and not used; this is acceptable for my business, probable for your business to.

How do you know that command failed, and how do you know the error?

If an asynchronous command is rejected by the Aggregate because of an domain invariant then some measures must be taken by issuing some compensatory command that somehow notifies the issuer of the command (i.e. send an explanation email that the command failed).

The problem with duplicate email is that you cannot send an email because that email address belongs to another person, that is why I check it before sending the command to the bus.

Validation should be performed in a decorator. Then any command that needs validation could be decorated as such.

Validations can be handled with exceptions if your returning void with your command so they could be picked up with either sync or async calls with the returned task result.

Another possibility is to think of validation as a type of "query" which would return back a validation result. Run the validation query, and then if passed, run the command. This would be an alternative to the decorator approach.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
scroll top