Skip to the content
Software Made Clear Diagrams that show the mechanism About

SOA vs microservices

ANSWER

Both split a system into services and then disagree about what sits between them. The older style put the cleverness in a shared bus; the newer pushes it out to the endpoints, trading one central thing that can fail for many small ones to operate.

IN PLAIN TERMS

One office sends everything through a central post room; in another, every desk keeps its own outbox. The post room can sort, translate and log in one place — until it is the reason nothing moves, and then everybody starts posting their own, much as the second office did all along.

Search for this comparison and the suspicion behind it is usually the same one: two names for the same idea, one of them carrying a decade of vendor hype the other never had. Something real does separate them, and it has nothing to do with how many services a system ends up with. It is where the coordination between those services lives — concentrated in one shared piece of middleware that everything routes through, or spread out to the edges and left to each service to handle on its own. Everything else in the argument follows from that one placement.

The argument is about the middle#

The older style — service-oriented architecture as it was conventionally built — concentrated the hard parts of talking between systems into shared middleware: a bus that routed a message to whichever system should handle it, transformed its format when sender and receiver did not agree on one, and orchestrated a sequence of calls across several systems as a single business process. Put a rule on the bus and every system behind it inherits that rule at once; put a monitor on the bus and every message crossing it is visible from one place. For a landscape of systems that were never designed to talk to each other, translating in one shared place genuinely earns its keep.

The cost is that the bus becomes the thing every change has to go through. A new field on a message, a new routing rule, a new transformation — each one is a change to shared infrastructure, usually owned by a team that is not the team asking for the change, and usually queued behind whatever else that team already has scheduled. The bottleneck is not really technical; a large enough team could, in principle, keep the bus responsive. It is organisational: one shared thing sitting on the path between every pair of services concentrates the decision rights over that path in whoever owns the box.

Microservices respond to that specific cost, not to the presence of shared infrastructure in general. The newer style keeps the layer between services deliberately thin — pass a request from one service to another with as little shared logic acting on it along the way — and pushes the problems the bus used to absorb out to the services themselves. The phrase the microservices literature has settled on for its own instinct is “smart endpoints and dumb pipes”: the decisions live at the ends of a connection, not inside it. Removing the queue at the bus does not remove the problems that used to queue there. It hands each one to whichever service now owns it.

What came back#

Push the logic out to the edges and, past a certain number of services, several of the problems the bus existed to solve reappear anyway — just not inside one shared box. Routing a request to the right instance, and retrying it when a call fails, start showing up again, now handled by something that sits beside each service rather than between all of them: a sidecar process running next to the service, or a gateway at the edge of the system. When there is no shared bus applying one retry policy on everyone’s behalf, deciding what a retry actually means becomes each service’s own responsibility — this article works through what handling the same request twice correctly requires, once nothing upstream is deciding that for it. Keeping message formats compatible as they change starts showing up again too, usually as a schema registry that producers and consumers check before they trust a payload — a narrower job than the transformation a bus used to do, but aimed at the same failure: two systems disagreeing about what a message means. And the visibility a bus gave for free, because every message already passed through it, has to be rebuilt on purpose, usually as centralised logging or tracing that every service is made to write into.

None of these three is the bus reappearing under a different name, and treating them as a tidy one-for-one replacement overstates what actually happened. A schema registry does not orchestrate a business process, and a sidecar does not enforce a business rule the way a bus’s rule engine could. What is accurate to say is narrower and more interesting than a clean correspondence: the functions the bus used to perform did not disappear, because the problems behind them — mismatched formats, calls that need retrying, a system nobody can see all of at once — do not disappear just because the bus did. What changed is whether the thing solving them sits on the request path, owned by one team every other team depends on, or sits beside it, adopted piece by piece and owned however each team chooses to own its own copy.

Splitting the services, keeping the database#

The mistake this comparison runs into most often has nothing to do with which side a team picked. It is splitting the services and leaving the database behind, whole, underneath all of them. Every service gets its own repository, its own deploy pipeline, its own name — and they all read and write the same tables the earlier system used to own alone. A schema change still has to go through every team whose service touches that table, a slow query written by one service degrades every other service sharing the database, and no single service can be tested, understood or reasoned about without a copy of the whole schema sitting behind it.

The tell is in the calendar, not the architecture diagram: a deploy that is supposed to be independent still needs a coordination meeting before it can ship, because the boundary that actually matters — the data — was never drawn. Calling the result microservices describes the deployment units, not the coupling, and the coupling is what decides whether teams can really move independently. Where that data boundary gets drawn, service by service, is the deeper subject of the architecture path — this comparison has no place on that roadmap itself, only a question in common with it.

Splitting a shared database is genuinely hard, though, and it is not always worth doing. A system built around one shared schema is often not ready to be split at the service boundary at all, and the more useful conclusion is frequently to leave it alone rather than draw service lines around a database that still has to be touched as one thing regardless of how many deploy pipelines sit above it.

Situation Take Because
Many mismatched systems must interoperate Shared bus Translation in one place earns its keep
Teams need to deploy without coordinating Thin middle A shared bus queues every change
Policy must be enforced identically Shared bus One place to enforce it
Services would share a database anyway Neither yet The data boundary has not been drawn

IF YOU REMEMBER ONE THING

The disagreement is about where coordination lives — concentrated in a shared bus, or pushed out to each service — and moving it does not remove the problems it was absorbing. Whichever side you pick, the boundary that decides whether teams can move independently is the data, not the deploy pipeline.

Questions people also ask

5 QUESTIONS
Are microservices just SOA done properly?

No — they answer the same problem, splitting a system into services, with an opposite instinct about where coordination should live. SOA concentrates it in shared middleware; microservices push it out to each service. Calling one a corrected version of the other treats a genuine trade-off as a mistake somebody eventually fixed.

What is an enterprise service bus?

Shared middleware that a system's services all route through, conventionally providing message routing, format transformation between mismatched systems, and orchestration of a multi-step process across several of them. Putting a rule or a monitor on the bus applies it everywhere at once — and makes every change to it a change every dependent team has to wait on.

Can microservices share a database?

Technically, yes; in practice it defeats the point. Independent deployability assumes a service's schema is its own to change. Two services reading and writing the same tables recreate the coordination a shared bus imposed, just without anything visible enforcing it — a schema change now needs agreement from every team touching that data.

Is a service mesh the new bus?

Partly, and only for some of what a bus did. A mesh's sidecars can standardise routing and retries across services, which is real overlap. They do not transform message formats or orchestrate a business process the way a bus did, so the resemblance covers a narrower slice of the job than the name suggests.

How small should a service be?

Small enough that one team can own it, deploy it, and change its schema without asking another team first. That is a statement about ownership and boundaries, not a target measured in code — a service sized by any other rule can still end up coupled to its neighbours through a database or a bus it never accounted for.