API, Message, and Data Ownership Contracts
Goal
After choosing boundaries and a runtime model, the next challenge is communication. Microservices need to collaborate, but every collaboration can either preserve independence or quietly rebuild the coupling the team was trying to escape.
This section connects API contracts, durable messages, events, read models, and service-owned data. These choices decide whether services are genuinely independent or only separated by network calls.
The durable idea is:
Service independence depends on explicit contracts for calls, messages, and data.
The Short Version
The easy question is:
Should services call each other or send messages?
The better architecture question is:
What kind of dependency are we creating, and who owns the result when it fails?
Microservices usually use a mix of communication styles:
| Communication Style | Good Fit | Main Risk |
|---|---|---|
| Synchronous API | Immediate answer needed, such as authorization, quote, or status lookup. | Long call chains can make one slow service slow down many others. |
| Durable command message | Work must be processed reliably, such as reserve inventory or start fulfillment. | Retries, ordering, duplicates, and failure handling must be designed. |
| Event notification | Other services need to know that something happened, such as order placed. | Event contracts become confusing when ownership and meaning are vague. |
| Read model or projection | Consumers need a query view across several services. | Teams may recreate shared-database coupling if shortcuts are taken. |
| Analytics path | Historical or aggregate analysis needs data from many domains. | Operational systems can be overloaded if analytics bypasses boundaries. |
The goal is not to avoid communication. The goal is to make the meaning, dependency, owner, and failure behavior explicit.
Synchronous APIs
Synchronous APIs are request-response interactions. One service asks another service for an answer now.
This is reasonable when the caller truly needs an immediate answer to continue. Checkout may need a payment authorization result before confirming an order. A customer support tool may need the current order status while a support agent is on the phone.
The risk is chaining. If checkout calls orders, orders calls inventory, inventory calls catalog, catalog calls pricing, and pricing calls promotions, one customer request now depends on many services being healthy at the same time.
Use synchronous calls carefully:
Keep call chains short.
Set timeouts.
Make retry behavior explicit.
Decide whether the caller can degrade, reject, retry, or continue asynchronously.
Avoid exposing internal service models as public contracts.
An API contract should model the capability being offered, not the private database tables behind it.
Durable Messages and Events
Durable command messages fit work that should not disappear when the receiver is unavailable. An order service can send a durable command to reserve inventory or start fulfillment. The customer request can finish while back-office work continues.
Events are different. An event says that something happened. It is usually not an instruction to one specific receiver.
Order service
-> command: ReserveInventory
-> command: StartFulfillment
-> event: OrderPlaced
Inventory service processes the reservation command.
Fulfillment service processes fulfillment work.
Notification and analytics services may react to OrderPlaced.
The names matter because they express responsibility. ReserveInventory asks for work. OrderPlaced announces a fact. Treating both as generic messages makes recovery and ownership harder to reason about.
Durable communication needs idempotency. Idempotency means a receiver can safely handle the same message more than once. This matters because reliable distributed systems often prefer at-least-once delivery over silent data loss.
Data Ownership
Data ownership is where many microservice designs become honest.
If every service reads and writes the same database, the system may have separate APIs and containers, but it does not have independent ownership. Schema changes become cross-service changes. Data rules become unclear. Incidents become harder to assign.
Where practical, each service should own the data required for its responsibility:
Catalog service -> catalog data
Orders service -> order data and order history snapshots
Payments service -> payment attempts, partner references, and audit state
Inventory service -> availability, reservations, and adjustments
Fulfillment service -> shipment work and exception state
This does not mean every service must use a different database product. Several services may use the same database engine because it fits their needs. The architecture point is ownership, not collecting technologies.
Eventual Consistency and Read Models
In a monolith, one database transaction may update several tables at once. In microservices, each service often owns its own transaction boundary. The larger business process may become eventually consistent.
For example, an order can be created before inventory reservation is fully confirmed. Payment authorization may complete before fulfillment starts. The design needs to model these states honestly instead of pretending everything changes at the same instant.
Eventual consistency is not automatically bad. It is a trade-off. It gives services more independence, but it requires clear user experience, retries, compensation, monitoring, and support workflows.
Cross-service reporting should usually use read models, projections, analytics pipelines, or event-fed views rather than direct writes across operational databases.
Contract Responsibility Model
Service A
-> API contract for immediate request
-> Message/event contract for asynchronous flow
-> Owns its data boundary
Service B
-> Consumes only the published contract
-> Owns its reaction and side effects
The contract is the shared agreement. The implementation remains private.
Common Mistakes
Replacing every call with events without understanding meaning.
Keeping one shared write database.
Making synchronous chains too long.
Ignoring idempotency and replay.
Publishing events without schema ownership and versioning.
Building reports by bypassing service boundaries and querying every service database directly.
If Someone Asks You
What contracts matter most between microservices?
A good answer is:
Microservices need explicit API, message, event, and data contracts. APIs fit immediate request-response needs, but long synchronous chains create coupling. Durable messages fit work that must be processed reliably. Events announce facts that other services may react to. Each service should own its data boundary where practical, and cross-service views should use read models, projections, or analytics paths instead of shared operational writes. I also design for idempotency, retries, eventual consistency, and observability.
What You Should Remember
Independent services need independent contracts and owned data boundaries.
Questions to Check Understanding
When is an API call a better fit than a message?
Why is shared data risky?
What does idempotency protect against?