Database per Service Pattern


The database per service pattern is a fundamental principle of microservice architecture: each service owns its data exclusively and no other service can directly access its database. This pattern ensures loose coupling between services, allowing them to evolve independently, choose appropriate storage technologies, and scale autonomously. However, it introduces significant challenges for queries that span multiple services.

Data Ownership

In the database per service pattern, each service has complete ownership of its data schema, storage technology, and access patterns. No other service can read or write to the database directly—all data access must go through the owning service's API.

This ownership is essential for independent evolution. A service can change its database schema, migrate to a different database technology, or refactor its data model without coordinating with other services. The API is the contract, and as long as the API remains backward compatible, the service is free to change its internal implementation.

Ownership also clarifies responsibility. When data issues arise, there is no ambiguity about which team owns the data. Data quality, consistency, and backup policies are the responsibility of the owning service.

Choosing Storage Technology

Database per service allows each service to choose the storage technology that best fits its needs. A product catalog service might use a document database for flexible product schemas. An analytics service might use a columnar store for efficient aggregations. A session store might use Redis for fast key-value access.

Polyglot persistence—using different database technologies for different services—is a key benefit of the pattern. Each service can optimize its data storage for its specific access patterns without being constrained by a shared database technology.

Queries Across Services

The most significant challenge with database per service is queries that need data from multiple services. Since services cannot directly access each other's databases, they must fetch data through APIs. This introduces latency, network overhead, and complexity.

Several patterns address cross-service queries. API composition involves a service or gateway calling multiple services and combining results in memory. This is simple but can be slow and resource-intensive for complex queries.

The CQRS pattern creates specialized query services that maintain pre-joined read models. A reporting service subscribes to events from multiple services and builds denormalized tables for efficient querying. This provides good query performance but introduces eventual consistency.

The materialized view pattern creates dedicated read services that aggregate data from multiple sources. These services own the read model and keep it synchronized through event subscriptions. This is the most scalable approach for complex cross-service queries.

Transactions Across Services

Distributed transactions across services require the Saga pattern rather than traditional ACID transactions. The Saga pattern coordinates a series of local transactions with compensating actions. This ensures data consistency without requiring a shared database or distributed transaction coordinator.

Performance implications of cross-service queries are significant. Each cross-service API call adds network latency, serialization overhead, authentication checks, and rate limiting. Services should be designed to minimize cross-service queries. This often means duplicating data (with clear ownership) across services.

Eventual Consistency

Database per service means that the system is eventually consistent. When data changes in one service, other services learn about it through events with some delay. Building a system that works correctly with eventual consistency requires careful design.

Strategies include displaying stale data with clear indicators, using optimistic UI updates, and designing workflows that tolerate temporary inconsistencies. The key is understanding which operations require strong consistency and which tolerate eventual consistency.

Practical Implementation

Organizations adopting database per service should start by identifying service boundaries that align with data ownership. Each service should own data that naturally belongs to its domain. Cross-service data should be analyzed to identify whether the service boundaries are correct or whether data synchronization is needed.

The pattern is most effective when combined with event-driven communication. Services publish events when their data changes, and other services consume events to update their cached or duplicated data. This reduces the need for synchronous cross-service queries.

Database per service is a powerful pattern for achieving service autonomy and independent scalability, but it requires disciplined data management, investment in cross-service query patterns, and acceptance of eventual consistency. For teams that make this investment, the pattern enables truly independent service evolution.