Master Microservices Architecture: Service Boundaries, Data Ownership, and Communication Patterns
Microservices Architecture: Service Boundaries, Data Ownership, and Communication Patterns
Microservices architecture decomposes applications into independently deployable services around business capabilities. This guide covers the three critical design decisions that determine success: defining boundaries, managing data ownership, and selecting communication patterns.
Service Boundaries
Service boundaries define where one service ends and another begins. Poor boundaries create distributed monoliths with tight coupling and high latency.
Bounded Contexts
Use Domain-Driven Design (DDD) to identify bounded contexts. A bounded context encapsulates a specific domain model with its own ubiquitous language.
- Each microservice maps to one bounded context
- Aggregates within a bounded context are natural service candidates
- Domain services (stateless operations across aggregates) may become separate services
Conway's Law
Service boundaries must align with team structure. If two teams own different parts of the same service, you have a boundary problem.
- One team per service (Two Pizza Team rule)
- Independent deployment cycles
- Separate code repositories
Validation Criteria
Validate boundaries against these technical requirements:
- Single responsibility per service
- No chatty inter-service calls (high coupling indicator)
- Independent deployability
- No shared deployment dependencies
- Data consistency boundaries grouped together
Start coarse-grained. Splitting services is easier than refactoring across existing boundaries.
Data Ownership
Distributed data management is the hardest problem in microservices. Each service owns its data exclusively.
Database-per-Service Pattern
Each service has its own private database. Other services access data only through that service's API.
# Anti-pattern: Shared database
OrderService → [Shared Database] ← PaymentService
# Correct pattern: Database-per-service
OrderService → [Orders DB]
PaymentService → [Payments DB]
Benefits:
- Independent schema evolution
- Technology diversity (Postgres for orders, MongoDB for payments)
- Clear ownership and accountability
Trade-offs:
- No ACID transactions across services
- Data duplication is required
- Complex query patterns across services
Eventual Consistency
Accept that data will be temporarily inconsistent. Use the Saga Pattern to manage distributed transactions.
Saga Pattern: Choreography vs Orchestration
Choreography: Services emit events and react to others' events.
// Order Service emits event
await eventBus.publish('OrderCreated', {
orderId: '12345',
amount: 99.99,
items: [...]
});
// Payment Service listens and reacts
eventBus.subscribe('OrderCreated', async (event) => {
const payment = await processPayment(event);
await eventBus.publish('PaymentProcessed', { orderId: event.orderId, paymentId: payment.id });
});
Orchestration: A central coordinator manages the transaction flow.
// Order Orchestrator
async function createOrder(orderData) {
const order = await orderService.create(orderData);
try {
const payment = await paymentService.process(order.id, order.amount);
await inventoryService.reserve(order.items);
await notificationService.sendConfirmation(order.id);
} catch (error) {
await compensatingActions(order.id);
throw error;
}
}
Compensating transactions undo completed steps when a saga fails.
Data Retrieval Patterns
API Composition
Client calls multiple services and aggregates results.
async function getOrderDetails(orderId) {
const [order, customer, items] = await Promise.all([
orderService.get(orderId),
customerService.get(orderId),
itemService.getByOrder(orderId)
]);
return { order, customer, items };
}
Use when: Low latency requirements, simple joins, real-time data needed.
CQRS (Command Query Responsibility Segregation)
Separate read models from write models. Update read models asynchronously via events.
// Write side: Command
async function placeOrder(command) {
const order = await orderRepository.save(command);
await eventBus.publish('OrderPlaced', order);
}
// Read side: Query handler
eventBus.subscribe('OrderPlaced', async (event) => {
await readModel.upsert({
orderId: event.id,
customerName: event.customerName,
totalAmount: event.total,
status: 'PLACED'
});
});
// Query optimized for reads
async function getOrderSummary(orderId) {
return readModel.findOne({ orderId });
}
Use when: Complex read queries, high read-to-write ratio, multiple read views needed.
Communication Patterns
Synchronous Communication
REST/gRPC for request-response patterns.
// gRPC service definition
service OrderService {
rpc CreateOrder (CreateOrderRequest) returns (OrderResponse);
rpc GetOrder (GetOrderRequest) returns (OrderResponse);
}
message CreateOrderRequest {
string customer_id = 1;
repeated OrderItem items = 2;
}
Use when:
- Immediate response required
- Simple request-response semantics
- Low-latency critical paths
Risks:
- Cascading failures
- Tight temporal coupling
- Blocking operations
Asynchronous Communication
Message brokers (RabbitMQ, Kafka) for event-driven architecture.
// Event schema with versioning
{
"eventType": "OrderShipped",
"eventId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-01-22T10:30:00Z",
"version": "2.0",
"data": {
"orderId": "12345",
"trackingNumber": "1Z999AA10123456784",
"carrier": "FedEx"
}
}
Critical requirements:
- Idempotency: Handle duplicate messages safely
- Ordering guarantees: Per-partition ordering in Kafka
- Dead letter queues: Handle failed messages
- Schema evolution: Backward-compatible event schemas
Resilience Patterns
Circuit Breaker
class CircuitBreaker {
constructor(threshold = 5, timeout = 60000) {
this.failureCount = 0;
this.threshold = threshold;
this.timeout = timeout;
this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
this.nextAttempt = 0;
}
async execute(fn) {
if (this.state === 'OPEN') {
if (Date.now() < this.nextAttempt) {
throw new Error('Circuit breaker is OPEN');
}
this.state = 'HALF_OPEN';
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
onSuccess() {
this.failureCount = 0;
this.state = 'CLOSED';
}
onFailure() {
this.failureCount++;
if (this.failureCount >= this.threshold) {
this.state = 'OPEN';
this.nextAttempt = Date.now() + this.timeout;
}
}
}
Service Discovery
Services locate each other dynamically without hardcoded URLs.
# Consul service registration
{
"ID": "order-service-1",
"Name": "order-service",
"Address": "10.0.1.5",
"Port": 8080,
"Check": {
"HTTP": "http://10.0.1.5:8080/health",
"Interval": "10s"
}
}
API Gateway
Single entry point that routes requests to appropriate services.
# Kong Gateway configuration
services:
- name: order-service
url: http://order-service:8080
routes:
- name: orders-route
service: order-service
paths:
- /api/v1/orders
Gateway responsibilities:
- Request routing
- Authentication and authorization
- Rate limiting
- Request/response transformation
- SSL termination
Getting Started
- Identify bounded contexts using domain modeling with business stakeholders
- Map aggregates to services - start with 5-10 services for most applications
- Implement database-per-service - no shared databases
- Choose communication pattern:
- Use synchronous for user-facing operations requiring immediate response
- Use asynchronous for background processing and cross-service consistency
- Implement resilience patterns - circuit breakers, retries, timeouts
- Set up observability - distributed tracing, metrics, logging
- Design for failure - assume network partitions and service unavailability
MatterAI builds frontier AI infrastructure for engineering teams — from inference-optimized models to autonomous coding agents and agentic code reviews.
Explore what we're building:
- Orbital IDE — Autonomous AI coding agent with background agents and deep codebase memory
- AI Code Reviews — Agentic pre-commit reviews across GitHub, GitLab, and Bitbucket
- Axon Models — Frontier-grade reasoning models at 70% lower inference cost
Share this Guide:
More Guides
Local LLMs in Your IDE: Connecting Ollama to Coding Agents and Autocomplete
Wire local models into VS Code, JetBrains, Cline, Continue, and Aider via the OpenAI-compatible API. Covers model routing, context budgets, tool calling with small models, and when a local model is the right choice for the job.
15 min readBuilding a Self-Hosted AI Stack: Ollama, Open WebUI, and Local RAG
Stand up a fully self-hosted AI stack on a single machine: Ollama for inference, Open WebUI as the chat interface, local embeddings for RAG, and a reverse proxy for secure access. No cloud dependency, no data leaving your network.
17 min readTop 5 Open-Source Coding Models to Run on Your Mac (2026)
The best local coding models for Apple Silicon in 2026, ranked by quality per gigabyte of unified memory. Covers qwen3-coder, devstral, gpt-oss, and more with real pull tags, sizes, and context windows.
14 min readRunning LLMs Locally: GGUF, Quantization, and Memory Planning
Learn the GGUF format, the quantization ladder from Q2 to FP16, and the exact memory math for running models on Apple Silicon and NVIDIA GPUs. Includes Ollama and llama.cpp tuning for KV cache and context.
15 min readOllama vs vLLM vs llama.cpp: Choosing the Right Local LLM Runtime
Compare the three dominant local LLM runtimes on architecture, throughput, hardware, and deployment context. Includes benchmark data, a decision framework, and a migration path from Ollama to vLLM.
16 min readContinue Reading
Local LLMs in Your IDE: Connecting Ollama to Coding Agents and Autocomplete
Wire local models into VS Code, JetBrains, Cline, Continue, and Aider via the OpenAI-compatible API. Covers model routing, context budgets, tool calling with small models, and when a local model is the right choice for the job.
15 min readBuilding a Self-Hosted AI Stack: Ollama, Open WebUI, and Local RAG
Stand up a fully self-hosted AI stack on a single machine: Ollama for inference, Open WebUI as the chat interface, local embeddings for RAG, and a reverse proxy for secure access. No cloud dependency, no data leaving your network.
17 min readTop 5 Open-Source Coding Models to Run on Your Mac (2026)
The best local coding models for Apple Silicon in 2026, ranked by quality per gigabyte of unified memory. Covers qwen3-coder, devstral, gpt-oss, and more with real pull tags, sizes, and context windows.
14 min readShip Faster. Ship Safer.
Join thousands of engineering teams using MatterAI to autonomously build, review, and deploy code with enterprise-grade precision.
