Skip to content

ADR-002: Table Prefix over Separate Schemas ​

Status ​

Accepted

Context ​

In our modular monolith, each service owns its database tables. We needed to decide how to separate tables between services:

  • Separate Schemas: auth.users, org.organizations, rx.prescriptions
  • Table Prefix: identity_users, org_organizations, rx_prescriptions

Decision ​

We will use table prefixes (e.g., auth_users, org_organizations) rather than separate PostgreSQL schemas.

Rationale ​

Why Table Prefix? ​

  1. Simpler migration management - All tables in one schema, easier to manage with Goose
  2. Cross-service queries - No schema qualification needed for joins
  3. Tool compatibility - Works seamlessly with all SQL tools and ORMs
  4. Extraction path - Easy to dump tables with prefix when extracting to microservice
  5. No GRANT management - Simpler permissions (single schema)

Why Not Separate Schemas? ​

AspectSeparate SchemasTable Prefix
IsolationBetter (schema-level)Good (naming convention)
MigrationsMore complexSimpler
Cross-service queriesNeed schema qualificationDirect
PermissionsSchema-level GRANTsDatabase-level
ExtractionMove schemaDump by prefix

Naming Convention ​

PrefixServiceExample Tables
auth_Identityauth_users, auth_sessions
org_Organizationorg_organizations, org_members, org_teams
rx_Prescriptionrx_prescriptions, rx_prescription_items
subscription_SubscriptionPlanned: subscription_plans, subscription_subscriptions, subscription_invoices, subscription_payments
admin_Adminadmin_users
authz_Authorizationauthz_roles, authz_permissions, authz_role_assignments
notification_Notificationnotification_notifications, notification_templates

Doctor profiles belong to the doctor service under ADR-013, not identity. Its persistence is deferred; this ADR does not assign a doctor table name or create any of the planned billing tables. Admin no longer owns tenant projections (ADR-012). Existing identity migrations use the auth_ prefix.

Consequences ​

Positive ​

  • Simpler database management
  • Easier cross-service queries during monolith phase
  • Straightforward extraction path

Negative ​

  • Less isolation than separate schemas
  • Naming convention must be enforced

Documentation follows the source. Planned contracts are marked explicitly.