Module 5: Extracting a Service
The order of extraction
Overview
Lessons 2 to 6 taught you to extract one service end to end: find the bounded context, put the bidirectional ACL on it, give it ownership of its data, cut the shared DB. But a monolith doesn't have a single bounded context —Mercado has four: catalog, orders, payments, shipping—. And the order in which you extract them decides whether the migration flows or gets stuck. This lesson teaches how to choose the extraction order: which comes out first, which last, and why.
The principle is short and counterintuitive for whoever is in a hurry: you extract the most independent context first, not the most important or the most painful. The most independent context is the leaf —the one that doesn't depend on anyone else to work, the one others read but that reads no one—. Extracting the leaf first cuts a clean, lone dependency: on taking it out, you drag nothing along with it. Extracting the most coupled one first —the hub everything hangs off of— is the opposite: to take it out, you'd have to untangle its dependencies with all the others, dragging half the system in the first extraction.
This lesson makes it measurable, extending the seam measurement of lesson 2. For each of Mercado's four modules, we score its independence —how many outbound it has (how many it depends on), how many inbound (who reads it), and whether it writes shared state— and order from most to least independent. The result is an extraction plan: catalog first (leaf, 0 outbound), orders last (the hub, 3 outbound). And you're going to see that extracting from the leaves toward the hub has a cumulative effect: each extraction of an independent context simplifies the seam of those that depended on it.
Connection with the module. Lesson 2 measured the seam to find one bounded context; this one uses the same measurement, extended to all, to order them. Lessons 3 to 6 are the technique you'll apply to each context in the order you decide here. Lesson 8 (the capstone) executes the complete extraction of the first on the list, catalog. Notice the boundary: here we decide the order of extraction by coupling. The sequence can also weigh other factors —the business value, the risk (the money of payments)— that are recorded in the ADR of architecture-decisions-and-tradeoffs; this module contributes the axis of structural independence, which is the one that makes the extraction technically clean.
An analogy: untangling a skein of yarn
You have a tangled skein of yarn —a knot of crossed threads— and you want to take it apart without making the knot tighter. Where do you start?
If you pull from the center of the knot, where the threads are most crossed, the knot tightens: each thread you move pulls three others, and instead of loosening, you tangle more. It's the worst place to start. Anyone who has untangled earbuds or Christmas lights knows it: attacking the tangle at the most tangled point is a guarantee of frustration.
What does work is looking for the loose end —the thread's end that hangs free, that isn't crossed with anything— and pulling from there. The loose end comes out clean, without pulling anything, and on coming out it loosens the rest of the knot a little. Then you look for the next end, now a bit more accessible because the first already came out, and you pull. Little by little, always starting with the loosest, the knot comes undone. You never attack the center; you let the center loosen on its own as you remove the edges.
In the monolith's extraction: the knot is the coupled system; the loose ends are the leaf bounded contexts (the ones that depend on no one, like catalog); and the center of the knot is the hub (orders, everything hangs off of it). You start with the loose end —you extract the leaf—, which comes out clean and loosens the knot: on taking out catalog, the modules that depended on it now depend on a clean service instead of a piece of the monolith, so they're a bit less tangled. You continue with the next loosest. And the hub, orders, you leave for last, when almost all its threads —catalog, payments, shipping— came out and the center loosened on its own. Pulling the hub first would tighten the knot; pulling the leaves first undoes it.
Worked example: score the independence and order
We're not going to opine on the order: we're going to compute it. For each of Mercado's modules we define its profile —which others it depends on (depends_on, the outbound seam), who reads it (derived), and whether it writes shared state (write_heavy)—. With that we score the independence: lower score = more independent = extracted first. The score is dominated by the outbound seam, because a module that depends on no one runs alone when extracted.
# Profile of each module of the Mercado monolith:
# depends_on -> which other modules it CALLS (efferent / outbound seam)
# depended_by -> who CALLS it (afferent / inbound seam)
# write_heavy -> writes a lot of its own shared state (harder to own)
modules = {
"catalog": {"depends_on": [], "write_heavy": False},
"payments": {"depends_on": ["orders"], "write_heavy": True},
"shipping": {"depends_on": ["catalog", "orders"], "write_heavy": True},
"orders": {"depends_on": ["catalog", "payments", "shipping"], "write_heavy": True},
}
# depended_by is derived from the depends_on of the others.
for m in modules:
modules[m]["depended_by"] = [o for o in modules if m in modules[o]["depends_on"]]
def extract_score(info):
# Lower = more independent = extracted first.
# The OUTBOUND seam dominates: if it depends on no one, it runs alone when extracted.
return len(info["depends_on"]) * 2 + (1 if info["write_heavy"] else 0)
ranked = sorted(modules.items(), key=lambda kv: extract_score(kv[1]))
print("Extraction order: first the most independent bounded context\n")
print(f"{'#':>6} {'module':<12}{'outbound':>10}{'inbound':>10}{'write?':>8}{'score':>7}")
print("-" * 55)
for i, (name, info) in enumerate(ranked, start=1):
print(f"{i:>6} {name:<12}"
f"{len(info['depends_on']):>10}{len(info['depended_by']):>10}"
f"{('yes' if info['write_heavy'] else 'no'):>8}{extract_score(info):>7}")
print("-" * 55)
print("\nExtraction plan:", " -> ".join(name for name, _ in ranked))
print("\n catalog: 0 outbound (leaf), many inbound (read it), doesn't write -> FIRST.")
print(" orders: 3 outbound (the hub everything hangs off) -> LAST.")
print(" Extracting the most coupled first (orders) would drag the other three")
print(" along; extracting the leaf first cuts a clean, lone dependency.")
What to expect. When you run the file, the output is exactly this:
Extraction order: first the most independent bounded context
# module outbound inbound write? score
-------------------------------------------------------
1 catalog 0 2 no 0
2 payments 1 1 yes 3
3 shipping 2 1 yes 5
4 orders 3 2 yes 7
-------------------------------------------------------
Extraction plan: catalog -> payments -> shipping -> orders
catalog: 0 outbound (leaf), many inbound (read it), doesn't write -> FIRST.
orders: 3 outbound (the hub everything hangs off) -> LAST.
Extracting the most coupled first (orders) would drag the other three
along; extracting the leaf first cuts a clean, lone dependency.
Read the table top to bottom, because the order of the rows is the extraction plan, and the score column justifies it.
catalog (score 0) goes first. It has 0 outbound —it depends on no other module—, many inbound (they read it), and it doesn't write shared state (write: no). It's the perfect loose end: you can pull it and it comes out clean, without dragging anything. When you extract it, it doesn't need to call back to the monolith, because it depended on no one. Score zero: maximum independence.
orders (score 7) goes last. It has 3 outbound —it depends on catalog, payments, and shipping—: it's the center of the knot, the hub everything hangs off of. Extracting it first would mean, for it to work as a service, having to call back to the monolith to the other three modules on each operation —each call now crossing the network—. It's the most crossed thread; pulling it first tightens the knot. It's left for last, when the other three already came out and their dependencies point to clean services.
In the middle, payments (score 3) and shipping (score 5), ordered by their coupling: payments depends on one (orders), shipping on two (catalog, orders). The score places them according to how many ties they have into the monolith.
The Extraction plan: catalog -> payments -> shipping -> orders is the direct reading of the table. And notice the cumulative effect the close mentions: on extracting catalog first, shipping —which depended on catalog— now depends on a clean service instead of a piece of the monolith. Each extraction of a leaf loosens the knot for the following ones: the seam of those that remain simplifies as their dependencies leave the monolith. That's why you extract from the leaves toward the hub, never the other way around.
Deep dive: structural independence and the other factors
The score of this example measures one thing —the structural independence, how many ties each context has into the monolith— and it's the axis that makes the extraction technically clean. But it's worth being honest about two nuances: how it combines with other factors, and what happens with the cumulative effect.
Coupling isn't the only factor, but it's the one that makes the extraction possible. A real migration also weighs the business value (which context, extracted, delivers more benefit?) and the risk (which hurts most if it goes wrong?). Notice payments: by structural coupling it comes out second (score 3, little dependency), but it handles money —a failure in its extraction is much more expensive than one in the catalog—. A prudent team could, for risk, move it later in the sequence even though its independence allows it earlier, and practice first with contexts where making a mistake is cheap. The independence score says what's technically easy to take out; the final sequence combines that with value and risk, which is a decision recorded in the ADR (architecture-decisions-and-tradeoffs). What doesn't change by any factor is that catalog —the low-risk, high-use leaf— goes first, and orders —the hub— goes last. That skeleton is fixed by the coupling; the business factors tune the middle.
The cumulative effect: extracting loosens the knot. The scores in the example are a snapshot of the initial state. But the coupling changes as you extract: when catalog comes out, it stops being a piece of the monolith and becomes a service with a clean API. The modules that depended on catalog —shipping, orders— now have a dependency toward a service, not into the monolith, which is easier to handle. If you recomputed the scores after each extraction, you'd see the knot loosen: previously tangled contexts become extractable as their dependencies come out. That's why the leaves→hub order isn't just "the easy first"; it's a strategy where each step enables the next.
Extracting from the leaves toward the hub loosens the knot:
step 0 (initial): catalog <- shipping <- orders -> payments
(all inside the monolith, tangled)
step 1 (catalog out): [catalog svc] <- shipping <- orders
shipping now depends on a SERVICE, not the monolith
step 2, 3, ...: each leaf that leaves simplifies the seam of those that remain,
until orders (the hub) is left almost alone -> extractable
And a warning about the premature hub. The temptation to extract orders first is real: it's usually the most central module, the most valuable, the one "everyone wants to be a service." But it's the center of the knot. Extracting it first forces you to decide, all at once, how it communicates with catalog, payments, and shipping —which are still inside the monolith—, creating a service that calls back to the monolith everywhere: a "distributed service" that's slower and more fragile than the monolith you had. The hub is extracted when its dependencies are already services, not before.
Common mistakes
Extracting the most coupled context first. What happens: the team starts with orders (or any hub) because it's the most important or the most central. Why it happens: the hub is the star module; it seems the one that benefits most from being a service, and the hurry pushes to attack it first. How to spot it: the extracted "service" calls back to the monolith constantly —to catalog, to payments, to shipping— because its dependencies are still inside; each operation crosses the network several times. How to fix it: extract from the leaves toward the hub, never the other way around. Start with the context with the fewest outbound (catalog, score 0), which comes out clean and loosens the knot. The hub is extracted at the end, when its dependencies are already services. Pulling the center of the knot first tightens it; pulling the loose end undoes it.
Choosing the order by importance or pain instead of by coupling. What happens: the order is decided by "which matters to us most" or "which hurts us most," ignoring the seam. Why it happens: value and pain are visible and urgent; coupling is abstract. How to spot it: the first extraction gets stuck untangling dependencies nobody measured, instead of advancing. How to fix it: coupling is the axis that decides what's technically extractable first, and that axis puts the leaf at the front. Value and risk tune the middle of the list (and can defer the money of payments), but they don't change the skeleton: leaf first, hub last. Measure the seam, order by independence, and then adjust for value and risk —not the other way around—. Starting with the important without looking at the coupling is starting at the center of the knot blindfolded.
Not recomputing the order as the migration advances. What happens: the team makes the plan once, at the start, and follows it to the letter without noticing that each extraction changed the coupling map. Why it happens: the initial plan feels definitive. How to spot it: a context that was "hard" in the plan is now easy (its dependencies already came out), but the team keeps postponing it out of inertia. How to fix it: the extraction order is dynamic. After each extraction, the knot loosened: previously tangled contexts may have become extractable. Review the seam every so often and let the order adapt —the cumulative effect of taking out the leaves is precisely that the hub, impossible at the start, becomes reachable at the end—. An extraction plan is a compass that recalibrates, not a frozen map.
Exercises
Exercise 1 — The loose end. With the analogy of the skein of yarn, explain: (a) why pulling from the center of the knot tightens it; (b) what the loose end represents in the extraction; (c) how "loosening the knot" translates to the effect of extracting a leaf.
See solution
(a) Pulling from the center of the knot tightens it because there the threads are most crossed: each thread you move pulls three others, so instead of loosening, you tangle more. In the extraction, the center of the knot is the hub (orders, with 3 outbound): extracting it first forces untangling its dependencies with catalog, payments, and shipping all at once, creating a service that calls back to the monolith everywhere —the tightened knot—.
(b) The loose end represents the leaf bounded context: the one that isn't crossed with anything, the one that depends on no one (0 outbound), like catalog. It's the free end of the thread: you can pull it and it comes out clean, without pulling anything else. It's where you start undoing the monolith.
(c) "Loosening the knot" translates to the fact that, on extracting a leaf, the modules that depended on it now depend on a service with a clean API instead of a piece of the monolith. When you take out catalog, shipping —which read it— now has a dependency toward a service, easier to handle. The seam of those that remain simplifies: the knot, a bit looser. Each extracted leaf makes the next end more accessible, until the hub, impossible at the start, is left almost alone and becomes extractable.
Exercise 2 — Read the score. In the output, catalog had score 0 (0 outbound) and orders score 7 (3 outbound). (a) Why does the outbound column dominate the score and not inbound? (b) What concrete problem would orders extracted first have? (c) Why do payments (score 3) and shipping (score 5) go in the middle, in that order?
See solution
(a) Because the outbound counts how many other modules this one depends on to work, and that's the measure of how much is dragged along when extracting it. A module with 0 outbound runs alone; one with 3 outbound, extracted, has to call back to the monolith to those 3. The inbound (who reads it) measures how useful it is to extract it, but not how hard —reading from others doesn't create ties toward others—. The score is dominated by the outbound because the extraction order is about minimizing what you drag, and what you drag are the outbound dependencies.
(b) orders extracted first would have to call back to the monolith to catalog, payments, and shipping —which are still inside— on each operation, each call now crossing the network with its latency and its possibility of failing. It would be a "service" slower and more fragile than the module it was: a distributed service with all the disadvantages of the network and none of the advantages of independence, because its dependencies are still trapped in the monolith. It's the center of the knot pulled first.
(c) They go in the middle because their outbound coupling is between that of the leaf and that of the hub: payments depends on one (orders, 1 outbound, score 3), shipping depends on two (catalog and orders, 2 outbound, score 5). The score orders them by how many ties into the monolith they have: fewer ties, earlier. payments before shipping because it has fewer outbound dependencies. (In a real migration, the risk of payments's money could defer it, but by pure coupling it comes out before shipping.)
Exercise 3 — The dynamic order. After extracting catalog (step 1), the team prepares to extract shipping, which depended on catalog and orders. (a) How did shipping's coupling change on catalog coming out? (b) Why does this illustrate that the order is dynamic? (c) What context is still the hardest and why?
See solution
(a) Before, shipping depended on catalog and orders, both inside the monolith (two ties into it). On catalog coming out, one of those dependencies now points to a service with a clean API instead of a piece of the monolith. shipping still has two dependencies, but one of them is now toward a well-defined service —easier to handle—: its seam simplified. The knot loosened a bit for shipping.
(b) It illustrates that the order is dynamic because the coupling map changes with each extraction. The initial score was a snapshot of the starting state; after taking out catalog, the state is different, and if you recomputed the scores, shipping would be a bit more "loose" than at the start. An extraction plan isn't a frozen map followed blindly, but a compass that recalibrates: each extracted leaf can make a previously tangled context extractable.
(c) orders is still the hardest, because it's the hub: it depends on catalog, payments, and shipping. Even though catalog already came out (one of its dependencies is now a service), it still depends on payments and shipping, which are still inside the monolith. orders becomes truly extractable only when its three dependencies are services —that is, at the end—. That's the meaning of extracting from the leaves toward the hub: the hub is left for when the knot, loosened by all the leaves that came out, releases it almost on its own.
Summary and next step
In this lesson you raised the view from the tree to the forest: the order of extraction. You saw, with the skein of yarn, that you start with the loose end (the leaf) and not with the center of the knot (the hub), because pulling the center tightens and pulling the end loosens. And you computed it: you scored the independence of Mercado's four modules and got the plan catalog → payments → shipping → orders, with catalog first (leaf, 0 outbound, score 0) and orders last (hub, 3 outbound, score 7). You learned that structural independence is the axis that makes the extraction technically clean, that value and risk tune the middle of the list (and can defer the money of payments), and that the order is dynamic: each extracted leaf loosens the knot for the following ones.
Before moving on you should be able to: state the principle of the order (the leaf first, the hub last) and why; read an independence score and produce the extraction plan; explain why extracting the hub first creates a fragile distributed service; and describe the cumulative effect of extracting from the leaves toward the hub.
With this you have the complete module: you know how to find the bounded context (L2), put the bidirectional ACL on it (L3-L4), give it ownership of its data (L5), cut the shared DB (L6), and in what order to extract all the contexts (L7). Lesson 8 —the capstone— puts it all together in a single extraction executed end to end: take Mercado's catalog (the first of your plan) out to its own service, run as an extraction journal in phases, with the monolith responding identical in each one. You know each piece separately; now you'll see them work together in a real migration.
Resources
- Sam Newman, Monolith to Microservices (O'Reilly, 2019), ch. 2, "Planning a Migration" — the chapter on how to prioritize and sequence the decomposition: start with what's easy to extract and delivers benefit, and leave the most coupled for when the team masters the process. The direct reference of this lesson. In English.
- Chris Richardson, "Refactoring a monolith into microservices" — microservices.io/refactoring/. The guide to incremental decomposition: extract services one at a time, starting with the ones of lowest coupling, to reduce the risk of each step. In English.
- Martin Fowler, "MonolithFirst" — martinfowler.com/bliki/MonolithFirst.html. The context on when and how to decompose: decompose a monolith that already exists (with its learned boundaries) by the cleanest seams, not by the most desired ones. In English.
- Chris Richardson, "Pattern: Decompose by subdomain" — microservices.io/patterns/decomposition/decompose-by-subdomain.html. The card of the pattern of decomposing by subdomain, with the idea that the decomposition order follows the domain's natural boundaries and its coupling. In English.