Skip to main content

Service-Based Architecture: Coarse-Grained Services with a Shared DB

What This Concept Is

Service-based architecture is a distributed style that sits between modular monolith and microservices. It is, in Richards and Ford's framing, the most forgiving distributed architecture.

Its defining properties:

  • A small number of coarse-grained services, typically 4-12. Each service is roughly domain-subsystem-sized (catalog, checkout, fulfillment, billing), not bounded-context-sized.
  • Each service has its own process and can be deployed independently, but services are larger than microservices.
  • A shared database (or a small number of shared databases) behind the services. Optionally partitioned by domain, but not isolated per service.
  • Synchronous (REST) calls through a small API layer or user interface for orchestration.
  • Single UI or small number of UIs calling the services.
                    +-----------+
| UI |
+-----+-----+
|
v
+------- API gateway --------+
| | | |
v v v v
+--------+ +--------+ +--------+ +--------+
|catalog | |checkout| |fulfill | |billing |
+---+----+ +---+----+ +---+----+ +---+----+
| | | |
+----------+----------+----------+
|
v
+----------------+
| shared DB |
| (optionally |
| partitioned) |
+----------------+

Why It Matters Here

Service-based architecture is the answer to: "we need some independent deploy, but we are not ready for microservices." It buys you:

  • deploy independence per service
  • scale the hot service separately
  • team ownership per service

without paying for:

  • database-per-service migrations
  • eventual consistency across services
  • service discovery, circuit breakers, and saga complexity at microservices granularity
  • distributed tracing across 50+ services

It is underrated. A lot of teams who "went microservices" would have been better served here. This cluster discusses it before microservices deliberately: you should know this option exists and be able to defend it before you agree to a microservices plan.

Concrete Example

OrderFlow evolved from modular monolith to service-based:

  • 4 services: catalog, checkout, fulfillment, billing
  • 1 Postgres cluster with 4 schemas: catalog, checkout, fulfillment, billing
  • read access across schemas is allowed through reporting views; write access is only to your own schema
  • one React UI fronts all four, routed through an API gateway
  • each service has its own CI pipeline and can be deployed independently; deploys happen weekly per service

When the checkout team ships a change to pricing rules:

  1. PR against checkout, CI runs, merge, deploy only the checkout service.
  2. No new DB migration across schemas.
  3. Catalog, fulfillment, billing are untouched.
  4. The UI rolls forward independently.

When checkout needs to emit an order event that fulfillment consumes, they write to a checkout.order_events table and fulfillment reads it (or there is a small queue). They do not need full microservices plumbing yet.

A real-world resonance

This is what most successful "internal enterprise systems" look like before anyone names the architecture. Four to ten services, sharing an Oracle/SQL Server/Postgres, each owned by a team, each deploying its slice, joined by a common UI and common auth. The naming came later.

Common Confusion / Misconception

"If the database is shared, it's not really distributed." Distribution here is about code and deployment, not storage. You still get independent deployability, independent scaling of the services, and independent failure domains for the service processes. You give up independent schema evolution. That is a deliberate tradeoff to avoid the complexity of per-service datastores.

"Sharing a database is always bad." It is a tradeoff, not a sin. A shared DB means:

  • pro: atomic transactions across multiple services are possible
  • pro: reporting and analytics are trivial
  • pro: schema evolution is coordinated but visible
  • con: a schema change can impact multiple services
  • con: the DB is a shared failure mode

For systems that do not need full per-service autonomy, the pros dominate. Microservices.io and the modern microservices guidance rightly warn against shared databases for microservices. The warning does not mean shared databases are always wrong. In service-based architecture, it is the defining feature.

"Service-based is just microservices with fewer services." It is not only quantity; it is granularity and data isolation. Service-based explicitly keeps the DB shared and services coarse. Microservices explicitly isolate data and size services to bounded contexts.

"This is the same as a monolith split into two apps." A monolith split into two apps with no boundaries is just a two-app monolith. Service-based requires the services to own their logic and expose APIs; it is the modular-monolith boundary design (Concepts 4-6) extended across processes.

How To Use It

When to pick service-based:

Practical adoption of service-based from a modular monolith:

  1. Pick 3-6 modules that already have the cleanest boundaries (per Concept 5).
  2. Extract the first one into its own process, keeping the shared DB. The boundary is already enforced in code; now it is also enforced in deploy.
  3. Add an API gateway (or a thin BFF) in front.
  4. Keep the boundary rules from Concept 6 -- they still apply, across processes now.
  5. Observe per-service deploy cadence and per-service incident count. If independent deploy gives you the expected benefit, extract the next one. If not, you had a code-quality problem, not an architecture problem.

Check Yourself

  1. Service-based and microservices both have "service" in the name. Name three concrete differences in granularity, data, and operations.
  2. Why is service-based rated higher on simplicity than microservices by Richards and Ford?
  3. If someone proposes extracting one service out of your modular monolith, should you say yes? What question do you ask first?

Mini Drill or Application

For a modular monolith you know (or imagine):

  1. Draw the current modular monolith (5-10 modules).
  2. Propose a service-based decomposition (4-8 services). Group modules into services.
  3. List the shared-DB constraints: which joins / transactions would you want to preserve and why?
  4. Pick one service to extract first and defend the choice in two sentences.

Read This Only If Stuck