1

I am learning to develop microservices using DDD, CQRS, and ES. It is HTTP RESTful service. The microservices is about online shop. There are several domains like products, orders, suppliers, customers, and so on. The domains built in separate services. How to do the validation if the command payload relates to other domains?

For example, here is the addOrderItemCommand payload in the order service (command-side).

{
"customerId": "CUST111", 
"productId": "SKU222",
"orderId":"SO333"
}

How to validate the command above? How to know that the customer is really exists in database (query-side customer service) and still active? How to know that the product is exists in database and the status of the product is published? How to know whether the customer eligible to get the promo price from the related product?

Is it ok to call API directly (like point-to-point / ajax / request promise) to validate this payload in order command-side service? But I think, the performance will get worse if the API called directly just for validation. Because, we have developed an event processor outside the command-service that listen from the event and apply the event to the materalized view.

Thank you.

Benedict
  • 61
  • 1
  • 6

2 Answers2

2

As always everything depends on the specifics of the domain but as a general principle cross domain validation should be done via the read model.

In this case, I would maintain a read model within each microservice for use in validation. Of course, that brings with it the question of eventual consistency. How you handle that should come from your understanding of the domain. Factors such as the length of the eventual consistency compared to the frequency of updates should be considered. The cost of getting it wrong for the business compared to the cost of development to minimise the problem. In many cases, just recording the fact there has been a problem is more than adequate for the business.

I have a blog post dedicated to validation which you can find here: How To Validate Commands in a CQRS Application

Codescribler
  • 2,235
  • 18
  • 22
2

As there are more than one bounded contexts that need to be queried for the validation to pass you need to consider eventual consistency. That being said, there is always a chance that the process as a whole can be in an invalid state for a "small" amount of time. For example, the user could be deactivated after the command is accepted and before the order is shipped. An online shop is a complex system and exceptions could appear in any of its subsystems. However, being implemented as an event-driven system helps; every time the ordering process enters an invalid state you can take compensatory actions/commands. For example, if the user is deactivated in the meantime you can cancel all its standing orders, release the reserved products, announce the potential customers that have those products in the wishlist that they are not available and so on.

There are many kinds of validation in DDD but I follow the general rule that the validation should be done as early as possible but without compromising data consistency. So, in order to be early you could query the readmodel to reject the commands that couldn't possible be valid and in order for the system to be consistent you need to make another check just before the order is shipped.

Now let's talk about your specific questions:

How to know that the customer is really exists in database (query-side customer service) and still active?

You can query the readmodel to verify that the user exists and it is still active. You should do this as a command that comes from an invalid user is a strong indication of some kind of attack and you don't want those kind of commands passing through your system. However, even if a command passes this check, it does not necessarily mean that the order will be shipped as other exceptions could be raised in between.

How to know that the product is exists in database and the status of the product is published?

Again, you can query the readmodel in order to notify the user that the product is not available at the moment. Or, depending on your business, you could allow the command to pass if you know that those products will be available in less than 24 hours based on some previous statistics (for example you know that TV sets arrive daily in your stock). Or you could let the customer choose whether it waits or not. In this case, if the products are not in stock at the final phase of the ordering (the shipping) you notify the customer that the products are not in stock anymore.

How to know whether the customer eligible to get the promo price from the related product?

You will probably have to query another bounded context like Promotions BC to check this. This depends on how promotions are validated/used.

Is it ok to call API directly (like point-to-point / ajax / request promise) to validate this payload in order command-side service? But I think, the performance will get worse if the API called directly just for validation.

This depends on how resilient you want your system to be and how fast you want to reject invalid commands.

Synchronous call are simpler to implement but they lead to a less resilient system (you should be aware of cascade failures and use technics like circuit breaker to stop them).

Asynchronous (i.e. using events) calls are harder to implement but make you system more resilient. In order to have async calls, the ordering system can subscribe to other systems for events and maintain a private state that can be queried for validation purposes as the commands arrive. In this way, the ordering system continues to work even of the link to inventory or customer management systems are down.

In any case, it really depends on your business and none of us can tell you exaclty what to do.

Constantin Galbenu
  • 16,951
  • 3
  • 38
  • 54
  • If the validation has been done in command-side using querying the read-side, we could build the snapshot in the command-side. The snapshot fields would be same with the materialized view because all the data required is retrieved when we do validation. So, do we still need the denormalizer or event processor to build materialized view? – Benedict Jun 14 '17 at 01:29
  • Besides that, when we subscribe for an event from another domains (e.g. customer changed event), the event could change the data in customer order. Do we need to persist the customer event to order event store because the customer event is write some changes in order domain? – Benedict Jun 14 '17 at 01:37
  • @Benedict when a remote event arrive to your local bc, you have a choice: 1. Create a command that leads to a new local event that is used by the OrderAggregate to validate the `addOrderItemCommand` or 2. Build&maintain a validator's private state that is used in validation of the `addOrderItemCommand` command – Constantin Galbenu Jun 14 '17 at 07:07