Ownership
One service decides how its data is structured, validated, and evolved.
Database per Service is one of the clearest tests of whether microservice boundaries are real. If multiple services still share ownership of the same tables, the architecture may have been split operationally but not conceptually. This pattern forces each service to own its data and publish information through APIs or events instead of backdoor queries.
Ordering should not inspect payment tables directly to decide whether an order is paid. It should know that through a contract from the payment capability. That protects autonomy, preserves local invariants, and makes ownership visible rather than implicit.
Database per Service means each service owns its persistence model. Other services interact through contracts, not shared schema access. This improves encapsulation at the system level but complicates reporting, joins, and workflow coordination. Those concerns do not disappear; they move into integration patterns such as read models, events, and dedicated reporting pipelines.
The biggest gain is not just technical isolation. It is ownership clarity. Teams stop changing each other's data structures by accident and start negotiating explicit business contracts.
One service decides how its data is structured, validated, and evolved.
Other services use APIs, events, or replicated read models instead of direct table joins.
Cross-service queries move into separate projections or analytics pipelines.
Teams can evolve schema and persistence rules without hidden cross-service breakage.
Queries that once relied on one transaction and one join now need projections, APIs, or asynchronous views.
Events and query APIs become part of the design, not an afterthought.
During migration, temporary duplication and synchronization work are normal and should be planned.
package org.javaomnibus.payments.api;
public interface PaymentStatusQuery {
PaymentStatusView findByOrderId(OrderId orderId);
}
public record PaymentStatusView(
String orderId,
String authorizationStatus,
Instant lastUpdatedAt
) {}