Azure Architecture Guide

CH10-04: Asynchronous Communication and Data Ownership

Goal

After choosing boundaries and runtime, 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 messaging, events, and data ownership. It builds directly on Chapter 5's integration mental model: commands, events, streams, and workflows mean different things.

The durable idea is:

Service independence depends on explicit contracts, careful communication choices, and clear data ownership.

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 StyleGood FitMain Risk
Synchronous APIImmediate answer needed, such as price lookup or authorization decision.Call chains can make one slow service slow down many others.
Durable command messageWork must be processed reliably, such as reserve inventory or start fulfillment.Retries and duplicate handling must be designed.
Event notificationOther services need to know something happened, such as order created.Event contracts can become messy if ownership is unclear.
Read model or analytics pathReporting needs data from several services.Teams may recreate shared database coupling if shortcuts are taken.

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. For example, checkout may need a payment authorization result before it confirms an order. An API call can be the right contract when latency, availability, and failure behavior are understood.

The risk is chaining. If the checkout API 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. That is not independence. It is a distributed version of one tightly coupled transaction.

Use synchronous calls carefully. Set timeouts. Avoid long call chains. Make failure behavior explicit. Decide whether the caller can retry, degrade, reject the request, or move work to an asynchronous path.

Durable Messaging

Azure Service Bus is commonly used when a service needs to hand off work reliably. A queue or topic can store messages until a receiver can process them. This helps when the receiver may be temporarily unavailable or when work should not disappear during a deployment, restart, or transient failure.

For example, an order service can send a durable command to fulfillment:

Order service
	-> Service Bus command: Start fulfillment
			-> Fulfillment service

The order service does not need to wait for the fulfillment service to complete every warehouse step during the customer's checkout request. But the design now needs idempotency, retry handling, poison-message handling, and visibility into message backlog.

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.

Events

An event says something happened. It is not usually an instruction to one specific receiver.

For example:

Order service publishes: OrderCreated
	-> Inventory service updates reservation view
	-> Recommendations service updates behavior model
	-> Email service sends confirmation

Events let services react independently, but they need careful contracts. The publishing team owns the meaning and versioning of the event. Subscribing teams own their reactions. No subscriber should assume it can control the publisher's internal workflow.

Azure Event Grid, Service Bus topics, or other eventing patterns may appear depending on the need. The important point at this stage is not the product name. It is whether the message is a fact that happened, a durable command to do work, or a stream of records.

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. Database 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 business responsibility:

Catalog service -> Catalog data
Orders service  -> Order data
Payments service -> Payment records and integration state
Inventory service -> Inventory availability and reservation state

This does not mean every service must use a different database technology. Several services might use Azure SQL Database because relational data fits their needs. Another service might use Azure Cosmos DB because document access patterns fit better. The architecture point is ownership, not collecting different database products.

Cross-service reporting may need separate read models, replicated data, analytics pipelines, or event-fed projections. That is usually healthier than giving every reporting query direct access to every service's operational tables.

Eventual Consistency

In a monolith, one database transaction may update several tables at once. In microservices, each service often owns its own transaction boundary. That means the whole 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 architecture needs to model these states explicitly instead of pretending everything changes at the same instant.

Eventual consistency is not a bug by itself. It is a trade-off. It gives services more independence, but it requires clear user experience, retries, compensation, monitoring, and support workflows.

Common Mistakes

  • Replacing every in-process call with a synchronous network call.

  • Sharing one database across services and calling the result microservices.

  • Treating every message as the same kind of asynchronous communication.

  • Ignoring duplicate messages, retries, ordering, and eventual consistency.

  • Publishing events without owning schema versioning and consumer impact.

  • Building reports by bypassing service boundaries and querying every service database directly.

If Someone Asks You

How should microservices communicate in Azure?

A good answer is:

I choose communication by dependency and failure behavior. Synchronous APIs fit immediate request-response needs, but I avoid long call chains. Service Bus is useful for durable commands and reliable work handoff. Events notify other services that something happened. Each service should own its data where practical, and cross-service reporting should use read models, events, or analytics paths instead of shared operational tables. I also design for retries, idempotency, eventual consistency, and observability.

What You Should Remember

Communication choices should preserve service ownership instead of hiding coupling.

Questions to Check Understanding

  1. When is synchronous communication reasonable?

  2. When does durable messaging help?

  3. Why does data ownership matter to service independence?