The shift to microservices and cloud-native architecture forces a re-examination of data management. The monolith database that served a monolithic application does not serve a distributed services architecture.
Database per service as the baseline
Each microservice owns its data and is the only service that accesses it directly. Other services access the data through the owning service's API. This is the baseline pattern for microservices data isolation. The immediate challenge: queries that previously joined tables across domain boundaries must now be replaced with API calls or event-driven data synchronisation. The benefit: services can be deployed independently because their data schema changes do not affect other services.
Saga pattern for distributed transactions
The saga pattern replaces distributed transactions (which do not work reliably in distributed systems) with a sequence of local transactions, each publishing an event to trigger the next step. Compensating transactions undo the work of earlier steps if a later step fails. The choreography model (services react to each other's events) and the orchestration model (a central saga orchestrator coordinates the steps) are the two implementations. Sagas add significant complexity compared to a local transaction, use them only when the business process genuinely spans service boundaries.
Event-driven data synchronisation
Services that need data from other domains can maintain a local cache derived from events published by the owning service. The Order service subscribes to Customer events and maintains a local read model of customer data it needs. The cache is eventually consistent. The benefit: the Order service does not need to query the Customer service synchronously on every order operation. The design challenge: handling event replay for cache rebuild, managing schema evolution in events, and accepting eventual consistency.
The CQRS read model as a query solution
Microservices make queries that span multiple services expensive (service-to-service API calls for each domain) or impossible (no shared database to join). CQRS read models solve this: a dedicated query service subscribes to events from multiple upstream services and maintains a denormalised read model optimised for the query. The query service is a read-only projection. The trade-off is eventual consistency between the upstream write models and the query service's read model.