"We need to move to microservices" is one of the most expensive sentences in technology. It is usually said with good reason: the application is slow to change, releases are risky, and one team's work keeps blocking another's. But the sentence describes a destination, not a decision, and it skips the question that matters: which part, if any, should come out first, and what will it cost to run once it has?
This part of Architecture in Plain English uses one concrete extraction to answer that.
Marula Commerce is fictional, and its systems and figures are illustrative. Marula's shop platform holds orders, checkout, search and the product catalogue in one application with one database. The merchandising team updates the catalogue several times a day: new ranges, price changes, stock photography. Every catalogue change waits for the same fortnightly release as checkout changes, and checkout is the part nobody wants to touch casually. The catalogue looked like an obvious candidate to extract.
What actually changes when you extract
Inside one application, the orders module asks the catalogue for a product price with a function call. It cannot time out. It does not need retries. If the catalogue table and the orders table need to be read together, a database join does it in one query, inside one transaction.
After extraction, the same question becomes a network call to another process, possibly on another machine. That call can be slow, fail, or return an answer that is a few seconds out of date. The orders team can no longer join to catalogue tables, because those tables now belong to the catalogue service and may change shape without warning.
That is the real cost of a microservice. It is not the extra container or the extra pipeline. It is that every interaction between the two parts now needs to be designed for failure, and that data which used to be shared must now be owned.
Five tests before you extract
1. Does it change on its own rhythm? The strongest reason to separate something is that it needs to change at a different pace from its neighbours. Marula's catalogue changes daily; checkout changes monthly at most. That mismatch was the real pain.
2. Can one team own it fully? A service owned by nobody, or by three teams, is worse than a module. Ownership means building, deploying, monitoring and being woken up for it. Marula had a merchandising technology team of four that already did most catalogue work.
3. Can it own its data without shared joins? This is where most extractions fail. If other modules read the catalogue's tables directly, those reads must be replaced with API calls or with copies of the data that the catalogue publishes. Marula found eleven places in the shop platform that joined to catalogue tables. Each one had to be rewritten before the split, not after.
4. Does it need different scaling or availability? Sometimes the answer is no, and that is fine: test 1 alone can justify an extraction. But if a part has very different load (search during a sale, for example) or a different availability target, that strengthens the case.
5. Are tracing and on-call ready for a network hop? Before extraction, a slow page has one place to look. After, it has two processes and a network between them. Without distributed tracing (covered in Part 4) and a clear on-call arrangement, the first incident will be a long one.
If any answer is no, the better move is usually to tidy the boundary inside the application: give the catalogue module a clear interface, stop other modules from reaching into its tables, and see whether that already relieves the pain. A well-bounded module is also the best possible starting point for a later extraction. Our Reuse, refactor, replace or retire article covers that wider choice.
The order of extraction
When the tests pass, the sequence matters more than the technology.
- Draw the boundary in code first. Make every other module call the catalogue through one interface, still in the same process. This is where the eleven direct joins were removed.
- Separate the data. Move catalogue tables into their own schema or database while the code still runs in one application. Other modules now cannot join even if they want to.
- Decide how others get catalogue data. For price at checkout, Marula chose a synchronous API call with a short timeout and a cached fallback price from the last minute. For search, which needs every product, the catalogue publishes change events and the search index keeps its own copy.
- Stand up the service and run both paths. For a period, requests can go to the old module and the new service, and the answers are compared. Only when they agree for long enough is the old path removed.
- Move the route. If an API gateway sits in front (see Part 1), the public path stays the same while the backend changes underneath it.
This is slower than "build the new service and switch over". It is also the version where nobody discovers on launch day that a monthly report has been reading a catalogue column that no longer exists.
The trade-off, stated plainly
| You gain | You pay |
|---|---|
| Catalogue releases without waiting for checkout | Network failure handling on every call |
| A team that owns its domain end to end | Duplicated data, with a staleness window |
| Independent scaling of catalogue reads | Distributed tracing, more pipelines, more on-call |
| A clearer, enforced boundary | Harder cross-domain reporting (it needs a data platform or published events) |
For Marula, the gain was worth it because the release rhythm was hurting the business every week. For an application where everything changes together, the same table would argue for keeping a well-structured module, sometimes called a modular monolith, and spending the effort elsewhere.
Signs you split too early
- Two services that must always be deployed together.
- A "service" that cannot answer any question without calling three others.
- A shared database that two services still both write to.
- Incidents where nobody can tell which service caused the problem.
Each of these means the boundary is in the wrong place, or the extraction stopped halfway. The fix is rarely a better framework. It is usually moving the boundary, or merging back.
Next: Part 3, Why a successful payment can still end in a failed order. All parts are on the series page.
How CloudNala can help
We help teams decide which parts of an application are worth separating, run the five tests with the people who will own the result, and plan an extraction sequence that keeps the business running. Where the answer is "not yet", we help tighten module boundaries so that the later move is cheap.
Work with CloudNala
CloudNala helps organisations move from technology ambition to practical execution across cloud, AI, data, platform engineering and digital services.
Whether you are exploring AI, modernising your cloud environment, building a public-sector digital service, or turning an idea into a working MVP, we can help you shape the roadmap and deliver the next step.
Request an Architecture Review or write to us at consult@cloudnala.co.za