पाठ 9 / 25

Data Ownership Inside a Monolith

Give each module its own tables and avoid cross-module joins.

Private data, even in one database

The hardest part of any later split is the data. A monolith where every module reads and writes every table cannot be divided without untangling years of joins. A modular monolith keeps data owned per module: each module has its own schema or table prefix, only that module's code touches those tables, and other modules ask through its API. Cross-module foreign keys and joins are avoided; store the other module's ID and fetch details through the API, or keep a small local copy updated by in-process events. You still enjoy one database server, one backup and, when needed, one transaction spanning modules, but you can enforce ownership with database permissions (a separate database user per module) and architecture tests. When a module later becomes a service, its schema moves with it.

Schemas per module in PostgreSQL

Each module's user can only touch its own schema.

CREATE SCHEMA catalogue;
CREATE SCHEMA ordering;
CREATE SCHEMA billing;

CREATE ROLE ordering_app LOGIN PASSWORD '...';
GRANT USAGE ON SCHEMA ordering TO ordering_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA ordering TO ordering_app;
-- no grants on catalogue or billing: ordering must use their APIs

CREATE TABLE ordering.orders (
    id           uuid PRIMARY KEY,
    customer_id  uuid NOT NULL,          -- ID only, no FK into another module
    total_minor  bigint NOT NULL,
    status       text NOT NULL
);

Reporting needs its own path

Reports often want to join everything. Instead of letting reports break module ownership, feed a separate reporting database or warehouse from module events or change data capture.

त्वरित जाँच: Why avoid foreign keys between tables of different modules?

  • Foreign keys are slow in all databases
  • They are not allowed in PostgreSQL
  • They couple modules at the data level and make a later split into services very hard
  • They prevent backups
Answer

They couple modules at the data level and make a later split into services very hard — Cross-module constraints tie data together, blocking independent evolution of modules.