1

I have a Sales service, which takes payments and raises events when a sale is confirmed.

I have an Order service, which consumes this event and records everything that was purchased as part of the trade. Therefore this purchased information is eventually consistent shortly after a sale has been confirmed.

The nice thing about this architecture is that the Sales service has huge volume demand on it, so making it as lightweight as possible is ideal.

Problem is... when a sale is confirmed the Sales service needs to know what has been purchased before any further trade can take place for that customer. This is because there are limits on how many items they can buy etc, and other constraints. It can't rely on the Order service to provide this information at any time, as there may be a backlog in processing orders.

I could solve this by having the Sales service also record all this information immediately when a sale is confirmed, but then I'm introducing substantially more logic and processing into the Sales service. In addition to writing the event,it's now calculating everything purchased as part of the order and pushing it all into its database.

Are there any patterns for solving these types of problems? Am I effectively forced to make the Sales service also understand how to process and store Orders?

FBryant87
  • 4,273
  • 2
  • 44
  • 72

2 Answers2

2

If the order service depends on events from the sales service to make decisions and the sales service needs to be strongly consistent with decisions made by the order service, that strongly suggests that they are in fact the same service. If they happen to be deployed separately, they form a distributed monolith (quite possibly the worst of both worlds).

Levi Ramsey
  • 18,884
  • 1
  • 16
  • 30
  • Interesting point, and you may well be correct. In terms of performance it's a shame because the sales confirmation processing can be expensive, which is why it would be great if the Sales service didn't have to deal with it under heavy traffic. But yes perhaps it's not worth the separation. – FBryant87 Jan 31 '21 at 14:29
  • 1
    All is not necessarily lost. The framing of the problem basically leads inexorably to not separating those concerns. But an alternative framing of the problem may lead to an approach that works well enough. Two things that might be worth considering: why not run potential sales through the customer service (which can have visibility into what customers might be ordering and kick out sales that could breach limits), if per-customer limits are the main thing to enforce. – Levi Ramsey Jan 31 '21 at 20:01
  • 2
    ...on the other hand, have you actually asked the "business" people, point blank, "Is making a sale worth taking a risk that a customer orders too much of something?". If the answer is "we want sales", then eventual consistency might actually be all that you need: if a customer orders too much of an item because there are multiple orders in the (normally very short in the scheme of things) time the order service takes to process, the business may be able to say "sorry, we can't fulfill that order. Here's your refund and a voucher for 10% off your next order." – Levi Ramsey Jan 31 '21 at 20:07
  • Coming back to this one briefly - if we find a way to make the Sales service now acceptably eventually consistent with events raised by the Orders service, is this an approach often used? I ask because these 2 services are still (eventually) dependent on each others events. In DDD they talk about events flowing downstream and not cyclic like in this fashion, I'm just trying to consider what the major problems with it are, if any – FBryant87 Mar 02 '21 at 07:03
  • 1
    DDD examples and such may typically have acyclic flows of events between services for simplicity, but I don't think a cyclic flow of information encoded as events is out of the ordinary. Consider an inventory service which reserves items which are ordered but not sold (setting aside potential lost sales, etc.). After a sale has completed, the reservation will have to close out. Maybe the information is laundered through intermediate services, but that's still cyclic. – Levi Ramsey Mar 16 '21 at 23:38
2

Your choices are either building a monolithic application that uses a single database or embrace eventual consistency and rely on compensations if things go wrong.

Note that in any non-toy application you have to support compensations anyway. For example, the last item that was promised to the customer was damaged during packaging. It would require the order to be canceled or delayed. Handling of such a situation is not much different from the sales service handling not up to date information about the item availability.

Handling all such edge cases is practically impossible when using queues and independent services. It becomes a tractable problem when employing a centralized orchestration solution like temporal.io.

Disclaimer: I'm tech lead of the Temporal open source project.

Maxim Fateev
  • 6,458
  • 3
  • 20
  • 35
  • Appreciate this. If we do embrace eventual consistency, is it ok for the Sales and Order services to be eventually dependent on each others events? (delays are acceptable in both cases) I ask because DDD talks about events only flowing downstream, and not both ways in this case – FBryant87 Mar 02 '21 at 07:08
  • 1
    If you use an orchestrator like temporal.io then it eliminates the need for these services to explicitly depend on each other. – Maxim Fateev Mar 02 '21 at 19:38