Let's consider the flow below:
API client calls [POST] /api/v1/invitation/:InvitationId/confirm
Confirm the invitation within a SAGA
Eventually raise an InvitationConfirmed event to indicate success
We are some troubles finding a good place to validate the "event" we pass to the SAGA. For instance, we want to make sure that:
- The specified InvitationId exists
- The corresponding invitation is not expired or already processed
We tried a couple of things:
Fire a command:
Fire a command RequestInvitationConfirmation
Handle synchronously this command and return an error if the command is not valid OR otherwise raise the InvitationConfirmationRequested event.
The rest of the flow is the same
CONS:
- Requires us to follow a "request/response" pattern (synchronous within the HTTP request lifetime)
Raise an event:
Raise an event InvitationConfirmationRequested
Within the SAGA, query the Invitation service and perform the validations. If the command is not valid, we publish an event InvitationConfirmationFailed
(...)
CONS:
- As far as I understand SAGA should be used to orchestrate the flow. Here we are introducing the concept of "validation". I'm not sure it's the recommended approach.
Validation is a very common concept. How would you handle it in a distributed fully asynchronous system?
Important point in the design of this system is: "Who is the client of this API?".
If this client is an internal Service or Application that's one thing (as in a distributed app, microservices etc.).
If the API is used by third party client's, that's another thing.
Short answer
If the API is used internally between Services, sending a command with invalid Id in the system is a fault, so it should be logged and examined by the system developers. Also cases like these should be accounted for by having a manual way of fixing them (by some administrative backend). Log these kinds of stuff and notify developers.
If the API is used from third party apps, then it matters how responsibilities are separated between the API and the other part of the system that it uses. Make the API responsible for validation and don't send commands with invalid id's. Treat command with invalid ID's like fault, as in the first case. In this case if you use asynchronous flow, you will need a way to communicate with the third party app to notify it. You can use something like WebHooks.
For the second part of the validations check these series of blog posts and the original paper.
Long answer
If you search around you will see a lot of discussions on errors and validations, so here's my take on that.
Since we do separation of other parts of our systems, it's seems natural to separate the types of error that we have. You can check this paper on that topic.
Let's define some error types.
Domain Errors
Application Errors
Technical Errors (database connections lost etc.)
Because we have different types of errors, the validation should be performed from different parts of our systems.
Also the communication of these errors can be accomplished by different mechanisms depending on:
the requester of the operation and the receiver
the communication channel used
the communication type: synchronous or asynchronous
Now the validations that you have are:
Validate that an Invitation with the specified Id exists
Validate that the Invitation has not expired
Validate that the Invitation is not already processed (accepted, rejected etc.)
How this is handled will depend on how we separate the responsibilities in our application. Let's use the DesignByContract principle and define clear rules what each layer (Domain, Application etc.) should expect from the other ones.
Let's define a rule that a Command containing an InvitationId that doesn't correspond to an existing Invitation should not be created and dispatched.
NOTE the terminology used here can vary vastly depending of what type of architecture is used on the project (Layered Architecture, Hexagonal etc.)
This forces the CommandCreator to validate that an Invitation exists with the specified Id before dispatching the command.
In the case with the API, the RouteHandler (App controller etc.) that will accept the request will have to:
perform this validation himself
delegate to someone else to do the validation
Let's further define that this is part of our ApplicationLayer (or module, components etc. doesn't matter how it's called, so I'll use Layer) and make this an ApplicationError. From here we can do it in many different ways.
One way is to have a DispatchConfirmInvitationCommandApplicationService that will ask the DomainLayer if an Invitation with the requested Id exists and raise an error (throw exception for example) if it doesn't. This error will be handled by the RouteHandler and will be send back to the requester.
You can use both a sync and async communication. If it's async you will need to create a mechanism for that. You can refer to EnterpriseIntegrationPatterns for more information on this.
The main point here is: It's not part of the Domain
From here on, everyone else in our system should consider that the invitation with the specified Id in the ConfirmInvitationCommand exists. If it doesn't, it's treated like a fault in the system and should be checked by developers and/or administrators. There should be a manual way (an administrative backend) to cancel such invalid commands, so this must be taken into account when developing the system, bu treated like a fault in the system.
The other two validations are part of the Domain.
So let's say you have a
Invitation aggregate
InvitationConfirmationSaga
Let's make them these aggregates communicate with messages. Let's define these types of messages:
RequestConfirmInvitation
InvitationExpired
InvitationAlreadyProcessed
Here's the basic flow:
ConfirmInvitationCommand starts a InvitationConfirmationSaga
InvitationConfirmationSaga send RequestConfirmInvitation message to Invitation
And then:
If the Invitation is expired it sends InvitationExpired message to InvitationConfirmationSaga
If the Invitation is processed it sends InvitationAlreadyProcessed message to InvitationConfirmationSaga
If the Invitation is not expired it, it's accepted and it sends InvitationAccepted message to InvitationConfirmationSaga
Then:
InvitationConfirmationSaga will receive these messages and raise events accordingly.
This way you keep the domain logic in the Domain, in this case the Invitation Aggregate.
You have a command ConfirmInvitation containing InvitationId. You send it to your Invitation domain from InvaitationAppService. Your Invitation domain should look like this
...
public void ConfirmInvitation()
{
if (this.Status == InvitationStatus.Confirmed)
throw new InvalidInvitationException("Requested invitation has already been confirmed");
//check more business logic here
this.Status = InvitationStatus.Confirmed;
Publish(new InviationConfirmedEvent(...));
}
...
Your InvitationAppService should have something like below:
...
public void ConfirmInvitation(Guid invitationId)
{
// rehydrate your domain from eventstore
var invitation = repo.GetById<Invitation>(invitationId);
if (invitation == null)
throw new InvalidInvitationException("Invalid Invitation requested");
invitation.ConfirmInvitation(new ConfirmInvitation(...));
}
You don't need to introduce a new event InvitationConfirmationRequested. DDD is an approach in which your domain/business validation should reside inside domains. Don't try to fit other patterns or technologies in your domain. Validating your domain inside saga(which is used to orchestrate distribute transactions across the services) might create complexities and chaos
Related
I'm experimenting with microservices, event sourcing and CQRS. However, I'm a little bit confused about how I go from issuing a command to performing a query to return the new state, specifically with regard to interactions with a web API gateway.
As an example, the simple application I am attempting to write (which probably doesn't actually need any of these; it is just something to aid my learning) creates a random-graph and then performs some long-running calculations on the graph. I've modelled this as two separate services: the GraphService and the ComputationService. The imagined process flow is as follows:
User requests new random graph.
API gateway constructs CreateGraph command and sends it to the
graph service.
GraphService command handler creates a graph and publishes a
GraphCreated event.
GraphService event handler subscribes to topic for graph events,
processes GraphCreated event and stores graph in persistent read
storage.
Client somehow gets the newly created graph.
ComputationService event handler subscribes to topic for graph
events, processes GraphCreated event and begins potentially
long-running computation, e.g. calculate diameter.
ComputationService publishes DiameterComputed event.
GraphService event handler subscribes to topic for computation
events, processed DiameterComputed event and updates the graph in
persistent read storage.
Client somehow gets updated - easier than getting the new graph, since already have an ID and can poll for changes / websockets / SSE, etc.
That seems relatively simple. However, my confusion lies in how to go about informing the API gateway, and thus the web client, of the new graph (as highlighted in bold above). In a typical CRUD process, the result of the POST request to create a new graph would be to return the URL of the new resource, for instance. However, with CQRS, commands should return nothing or an exception.
How do I pass information back to the client of the service (in this case the API gateway) about the ID of the new graph so that it can perform a query to get the representation of the new resource and send it to the user? Or at least get an ID so that the web client can ask the API gateway, etc?
As I see it at the moment, after sending a command, everyone is just left hanging. There needs to be some sort of subscription model that can be interrogated for the status of the graph creation. I considered having the API gateway generate a request ID which gets embedded with the CreateGraph command, but this then couples the service to the API.
I'm obviously missing something, but have no idea what. None of the examples I've looked at or discussions I've read address this issue and assume that the ID of whatever resource is already known. I couldn't find any discussions here addressing this issue, but if I've just missed them, please point me there rather than duplicating questions. Any pointers would be hugely welcomed.
How do I pass information back to the client of the service (in this case the API gateway) about the ID of the new graph so that it can perform a query to get the representation of the new resource and send it to the user? Or at least get an ID so that the web client can ask the API gateway, etc?
By listening for the echo.
The basic idea behind at least once delivery is that I'm going to send you a message, and keep sending it over and over until I receive a message that proves you've received at least one copy of my message.
Therefore, my protocol looks something like
Establish a mail box where I can collect messages
Encode into the message instructions for delivering to my mailbox
Send the message to you
Check my mailbox
if the answer is there, I'm done
otherwise, I send you another copy of the message
The mail box could be implemented any number of ways -- it could be a callback; it could be a promise, it could be a correlation identifier. You could have the signal dispatched by the command handler, when it gets acknowledgement of the write by the book of record, or by the "read model" when the new resource is available.
Scenario:
I have 2 Microservices (which both use CQRS + Event Sourcing internally)
Microservice 1 manages Contacts (= Aggregate Root)
Microservice 2 manages Invoices (= Aggregate Root)
The recipient of an invoice must be a valid contact.
CreateInvoiceCommand:
{
"content": "my invoice content",
"recipient": "42"
}
I now read lot's of times, that the write side (= the command handler) shouldn't call the read side.
Taking this into account, the Invoices Microservice must listen to all ContactCreated and ContactDeleted events in order to know if the given recipient id is valid.
Then I'd have thousands of Contacts within the Invoices Microservice, even if I know that only a few of them will ever receive an Invoice.
Is there any best practice to handle those scenarios?
The recipient of an invoice must be a valid contact.
So the first thing you need to be aware of - if two entities are part of different aggregates, you can't really implement "apply a change to this entity only if that entity satisfies a specification", because that entity could change between the moment you evaluate the specification and the moment you perform the write.
In other words - you can only get eventual consistency across an aggregate boundary.
The aggregate is the authority for its own state, but everything else (for example, the contents of the command message), it pretty much has to accept that some external authority has checked the data.
There are a couple approaches you can take here
1) You can blindly accept that the recipient specified in the command is valid.
2) You can try to verify the validity of the recipient from some external authority (aka: a read model of some other aggregate) between receiving it from the untrusted source and submitting it to the domain model.
3) You can blindly accept the command as described, but treat the invoice as provisional until the validity of the recipient is confirmed. That means there is a second command to run on the invoice that certifies the recipient.
Note - from the point of view of the model, these different commands are equivalent, but at the application layer they don't need to be -- you can restrict access to the command to trusted sources (don't make it part of the public api, require authorization that is only available to trusted sources, etc).
Approach #3 is the most microservicy, as the two commands can be separated in time -- you can accept the CreateInvoice command as soon as it arrives, and certify the recipient asynchronously.
Where would you put approach 4), where the Invoices Microservice has it's own Contacts Store which gets updated whenever there's a ContactCreated or ContactDeleted event? Then both entities are part of the same service and boundary. Now it should be possible to make things consistent, right?
No. You've made the two entities part of the same service, but the problem was never that they were in different services, but that they are in separate aggregates -- meaning we can be changing the entity states concurrently, which means that we can't ensure that they are immediately synchronized.
If you wanted immediate consistency, you need a model that draws your boundaries differently.
For instance, if the invoice entities were modeled as part of the Contacts aggregate, then the aggregate can ensure the invariant that new invoices require a valid recipient -- the domain model uses the copy of the state in memory to confirm that the recipient was valid when we loaded, and the write into the book of record verifies that the book of record hadn't changed since the load happened.
The write of the aggregate state is a compare-and-swap in the book of record; if some concurrent process had invalidated the recipient, the CAS operation would fail.
The trade off, of course, is that any change to the Contact aggregate would also cause the invoice to fail; concurrent editing of different invoices with the same recipient goes out the window.
Aggregates are all or nothing; they aren't separable.
Now, one out might be that your Invoice aggregate has a part that must be immediately consistent with the recipient, and another part where eventually consistent, or even inconsistent, is acceptable. In which case your goal is to refactor the model.
The recipient of an invoice must be a valid contact.
This is a business rule. The question should be asked, what does this business rule mean for my application? Who should take responsibility for implementing this rule, or can the responsibility be shared?
One possibility is that, yes, the business rule is about invoices so it should be the responsibility of the Invoices Service to implement it.
However, the business rule is really about the creation of invoices. And the owner of invoice creation in your architecture is, strangely, not the Invoices Service. The reason for this is that the name of the command is CreateInvoiceCommand.
Let's think about this - the Invoices Service will never just create an invoice on its own. It just provides the capability. In this architecture, the actual owner of invoice creation is the sender of the command.
Using this line of reasoning, if the business rule is saying that invoice creation cannot happen against an invalid recipient, then it becomes the responsibility of the command sender to ensure this business rule is implemented.
This would be a very different scenario if, rather than receiving a command, the Invoices Service subscribed to events. As an example, an event called WidgetSold. In this scenario, the owner of invoice creation clearly would be the Invoicing service, and so the business rule would be implemented there instead.
If the user clicks the create invoice for contact 42 button, it's the
user's responsibility to take care that contact 42 exists
Yes, that is correct. The user's intention is to create an invoice. The business rules around invoice creation should, therefore, be enforced at this point. How this happens (or whether this happens at all) is a different question.
But what if the user doesn't care? Then it would create an invoice
with an invalid recipient id.
Also correct. As you say, there are side-effects to this approach, one of which is that you can end up with inconsistent data across your system. That is one of the realities of SOA.
Isn't this somehow similar to this: The Invoice has a currencyCode
property, it's a String.
I don't know if I agree or not. Is asking is this a valid ISO currency? different to asking is entity 42 valid according to another system?. I would think so.
Isn't it kinda the same as given recipient is not null and is valid
according to my Contacts Database?
I agree that in reality, you could implement this validation in the service. I am just saying that I don't think it's the right place for it. If you wanted to do this, you would have to either call out the another service or store all contacts locally, as you framed your question originally. I think it's simpler to just do it outside of the service.
I think that the answer depends on how resilient you want the system to be, that is, how to handle the situation in wich the Contacts Microservice is down (not responding or very slow).
1. You want to be very resilient
If the Contacts Microservice is down, you want to be able to emit invoices for some (maybe most) of the contacts. In this case you listen to the ContactCreated and ContactDeleted and maintain a (eventually consistent) local list of valid contacts; they should be named accordingly to the Ubiquitous language in this bounded context, like Payers (or something like that). Then, in the Application layer, when building the CreateInvoiceCommand you check that Payer is valid and create the command.
2. You don't need to be resilient
If the Contacts Microservice is down, you refuse to generate invoices. In this case, when building the command you make a request to the Invoices Microservice API endpoint and verify that the Payer is valid.
In any case, you check for contact's validity before the command is dispatched.
I’ve been studying the actor model for some time and trying to figure out how to correctly combine it with a RESTful API. I’m struggling how to separate responsibilities of both layers, either by using the ask-pattern or the actor-per-request. With both patterns request-reply semantics are leaking into the actor model, which seems like an anti-pattern. Most messages, initiated by an HTTP-requests, send to an actor require a reply. The receiving actors have multiple conditionals where it needs signals the API it cannot fulfil the request.
Furthermore, what is considered good practice in regard to input validation; should this be implemented as part of the HTTP (for example if field X is a valid email address, if field Y holds an integer). And for complex domain logic, how/should the actor notify the sender when a (pre-)condition fails?
While request/reply is an anti-pattern in inter-actor communication, nothing stands on your way to use it from outside of an actor system. You can use Ask from there and by using combination of Forward + Tell back to original sender send the reply without necessarily using request/reply model inside of an actor.
When it comes to input validation, ofc the simple validation (field presence, email format etc.) can be easily done on the web framework's level. However more advanced cases (like permission management) will probably make use of actors - at least if your business logic uses them as well.
For complex scenarios - try to think with protocols. Describe set of contracts between actors and/or external services, and use messages to control flow of your logic. It's usually hard to describe that kind of reasoning, but it's usually very easy to draw it with pencil ;)
I.e. you may decide to use some kind of AuthorizationGate actor, which given an unathorized request, will validate it: on auth failure it sends back some RequestFailed message back to original sender (the asker), on success it could transform that message into ValidRequest and send it to actor responsible for handling that message type. Then an actor (which handles only valid requests), processes it, sends RequestSucceed or RequestFailed back to original sender (remember to either store that sender as message field, or use actorRef.Forward instead of actorRef.Tell so you don't override it).
I am learning DDD and making use of the CQRS pattern. I don't understand how to validate business rules in a command handler without reading from the data store.
For example, Chris wants to give Ashley a gift.
The command might be GiveGiftCommand.
At what point would I verify Chris actually owns the gift he wants to give? And how would I do that without reading from the database?
There are different views and opinions about validation in command handlers.
Command can be rejected, we can say No to the command if it is not valid.
Typically you would have a validation that occurs on the UI and that might be duplicated inside the command handler (some people tend to put it in the domain too). Command handler then can run simple validation that can occur outside of the entity like is data in correct format, are there expected values, etc.
Business logic, on the other hand, should not be in a command handler. It should be in your domain.
So I think that the underlying question is...
Should I query the read side from Command Handlers?
I would say no. Do not use the read model in the command handlers or domain logic. But you can always query your read model from the client to fetch the data you need in for your command and to validate the command. You would query the read side on the client to check would if Chris actually owns the gift he wants to give. Of course, the validation involving a read model is likely to be eventually consistent, which is of course another reason a command could be rejected from the aggregate, inside the command handler.
Some people disagree saying that if you require your commands to contain the data the handler needs to validate your command, than you can never change the validation logic within your handler/domain without affecting the client as well. This exposes too much of the domain knowledge to the client and go against the fact that the client only wants to express an intent. So they would tend to provide an GiftService interface (which are part of the ubiquitous language) to your command handler and then implement the interface as needed - which might include querying the read side.
I think that the client should always assume that the commands it issues will succeed. Calling the read side to validate the command shouldn't be needed. Getting two contradictory commands is very unlikely (users creating accounts with the same email address). Then you should have a mean to issue a corrective action, something like for example a Saga/Process Manager. So instead making a corrective action would be less problematic that if the command could have been validated and not dispatched in the first place.
It depends if the operation is async or not i.e does the user expect some immediate response. Gift ownership is basically a security feature and that can be done as a 'prep' operation before invoking the actual service or sending the GiveGiftCommand.
The only command validation you can do is to make sure it contains the data in the required format (UI validation) and that the user has the permissions to do that action. But after the command is sent it's up to the Domain to decide if other business constraints are respected.
If the user expects some immediate feedback, you can actually 'wait' until the command is completed and for that you can use an approach where a command handler can provide a result to the sender using a mediator . But this implies that at least some commands are executed in an immediate manner and that might not be the case in your app. However, this is the simplest approach if you want to just return a message error as opposed to implementing compensations and other stuff. Some use cases are simple.
About command handlers and business logic, I disagree with Tomasz Jaskuλa . A command handler is a function, a technical detail. You can put business logic in a command handler or a static function, it doesn't matter. Messages and their handlers are infrastructural components that can be used to implement functionality. For example, in an app you can have Domain Events, Application Events etc . They're all events i.e notification that something changed and you can have event handlers that reside in Domain or in other places.
There's no rule preventing you to 'read' from the db, however at least the read model theoretically is stale. However this might not be such a problem in 99% of cases. For the rest 1% you need very specific solutions.
I just asked the exact same question from a knowledgeable friend of mine and his answer was that it is OK to do this validation inside the command handler (in my case inside an akka persistent actor that interprets commands and writes events to the journal).
However if that is not possible for performance reasons (because processing validation inside a persistent actor would block the actor and that would be a scalability bottleneck for the whole app) then optimistic locking (OCC) can be used.
In other words the validation can be performed in an other actor (lets call it the validator actor - which is not in the persistent actor) , this will not block the persistent actor but it might happen that the data that is used for the validation has changed in the persistent actor while the validation was running in the validator actor) .
If the validator actor returns with an OK and all the data that has been used for the validation is still the same in the persistent actor (has the same version - version as in OCC) as it has been at the point when the command arrived to the command handler (persistent actor) then the command is accepted by the persistent actor , otherwise the validation needs to be resubmitted for re-evaluation to the validator actor.
I am new to CQRS and I am tying to make sense of business rule validation within the write side (domain). I know that client side validation should be done in terms of valid date (required field, string length, valid email, etc) and business rule/business domain related validation should be done in domain side. Actually, same client side validation rules should also be applied to command in the domain since we don't trust the users.
So, we have a valid command (AddEmailToCustomer) and the command handler is invoked on the command. Here is my approach to validation.
Create instances of two command validators in the command handler.
First one validates the command data same as client side validation (required field, valid email, etc.)
Second validator validates the data based on logic within the second validator. Something like "is this customer active", or what ever. I know the changing email doesn't fit here but it is not important. Important thing is there is a business validation here.
We look at the ValidationResult returned by the Validator.Validate(ICommand cmd) and we find out there are errors
We will not get a customer from repository to call on the UpdateEmail method on the AR. So what do we do at this point?
Do I throw and exception in the command handler and add these errors there?
Do I send the command to error queue or somewhere else?
Do I respond with something like Bus.Reply and return error code? If so, what do I do with the error messages?
How do I communicate these errors to the user? I know I can email them later but in a web scenario I can send a request id in the command (or use the message id), and poll for response with the request id and display the error messages to user.
Your guidance is appreciated.
Thanks
It's important to know that commands can be rejected after they are sent to the handler.
At the very least, you could encounter a concurrency violation that cannot be detected until the aggregate root is touched.
But also, the validation that can occur outside of the entity is simple validation. Not only string lengths, numeric ranges, regex matching, etc. but also validation that can be reasonably satisfied through a query or view, like uniqueness in a collection. It's important to remember that validation involving a materialized view is likely to be eventually consistent, which is another reason a command could be rejected from the aggregate, inside the command handler. That said, to preempt the situation, I often use read models to drive UI choices which only allow valid actions.
Validation that cannot occur outside an entity is your business logic validation. This validation is dependent upon the context in which it is run (see Udi Dahan's Clarified CQRS).
Business logic should not be in a separate validation service. It should be in your domain.
Also, I'm of the opinion that validation which occurs in the UI should be re-checked not in the command handler, but in the domain too. That validation is there to prevent corruption to the domain -- if it is not performed outside the domain then the domain is still subject to invalid parameters.
Using command handlers to duplicate this validation is only a convention. If no other front end is sending commands, then it is a useless duplicate. If there are multiple front ends, it is just one choice of where to place the then-necessary duplicate validation, and in those cases I prefer to handle it in the domain.
Lastly, you will need to bubble up commands rejected from within the handler. I accomplish this with exceptions as much as possible.