ArchitectMind
  1. Home
  2. Tutorials
  3. Book 7 · Application Architecture & Distributed Systems
  4. Volume V
  5. What Makes a Microservice?
Book 7 · Volume V — Microservices Architecture · Topic 1.1

What Makes a Microservice?

Business capability, autonomous deployment and data ownership

13 min readStage 3 · Systems8 interview questions
Chapter
Chapter 1 — Microservices Fundamentals
Prerequisites
Volumes II–IV (styles, boundaries, distributed systems)
You will be able to
Define a microservice by its essential properties, map business capabilities to services, explain and test autonomous deployability, enforce data ownership, distinguish microservices from modules and "services that happen to be small", and evaluate whether an existing service delivers microservice benefits
Covers
Business capability; Autonomous deployment; Data ownership
On this page
  1. 1. Concept / Definition
  2. 2. Why It Exists
  3. 3. Core Concepts
  4. 4. How It Works Internally — Microservice Anatomy
  5. 4A. Deep Dive — Testing Whether It's a Microservice
  6. 5. APIs / Features / Syntax
  7. 6. Practical Examples
  8. 7. Common Problems & Mistakes
  9. 8. Performance & Production Considerations
  10. 9. Architecture & Design Considerations
  11. 10. Interview Questions & Answers

1Concept / Definition

A microservice is an independently deployable service that implements one business capability, is deployed autonomously through its own pipeline, and owns its data exclusively — exposing it only through well-defined APIs and events. Size is a consequence of these properties, not the definition.

What makes a microservice? (TOC)
Figure 1 — What makes a microservice? (TOC)
TOC propertyDefinition
Business capabilityThe service implements something the business does (Payments, Catalog, Fulfilment) end-to-end — API, logic and data — rather than a technical layer (UI, data access)
Autonomous deploymentThe service can be built, tested and released to production on its own schedule, without coordinating a release with other services, while existing consumers keep working
Data ownershipThe service is the only one that reads and writes its persistent data directly; other services access it via the service's API or events, never through the database
Table 1 — The three TOC properties

2Why It Exists

Large monoliths slowed organisations: many teams shared one codebase, one release train and one database, so every change needed coordination. Microservices trade in-process simplicity for team autonomy, targeted scaling and fault isolation.

Monolith module vs microservice
Figure 2 — Monolith module vs microservice

3Core Concepts

3.1 Business capability

Business capability → service
Figure 3 — Business capability → service
Good capability servicePoor "service"
payment-svc (authorise, capture, refund)db-access-svc
catalog-svc (products, categories, media)validation-svc
fulfilment-svc (pick, pack, ship)common-utils-svc
pricing-svc (price rules, promotions)customer-table-svc (CRUD only)
Table 2 — Capabilities vs technical slices
JAVA
// A capability-shaped API: business operations, not table CRUD
@RestController
@RequestMapping("/payments")
class PaymentController {
    @PostMapping("/{orderId}/authorisation")
    Authorisation authorise(@PathVariable String orderId,
                            @RequestBody AuthoriseRequest r) {
        return payments.authorise(orderId, r.amount(), r.method());
    }
    @PostMapping("/{orderId}/capture")
    Capture capture(@PathVariable String orderId) { return payments.capture(orderId); }
    @PostMapping("/{orderId}/refunds")
    Refund refund(@PathVariable String orderId, @RequestBody RefundRequest r) {
        return payments.refund(orderId, r.amount(), r.reason());
    }
}

3.2 Autonomous deployment

Autonomous deployment (TOC)
Figure 4 — Autonomous deployment (TOC)
EnablerWhy it matters
Own repository & pipelineNo shared build to wait for
Backward-compatible APIsConsumers unaffected by release
Consumer-driven contract testsProve compatibility before deploy
Runtime independenceStarts even if dependencies are down
Feature flagsDeploy ≠ release
Table 3 — What makes deployment autonomous
YAML
# Per-service pipeline (GitHub Actions excerpt)
on: { push: { paths: ["services/payment-svc/**"] } }
jobs:
  build:
    steps:
      - run: ./mvnw -pl services/payment-svc verify  # unit + contract provider tests
      - run: ./mvnw -pl services/payment-svc spring-boot:build-image
      - run: argocd app set payment-svc --kustomize-image payment-svc:${{ github.sha }}
Deployment frequency (illustrative)
Figure 5 — Deployment frequency (illustrative)

3.3 Data ownership

Data ownership (TOC)
Figure 6 — Data ownership (TOC)
Ownership ruleEnforcement
One writer per datasetSeparate DB/schema per service
No direct reads by othersDB credentials only for owner
Share via API/eventsOpenAPI, AsyncAPI contracts
Copies are read-onlyReplicas fed by events (Chapter 4)
Table 4 — Enforcing ownership
SQL
-- Schema-per-service with least privilege (PostgreSQL)
CREATE SCHEMA payments AUTHORIZATION payment_svc;
REVOKE ALL ON SCHEMA payments FROM PUBLIC;
-- no GRANTs to order_svc, report_svc, ... — they must use the API or events

4How It Works Internally — Microservice Anatomy

Layer inside one serviceContents
APIREST/gRPC endpoints, event publishers/consumers
DomainAggregates, rules, use cases
PersistencePrivate datastore, outbox
PlatformConfig, health, metrics, tracing
DeliveryDockerfile/buildpack, Helm/Kustomize, pipeline
Table 5 — Everything needed to run the capability
Microservice benefitDepends on
Independent changeCapability boundary + contracts
Independent deployPipeline + compatibility
Independent scalingOwn runtime
Fault isolationOwn runtime + resilience
Technology choiceOwn data + API boundary
Table 6 — Benefits and their preconditions

4ADeep Dive — Testing Whether It's a Microservice

Is it really a microservice? (checklist)
Figure 7 — Is it really a microservice? (checklist)
Metric (DORA-style)Healthy microservice signal
Deployment frequencyMany per week per service
Lead time for changeHours to a day
Change failure rateLow, isolated to one service
Coordinated releasesRare to none
Cross-team PRs per changeNear zero
Table 7 — Measure autonomy, don't assume it
JAVA
// Consumer-driven contract (Spring Cloud Contract) — provider verifies before deploy
Contract.make {
    request {
        method POST()
        url "/payments/o-42/authorisation"
        body(amount: 99900, method: "UPI")
        headers { contentType applicationJson() }
    }
    response {
        status 201
        body(status: "AUTHORISED")
        headers { contentType applicationJson() }
    }
}

5APIs / Features / Syntax

NeedTooling
Service runtimeSpring Boot 4, Java 25, virtual threads
ContractsOpenAPI 3.1, AsyncAPI 3, Pact/Spring Cloud Contract
DeliveryBuildpacks, Argo CD, GitHub Actions
DataPrivate PostgreSQL/MongoDB/Redis, outbox
PlatformKubernetes, Actuator, Micrometer/OTel
Table 8 — Toolkit

6Practical Examples

6.1 Layered "microservices"

A bank split its app into ui-svc, logic-svc and data-svc. Every feature changed all three and needed a joint release. Re-cutting by capability (accounts, cards, payments) let teams release independently.

6.2 The shared-database "microservices"

Twelve services shared one Oracle schema; a column change required a 6-team release train. Moving each capability's tables to its own schema with APIs and events restored autonomy over 9 months.

6.3 Worked example — capability naming

NameVerdict
payment-svc✔ capability
payment-db-svc✘ layer
Table 9 — Name by business

Situation. A new team proposed three services for ShopNow payments: payment-api-svc, payment-logic-svc and payment-db-svc. In review, the architect asked what each one does for the business. Only the combined answer, "takes payments", made sense, so the three became one payment-svc that owns the API, the rules and the data.

Why it matters. A name is a quick test of a boundary. If it describes a business activity (payment, catalog, fulfilment), the service probably owns a capability. If it describes a technical layer (db, api, utils), every feature will cut across several services and their teams.

6.4 Worked example — deploy independence test

QuestionAnswer needed
Can I deploy payment-svc at 3 pm Tuesday alone?Yes
Table 10 — Simple litmus test

Situation. The question is deliberately concrete: can the payment team ship a change at 3 pm on a Tuesday, alone, without telling anyone? If the honest answer is "only if order-svc deploys at the same time" or "after the Thursday release meeting", payment-svc is not autonomously deployable, whatever its size.

Why it matters. Independent deployment is the property that delivers most microservice benefits, and it is easy to lose quietly. Asking this question at each design review, and checking the deployment history, catches hidden coupling before it turns into a release train.

6.5 Worked example — backward-compatible change

ChangeCompatible?
Add optional response field✔
Rename field✘ (add new, deprecate old)
Table 11 — Protect consumers

Situation. Mobile apps read the status field of the payment response. Adding an optional field such as authorisationCode breaks nobody, because clients ignore fields they do not know. Renaming status to paymentStatus would break every client still in app stores, so the team adds paymentStatus, keeps status, and removes it only after usage metrics show it is no longer read.

Why it matters. Consumers upgrade on their own schedule, and some, such as installed mobile apps, never upgrade at all. Backward-compatible change is what lets a provider deploy without coordinating with them. Contract tests turn this rule into a pipeline check rather than a reviewer's memory.

6.6 Worked example — data access audit

TableAccessed by
payments.paymentpayment-svc only ✔
orders.orderorder-svc, report-svc ✘
Table 12 — Find violations

Situation. A query of the database's access logs over 30 days showed that payments.payment is read and written only by payment-svc, as intended. The orders.order table, however, is also queried by report-svc using a shared read-only login that someone created for a one-off report two years earlier.

Why it matters. That one extra reader means order-svc can no longer change its schema freely: any column rename could break finance reports. Audits like this find coupling that architecture diagrams do not show. The fix is to remove the shared credentials and give report-svc an event feed or an API instead.

6.7 Worked example — reporting without DB access

NeedApproach
Finance reportsEvents → warehouse
Table 13 — Respect ownership

Situation. Finance needs daily revenue reports that join orders, payments and refunds. Instead of querying three service databases, each service publishes events such as OrderPlaced, PaymentCaptured and RefundIssued. A pipeline loads them into the data warehouse, where analysts can join them freely.

Why it matters. Reporting is the most common reason ownership gets broken, because direct database access looks quicker. Events keep each service free to change its internal schema, and the warehouse gives analysts a stable, documented model built for queries instead of one shaped by transactional needs.

6.8 Worked example — startup independence

Dependency down at startupBehaviour
pricing-svcpayment-svc still starts
Table 14 — No boot-time coupling

Situation. During an incident pricing-svc was down, and Kubernetes rescheduled payment-svc pods onto new nodes. The new pods started normally because payment-svc does not call pricing-svc during startup; it reaches pricing only when a request needs it, behind a timeout and circuit breaker.

Why it matters. If a service refuses to start until its dependencies respond, one outage spreads through restarts and deployments. Avoid calling other services in startup code, keep readiness checks limited to the service's own resources, and handle unavailable dependencies at request time.

6.9 Worked example — feature flags

DeployRelease
Code shipped darkFlag enabled later
Table 15 — Decouple deploy from release

Situation. The new UPI payment flow was merged and deployed to production on Monday behind a disabled flag, so no customer saw it. On Wednesday the flag was enabled for 5% of users and then for everyone on Friday. When an issue appeared, the team turned the flag off in seconds without a rollback.

Why it matters. Feature flags separate deploying code from releasing a feature. Teams can deploy small changes many times a day, while product owners choose when users see them. Remove flags once a feature is fully released, or they become permanent clutter.

6.10 Worked example — team mapping

ServiceTeam
payment-svcTeam Money
Table 16 — One owner

Situation. payment-svc has exactly one owner, Team Money, which builds it, deploys it, carries the pager for it and decides its roadmap. Other teams that need a change open a request or contribute a pull request, but Team Money reviews and ships it.

Why it matters. Shared ownership usually means no ownership: nobody feels responsible for incidents, technical debt or API quality. One owning team per service, as in Team Topologies' stream-aligned teams, keeps decisions fast and accountability clear. A team can own several services, but a service should have only one team.

6.11 Worked example — scaling independently

ServiceReplicas at sale
catalog-svc30
payment-svc8
Table 17 — Scale what's hot

Situation. During ShopNow's annual sale, product browsing rises tenfold while the number of payments rises about threefold. The Kubernetes autoscalers scale catalog-svc to 30 replicas and payment-svc to 8, each according to its own CPU and request metrics.

Why it matters. In a monolith every module scales together, so you pay for capacity that most of the code never uses. Separate services let you scale only the hot capability. The benefit is real only if each service has its own runtime and its own data store that can handle the extra load.

6.12 Worked example — fault isolation

FailureImpact
recommendation-svc downPage renders without recs
Table 18 — Graceful degradation

Situation. recommendation-svc failed during peak traffic. The product page called it with a 200 ms timeout behind a circuit breaker, so the page rendered without the "You may also like" section and customers could still browse and buy.

Why it matters. Separate processes only isolate faults if callers are designed to cope with failure. Timeouts, circuit breakers and fallbacks turn a dependency outage into a degraded feature instead of a site-wide incident. Decide in advance which features are essential and which can be dropped.

6.13 Worked example — technology choice

ServiceStore
search-svcOpenSearch
order-svcPostgreSQL
Table 19 — Own data enables choice

Situation. search-svc needs full-text search, typo tolerance and relevance ranking, so it stores product data in OpenSearch. order-svc needs transactions and strong consistency, so it uses PostgreSQL. Each team chose the store that fits its capability without affecting the other.

Why it matters. Choosing the right technology per service is possible only because each service owns its data behind an API. Use this freedom carefully: every new technology adds operational work, so most organisations keep a short list of supported stores and require a reason to go outside it.

6.14 Worked example — shared library risk

LibraryRisk
shopnow-domain.jar with entitiesForces lockstep upgrades
Table 20 — Share utilities, not domain models

Situation. ShopNow once shared a shopnow-domain.jar containing Order, Customer and Product entities across 15 services. Adding one field to Order required rebuilding and redeploying all 15 services at the same time, which brought back the release train that microservices were meant to remove.

Why it matters. Shared domain models couple services at compile time. Share technical utilities instead, such as logging, tracing and security helpers, and let each service define its own model of the data it receives. Duplicating a few classes is cheaper than coordinating 15 releases.

6.15 Worked example — API versioning

VersionSupport
v16 months after v2
Table 21 — Consumers migrate on their schedule

Situation. payment-svc released v2 of its API with a new error format. v1 stayed available for six months, its usage was tracked, and consumers were reminded as the deadline approached. v1 was switched off only when traffic to it reached zero.

Why it matters. Versioning lets a provider make breaking changes without forcing every consumer to upgrade on the same day. A published support window, usage metrics and a clear deprecation process turn versioning from a promise into something consumers can plan around. Keep the number of live versions small, usually two.

6.16 Worked example — ownership in platform

ArtifactOwner field
Backstage catalog-info.yamlspec.owner: team-money
Table 22 — Discoverable ownership

Situation. Every ShopNow service has a catalog-info.yaml in its repository with spec.owner set to a team, such as team-money for payment-svc. Backstage reads these files and shows the owner, on-call rota, APIs and dashboards on one page per service.

Why it matters. When an incident starts at 2 am, nobody should have to guess who owns a failing service. Recording ownership in code keeps it up to date with the service itself, and the platform can block deployments of services without an owner.

6.17 Worked example — cost of a service

OverheadPer service
Pipeline, dashboards, on-call, DBReal and recurring
Table 23 — Microservices aren't free

Situation. Before splitting a new service, ShopNow lists what it adds: a repository and pipeline, a database, dashboards and alerts, an on-call rota, security patching and contract tests. For a small team that can mean several days of setup and a few hours of upkeep every week, per service.

Why it matters. Microservices trade code complexity for operational complexity. The overhead is worthwhile when it buys real team autonomy, scaling or isolation, and wasteful when it does not. A platform team with golden-path templates lowers the cost but never removes it.

6.18 Worked example — when not to split

SituationChoice
One small team, early productModular monolith
Table 24 — Autonomy must pay off

Situation. A start-up with six developers and an unproven product wanted to begin with twelve microservices. The architect recommended a modular monolith with clear module boundaries and a separate schema per module, deployed as one application.

Why it matters. With one small team, independent deployment has little value because the same people would deploy everything anyway, while the operational cost arrives on day one. Strong module boundaries keep the option open: when a module needs its own team, scaling or release cadence, it can be extracted cleanly.

6.19 Worked example — ADR

DecisionRationale
payment-svc separatePCI scope, own team, independent releases
Table 25 — Recorded

Situation. The decision to run payment-svc as a separate service was recorded in an architecture decision record. It gives three reasons: limiting PCI DSS audit scope to one service, giving Team Money full ownership, and allowing payment fixes to ship without waiting for other releases.

Why it matters. An ADR captures why a boundary exists, not just that it exists. When someone later proposes merging or splitting services, the team can check whether those reasons still hold, instead of relying on memory or guesswork.

6.20 Worked example — review checklist

Check✔
One capability✔
Own pipeline, deploy alone✔
Private data✔
One owning team✔
Table 26 — Microservice review

How to use it. Run this four-item check whenever a new service is proposed and once a year for existing ones. Each item maps to an earlier example: one capability (6.3), deployable alone (6.4, 6.8), private data (6.6) and one owning team (6.10). A service that fails two or more checks is a distributed monolith in practice, and should either be redesigned or merged back.

6.21 Worked example — interview answer

PropertyAnswer
CapabilityBusiness-aligned scope
DeploymentIndependent release
DataExclusive ownership
Table 27 — Structured answer

How to use it. In an interview, answer "what makes a microservice?" with these three properties in this order, then add one sentence on why: together they let one team change, deploy and scale a capability without coordinating with others. Then name the cost, such as operational overhead and distributed data, and say when you would choose a modular monolith instead. Size alone should never be your definition.

7Common Problems & Mistakes

  • Layer-based services.
  • Shared databases.
  • Lockstep releases.
  • Shared domain libraries.
  • No single owning team.
  • Splitting too early without autonomy benefits.

8Performance & Production Considerations

AspectGuideline
DeliveryPipeline per service
CompatibilityContract tests, versioning
DataEnforced by credentials
OperationsPer-service dashboards, on-call
CostBudget overhead per service
Table 28 — Operational guidelines

9Architecture & Design Considerations

  • Define services by capability, not layer or size.
  • Make independent deployment a tested property.
  • Enforce data ownership technically.
  • Prefer a modular monolith until autonomy pays.

10Interview Questions & Answers

Q1Basic

What is a microservice?

An independently deployable service implementing one business capability and owning its data. Its boundary follows a business capability rather than a technical layer, it has its own pipeline so it can be released without coordinating with other services, and no other service touches its database. Small size is a side effect of these properties, not the definition. Together they let one team change, deploy and scale the capability on its own.

Q2Basic

What is a business capability?

Something the business does, like payments or fulfilment. It is defined by what the business does, not by how the software is built, so it stays stable when technology changes. Payments, for example, includes authorising, capturing and refunding. Aligning a service with a capability means most feature changes touch one service and one team rather than several.

Q3Intermediate

Why must services own their data?

Shared databases couple schemas, deployments and failures across services. If two services read the same tables, the owner cannot change its schema without checking with the other, so deployments become coordinated again. A shared database also spreads failures and load: a slow query from one service can affect all of them. Exclusive ownership, with data shared through APIs and events, keeps each service free to change.

Q4Intermediate

How do you enable autonomous deployment?

Separate pipelines, backward-compatible APIs, contract tests and no startup coupling. Give each service its own repository or path-scoped pipeline, keep API changes backward compatible, and verify them with consumer-driven contract tests before deploying. Make sure a service starts without calling its dependencies, and use feature flags so that deploying code does not mean releasing it. Then track deployment history to confirm services really ship alone.

Q5Senior Engineer

Why are layer-based services a problem?

Every feature changes several services and teams, forcing coordinated releases. Layers such as UI, logic and data are cut across every feature, so a single change touches all of them and requires their teams to release together. You pay the cost of distribution (network calls, separate deployments) without gaining autonomy. Splitting by business capability keeps a typical change inside one service.

Q6Senior Engineer

How do you measure whether services are autonomous?

Deployment frequency, lead time and the number of coordinated or cross-team changes. Use DORA-style metrics per service: deployment frequency, lead time for changes and change failure rate. Add autonomy signals such as how many releases needed coordination and how many changes required pull requests in other teams' repositories. High deployment frequency with near-zero coordinated releases shows the architecture is delivering independence.

Q7Technical Architect

What should a shared library contain in a microservice system?

Technical utilities, not domain models that force lockstep upgrades. Shared libraries should hold cross-cutting technical code such as logging, tracing, security and HTTP client configuration, ideally versioned so services upgrade at their own pace. Domain entities like Order or Customer should not be shared, because a change to one forces every consumer to rebuild and redeploy together.

Q8Solution Architect

When would you advise against microservices?

When a small team or early product doesn't need independent deployment and the operational cost outweighs benefits — a modular monolith is better. Also when domain boundaries are still unclear, because wrong service boundaries are expensive to move, and when the organisation lacks automated delivery, observability and on-call maturity. Start with a modular monolith with strict module boundaries, and extract a service when a specific need appears, such as independent scaling, a separate team or a compliance boundary.

Newsletter

Get new tutorials every week

New ArchitectMind tutorials and interview questions, once a week. No spam, unsubscribe anytime.

Almost done! Check your inbox and click the confirmation link to complete your subscription.
Keep going in Book 7

Application Architecture & Distributed Systems

Volume I · Topic 1.1

Software Architecture

Architecture Fundamentals & Architectural Thinking

Volume I · Topic 1.2

Architecture Responsibilities

Architecture Fundamentals & Architectural Thinking

Volume I · Topic 1.3

Architect vs Developer

Architecture Fundamentals & Architectural Thinking

Volume II · Topic 1.1

Monolith Fundamentals

Application Architecture Styles & Patterns

Volume II · Topic 1.2

Types of Monoliths

Application Architecture Styles & Patterns

Volume II · Topic 1.3

Monolith Advantages

Application Architecture Styles & Patterns

Volume III · Topic 1.1

Domain-Driven Design

Domain-Driven Design & Business Boundaries

Volume III · Topic 1.2

Strategic vs Tactical DDD

Domain-Driven Design & Business Boundaries

Volume III · Topic 1.3

Domain Complexity

Domain-Driven Design & Business Boundaries

Volume IV · Topic 1.1

What Is a Distributed System?

Distributed Systems Fundamentals

Volume IV · Topic 1.2

Why Distributed Systems Exist

Distributed Systems Fundamentals

Volume IV · Topic 1.3

Distributed System Complexity

Distributed Systems Fundamentals

Volume V · Topic 1.2

Service Granularity

Microservices Architecture

Volume V · Topic 1.3

Microservice Boundaries

Microservices Architecture

Volume VI · Topic 1.1

Event Fundamentals

Event-Driven & Asynchronous Architecture

Volume VI · Topic 1.2

Event Types

Event-Driven & Asynchronous Architecture

Volume VI · Topic 1.3

Event-Carried State

Event-Driven & Asynchronous Architecture

Volume VII · Topic 1.1

Scalability

Resilience, Scalability & Distributed Data

Volume VII · Topic 1.2

Stateless Architecture

Resilience, Scalability & Distributed Data

Volume VII · Topic 1.3

Load Balancing

Resilience, Scalability & Distributed Data

Volume VIII · Topic 1.1

ADR

Architecture Decision-Making & System Evolution

Volume VIII · Topic 1.2

Architecture Decision Process

Architecture Decision-Making & System Evolution

Volume VIII · Topic 1.3

Reversible vs Irreversible Decisions

Architecture Decision-Making & System Evolution