Module 4.11 laid the architecture styles side by side and asked in lesson 4.11.2 when a service of its own pays off. This module does it: the tenders context becomes the Kladde's first service of its own. It has a rhythm of its own (the nightly import), a load of its own and hardly any writes from others, and its boundary in the code has stood since lesson 4.3.6. The module builds on two modules: on 4.6 (outbox, inbox, process manager), because services talk through messages, and on 4.7 (networking, identity), because every call now goes over the network. At the end, in lesson 4.12.8, you put the microservices next to the virtual actors from module 4.5.

This first lesson clarifies where a service boundary belongs and how big a service may be. Then you extract the tenders, in such a way that no caller in the monolith notices.

One service per bounded context

The most important rule stands in one sentence in Microsoft's guide to microservices in the Azure Architecture Center: A microservice is no smaller than an aggregate and no larger than a bounded context. Two things follow from that:

  • A service doesn't mix models. If a service mixes two bounded contexts, it mixes two languages and two models. Then it is the domain analysis from module 4.2 that is wrong, not the code.
  • A context may have several services, not the other way around. If the tenders context one day has an import service and a search service, that is fine, as long as both speak the same model and belong to one team.

The most common wrong boundary is one service per table or entity: a contact service, a quote service, a quote line service, a price service, each with create, read, update, delete. That looks tidy and is the opposite of a cut by business capability:

Use case “create a quote for a tender” Cut by entity Cut by context
Calls over the network read tender, read contact, create quote, then once per line create line and read price read tender; the quote is created with its lines in one call to itself
Where the rule “a total above the limit needs a second approval” lives spread out: the quote service and the line service have to coordinate in the Quote aggregate (lesson 4.3.2)
Transaction no shared one: a half-created line stays behind one per aggregate, as before
What happens when a service fails no quote possible, even if only the price service is missing without the tenders service only the link to the tender is missing

A cut by entities produces chatty calls: For one case it takes many small requests, each with network latency and a failure of its own. The aggregates are torn apart, their invariants spread over services. In the end you have a distributed monolith: all the disadvantages of distribution, none of its advantages, because everything can again only be delivered together.