Microservices Revisited: Defining DDD Boundaries, Data Autonomy, Service Mesh and Observability

The article revisits microservice design by explaining how Domain‑Driven Design determines business boundaries, why each service must own its data, how Service Mesh and unified governance handle communication, and how logs, metrics and traces provide observability for distributed systems.

YiSu Grain
YiSu Grain
YiSu Grain
Microservices Revisited: Defining DDD Boundaries, Data Autonomy, Service Mesh and Observability

Microservice second pass

After learning the three first‑pass principles (split by business capability, independent deployment, independent scaling), the second pass asks:

How to define business boundaries?

Who owns the data after services are split?

How to manage communication, fault handling and logging across dozens of services?

Monolithic retail example

A chain‑retail system originally had a single monolith with modules:

Product Management;
Pricing & Promotion;
Inventory Management;
Order Processing;
Member Points;
Payment Settlement.

All modules shared one database. The first migration created a service per table:

Product Table Service;
Inventory Table Service;
Order Table Service;
Order Detail Table Service;
Member Table Service.

Resulting problems:

Creating an order required calls to more than a dozen services.

Order service directly modified the inventory database.

Marketing service also updated the member‑points table.

Changing a product field forced notifications to multiple teams.

A timeout in any service blocked the whole order flow.

Each team duplicated retry, circuit‑breaker and logging code.

This moved the original database coupling onto the network instead of solving it.

Knowledge shelf for Day 33

DDD and Bounded Context

Reasonable microservice boundaries

Data autonomy and storage isolation

Cross‑service queries and distributed transactions

Microservice distributed governance

Service Mesh data plane and control plane

Logs, metrics and tracing

Roles of API Gateway, Service Registry, Kubernetes and Service Mesh

01. What we learn in the second pass

First pass only tells *what* microservices are. The second pass must answer concrete design questions such as:

Should order and inventory be in the same service?

Why are product and promotion both called "Product"?

Can services still share a database?

How to query data that spans multiple services?

How to keep order, inventory and payment consistent?

Who implements retry, circuit‑breaker, gray release and tracing?

02. DDD is about understanding business, not drawing tables

Domain‑Driven Design (DDD) starts from business questions:

What business does the enterprise actually run?

Which terminology do business users use?

Why do specific business rules change?

Which models must stay consistent?

Which capabilities should be owned by the same team?

For the retail system:

Domain : chain retail.

Sub‑domains :

Product;
Pricing & Promotion;
Inventory;
Order;
Member;
Settlement.

Bounded Context defines a clear boundary where a set of terms and domain models have consistent meaning. The same word can mean different things in different contexts.

Bounded Context is not a JSON data structure; JSON only describes how data is written.

Example rule in the inventory context

"available quantity cannot be less than zero" belongs to the inventory bounded context, regardless of whether it is expressed as JSON, a Java class or a table.

03. Why the same "Product" can have four different models

Product Catalog : name, brand, category, images, specifications.

Pricing & Promotion : original price, promotional price, discount rules, effective time.

Inventory : SKU, warehouse, total quantity, locked quantity, available quantity.

Order : snapshot of name, price and quantity at purchase time.

Because the product name or price may change later, the order stores its own snapshot to keep historical accuracy.

Real order example (phone priced ¥5,999)

Product catalog returns SKU, name, brand, category, colour, capacity, images.

Pricing service calculates original price ¥6,499 and promotional price ¥5,999.

Order service asks inventory service to reserve one unit. Inventory before reservation: total 10, locked 2, available 8.

After reservation: total 10, locked 3, available 7.

Order service saves a snapshot: product name, price ¥5,999, quantity 1, status "awaiting payment".

When payment succeeds, payment service publishes a "payment succeeded" event; order becomes "paid" and inventory reduces total stock and releases the lock.

If a unified "Product" model were shared, any schema change would force all teams to upgrade, test and redeploy, creating tight coupling.

04. Bounded Context is not exactly one microservice

One bounded context does not equal one microservice.

A large context can be split into several services based on team size, deployment needs and scaling requirements. Early in a project, tightly related capabilities may be combined into a "small service" to avoid over‑splitting.

Five questions to decide whether two functions belong to the same boundary:

Is the business responsibility highly cohesive?

Do the terminology and rules stay consistent?

Can the same team own the data?

Are change and release cycles similar?

Is independent scaling really needed?

05. Data autonomy: service owns both code and data

Data is a private asset of a microservice; other services must access it via the owning service’s API or events, not by bypassing the service.

Example with the inventory service:

Defines the inventory model.

Enforces inventory business rules.

Decides how data is stored and evolved.

Guarantees that available quantity never becomes negative.

Prevents other services from directly modifying the inventory tables.

When the order service needs to reserve stock it should call a method such as reserveStock(skuId, quantity) instead of executing raw SQL.

Logical ownership can be realized by:

Different database instances.

Separate databases on the same server.

Separate schemas within one database.

Different storage technologies.

Schema example (logical namespace):

Database Server
└── retail_db
   ├── catalog_schema
   │   ├── product table
   │   └── category table
   ├── inventory_schema
   │   ├── stock table
   │   └── stock_record table
   └── orders_schema
       ├── order table
       └── order_item table

06. Cross‑service queries – no secret JOINs

When a user views order details, data lives in different services (order, logistics, member, product). Direct JOINs across databases are prohibited.

Two main approaches after data isolation:

API aggregation – call multiple services at query time and compose the result.

Asynchronous replication – services publish domain events; a read‑model service maintains a local view for fast queries.

API aggregation example: GET /order-details/1001 Aggregation service calls in parallel:

Order service – order status;
Product service – name and image;
Logistics service – current status;
Member service – membership level.

Combined JSON result:

{
  "orderId": "1001",
  "orderStatus": "shipped",
  "productName": "Phone Black 256GB",
  "productImage": "phone.jpg",
  "logisticsStatus": "in delivery",
  "memberLevel": "gold"
}

Typical latencies: order 50 ms, product 80 ms, logistics 150 ms, member 60 ms. Overall response is limited by the slowest call.

Implementation must also set timeout, retry (only for idempotent failures), circuit‑breaker, degradation (e.g., still return core order data if member service fails) and caching for rarely‑changed data.

Asynchronous replication example:

OrderCreated → Kafka → Order‑detail‑view service updates its local table.

Query becomes a simple local SQL on order_detail_view, providing fast reads at the cost of eventual consistency.

Idempotence is essential for asynchronous replication: consumers must handle duplicate events without side effects.

07. Distributed transactions after data isolation

Creating an order now involves multiple services (order creation, inventory reservation, payment, points). Each service can only reliably commit its own local transaction. The former single‑database transaction is split across services, requiring a strategy for consistency.

2PC / XA : strong consistency, high performance and availability cost; suitable when participants are controllable.

TCC : Try‑Confirm‑Cancel, strong control; suitable for core transactions but high development effort.

Saga : series of local transactions with compensations; fits long‑running cross‑service transactions.

Reliable Message : event‑driven eventual consistency; business can tolerate short inconsistency.

Outbox : business data and pending events stored in the same local transaction; prevents data commit without message loss.

All approaches need supporting mechanisms: idempotence, deduplication, compensation, timeout, retry and possibly manual reconciliation.

08. Governance becomes mandatory as services multiply

A single monolith call ( orderService.createOrder()) becomes a chain of network operations: service discovery, instance selection, serialization, network latency, timeout handling, retries, circuit‑breaker, etc. Governance must answer:

Where is the target service?

Which instance should receive the request?

What is the timeout threshold?

How to handle transient failures?

How to isolate persistent failures?

How to limit traffic, perform gray releases, enforce security, and collect observability data?

These concerns together constitute distributed governance .

09. Service Mesh – moving communication governance out of business code

Without a Service Mesh, each service must implement service discovery, load balancing, timeout, retry, circuit‑breaker, metrics, authentication and encryption – often duplicated across languages.

With a Service Mesh, a sidecar proxy is deployed next to every service. The proxy intercepts all inbound and outbound traffic and handles:

Routing and load balancing

Timeouts, retries and circuit‑breakers

mTLS authentication and authorization

Metrics and tracing collection

Policy enforcement from a central control plane

Data plane proxies forward actual requests; the control plane distributes configuration, certificates and policies.

Service Mesh architecture
Service Mesh architecture

Service Mesh is part of the broader cloud‑native ecosystem but not mandatory. It excels when there are many services, multiple programming languages, and a need for uniform policies.

10. Service Mesh vs ESB

Main object : ESB integrates heterogeneous enterprise systems; Service Mesh governs communication between microservices.

Main capabilities : ESB provides protocol conversion, message transformation, routing and workflow orchestration; Service Mesh provides traffic governance, security, observability and resilience.

Business processing : ESB often carries substantial transformation and orchestration logic; Service Mesh principally does not carry domain business logic.

Typical structure : ESB is a centralized bus; Service Mesh is a distributed set of sidecar proxies forming a data plane.

11. Observability – answering “Why did it fail?”

A single order may travel through:

Gateway → Order → Inventory → Payment → Kafka → Points.

If a user reports "payment succeeded but the page returned after 8 seconds", looking at one machine’s CPU is insufficient.

Logs: what event occurred, error details, involved order, surrounding context.
Metrics: QPS trends, P95 latency, error rate, connection pool usage, message backlog, order success rate.
Traces: which services were called, per‑hop latency, where the error originated, dependency graph.

Example trace (milliseconds):

Gateway 20 ms
Order service 80 ms
Inventory service 120 ms
Payment service 7200 ms
Write message 30 ms

The payment service is the bottleneck. Checking its logs and metrics reveals a third‑party payment timeout and a saturated connection pool, explaining the delay.

Monitoring sets thresholds and alerts for known metrics; observability combines logs, metrics and traces to answer "why", "where" and "who" were affected.

12. Distinguishing API Gateway, Service Registry, Kubernetes and Service Mesh

API Gateway : external client → system; provides unified entry, routing, authentication, rate‑limiting, protocol adaptation.

Service Registry : service instance directory; handles registration, discovery and instance change awareness.

Kubernetes : containers & compute resources; handles scheduling, deployment, scaling, self‑healing, lifecycle.

Service Mesh : service‑to‑service communication; provides traffic governance, security, observability and resilience.

Message Queue : asynchronous messaging; provides decoupling, peak‑shaving, broadcast and eventual consistency.

All can coexist: external traffic enters via the API gateway, internal calls are governed by the Service Mesh, services discover each other through the registry, and Kubernetes runs the containers.

Component diagram
Component diagram

13. Complete architecture proposal for the retail system

Use DDD to identify sub‑domains (product catalog, pricing, inventory, order, member, settlement) and bounded contexts, then define service boundaries that keep high cohesion and low coupling.

Enforce data storage isolation: each service owns its data model and exposes only APIs or domain events. Physical isolation can be separate DB instances, separate databases, or separate schemas.

Handle cross‑service queries: simple cases with API aggregation; high‑throughput or complex cases with event‑driven read models (CQRS).

Design order creation, inventory reservation, payment and points as a series of local transactions. Choose TCC, Saga or reliable‑message patterns based on consistency needs, and implement outbox, idempotence, deduplication and compensation.

Shorten synchronous call chains: keep inventory reservation synchronous, move notifications, SMS, points to asynchronous messages; configure timeout, limited retry, back‑off, circuit‑breaker, bulkhead, rate‑limiting and degradation.

Adopt a unified governance framework or Service Mesh so that service discovery, load balancing, timeout, retry, circuit‑breaker, rate‑limiting and metrics are centrally managed, freeing business code from boilerplate.

Build an integrated observability platform (logs, metrics, tracing) with a shared Trace‑ID across gateway and services, correlate latency, error rates, slow queries and message backlog, set SLO‑based alerts and locate root causes quickly.

Containerise services, use CI/CD pipelines, configuration centre, service registry, Kubernetes for scheduling, health checks, auto‑scaling, canary releases and fast rollback to support independent team releases and elastic scaling during promotions.

Key takeaway

Microservices first use business boundaries to split, then data autonomy to keep services independent; the resulting distributed complexity must be controlled with governance, Service Mesh and a solid observability stack.
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

microservicesobservabilityservice meshDDDgovernancedistributed transactionsdata autonomy
YiSu Grain
Written by

YiSu Grain

A fleeting mayfly in the world, a single grain in the boundless sea.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.