Centralizing the decision-making
Imagine we’re building yet another e-commerce platform. One of its crucial business processes is, of course, processing an order. After a successful payment, the Orders module (domain) has to call Warehouse asynchronously to prepare purchased goods. Yet, they may not be there. Usually, it’s not a big deal, since we can get them from our suppliers. But what if any of the items are not available anymore? The order has been already placed! Money has changed hands. Our customer is already waiting for delivery. We have no choice - the order has to be canceled.
Let’s assume Warehouse has to let the Orders know about it. Putting deployment strategy and communication medium aside,
how should we name such an operation on the API level? Should it be cancelOrder
or handleItemUnavailable
?
One would say it’s just a matter of preference, but I disagree with that. In fact, it’s not about these two names
in particular, but about who’s making the decision. Surprisingly, our choice has serious consequences for the overall
design and long-term maintainability.
To achieve high cohesion and loose coupling we need to centralize the decision-making. This means that each module (domain) should be the only decision-maker when it comes to its area of responsibility. At the same time, these modules should know only what is absolutely necessary about each other.
Choosing cancelOrder
as a name makes the Warehouse deciding about when to cancel an order. In fact, Warehouse
may not need the order concept at all! From its perspective, the requested item is permanently unavailable, that’s all.
Avoiding importing foreign concepts supports loose coupling.
Orders should be the only one caring about, well… orders. The whole order lifecycle management should be encapsulated
here. Encapsulation improves cohesion, which is desired. Choosing handleItemUnavailable
means it’s up to Orders to make
the next move.
Centralizing the decision-making increases long-term maintainability. To improve the customer experience, we may
avoid immediate cancellation in such cases. Instead, we could offer a similar product at a reduced price. With
cancelOrder
as a name, we would need to touch at least two modules (domains) to introduce such a change. Choosing
handleItemUnavailable
makes the change transparent to the Warehouse.
In the event-driven world, we could even turn this call into a more general WarehouseItemUnavailable
event.
This would not only eliminate the point-to-point communication, but also allow plugging additional consumers later on.
Once the item is no longer available we may want to update our catalog to prevent further purchases, or notify our
sales department…
The described approach scales well across different deployment strategies. Orders and Warehouse could be two different files or modules of the same application, as well as separate services. Depending on the context, the operation name may slightly differ. Yet, the reasoning behind it stays the same. We want to centralize the decision-making.