0012 — Postgres topology
One Postgres server locally and in Azure; one database per service.
Decided. Each service owns exactly one Postgres database. No service may connect to another service's database. Locally and in production, all service databases live on a single Postgres server.
What this looks like
Locally: one Docker container running Postgres. Each service has its own database and its own login role with access limited to that database. Adding a service means adding a database — not adding a container.
Production (Azure): one Azure Database for PostgreSQL Flexible Server. Same structure: one database per service, one login role per service, no cross-database access. A Flexible Server is a managed VM running Postgres; you create as many databases on it as you need and pay for one server, not one server per service.
If a service ever has significantly different scaling or availability requirements, it gets its own server instance. That decision is made per service, when the reason exists.
Why not one server per service
Running a separate Postgres server per service locally is unnecessary friction — more containers, more ports, more memory. In production it means paying for and operating N servers when one handles the load. The separation that matters is ownership and access: service A cannot query service B's tables. That is enforced by Postgres roles and grants, not by separate hardware.
Each service has a login role that has CONNECT granted only on its own database,
and CONNECT is explicitly revoked from public on every database. A connection
attempt by accounting_app to the workorders database is rejected by Postgres
before it gets anywhere near a table.
Why local and production match structurally
Local runs the same role-per-service setup as production. If the grants are wrong, you find out locally, not in production. A developer connecting to inspect data uses the admin role, which can reach every database; the service process uses only its own app role.