A shared database is accessed directly by more than one module, service, or application. Those consumers may read the same tables, update the same records, or rely on stored procedures and triggers. The arrangement can be intentional, but it makes the database schema part of every consumer's effective interface.
How sharing creates coupling
Direct access lets a consumer depend on details that the data owner may not know are public. A renamed column, changed constraint, or revised status value can affect another application without any code dependency between their repositories.
| Shared element | Hidden dependency | Audit evidence |
|---|---|---|
| Tables and views | Consumers depend on schema shape | Query logs and source search |
| Stored logic | Rules run outside application code | Procedures, functions, and triggers |
| Credentials | Access is broader than ownership | Grants, roles, and connection settings |
| Batch exports | Downstream timing and format assumptions | Jobs, files, and schedules |
The dependency exists even when a consumer performs reads only. Reporting, caching, and integration jobs can still constrain when a schema or retention rule changes.
What an audit maps
Repository search finds queries and object mappings, but it may miss external reports, manual scripts, or applications maintained elsewhere. Runtime database evidence can reveal active users and statements. Team interviews and deployment configuration add the ownership context that technical traces do not contain.
The inventory records:
- Readers and writers. Every known consumer and the operations it performs.
- Authoritative fields. Which component decides the valid value for each important record.
- Stored behaviour. Rules implemented in triggers, procedures, defaults, and constraints.
- Timing assumptions. Jobs or exports that depend on update order and availability.
- Access boundaries. Credentials and privileges used by each consumer.
This map becomes part of the dependency graph for the legacy system.
Separating ownership from storage
A modular monolith can keep one physical database while assigning table or schema ownership to modules. Other modules then use an interface rather than writing owned data directly. This restores a logical boundary before any infrastructure changes.
Service extraction introduces a deployment boundary, but it does not require an immediate database move in every case. The migration can first route writes through the future owner, then redirect reads, and only later move storage. Each stage needs a named authority so two components do not make conflicting changes.
Planning a safe transition
The migration plan starts with the consumers that already use a stable interface. Direct queries are replaced according to risk and business importance. Compatibility views or translation code can support a transition, provided their retirement conditions are recorded.
A legacy codebase audit connects the shared database to the monolith, its bounded contexts, and the capabilities selected for change. That evidence determines migration order. The goal is not to split storage for its own sake. It is to make data ownership and change impact explicit enough that each later refactoring step can be verified.