Home/Writing/Build note

28 apps, 756 connections: why the database is the contract

Decasonic had 300+ agents spread across 10 to 15 products, each with its own database and no shared memory. I replaced the mesh with one rule: every app writes what it knows to one schema and reads what it needs from it.

Saad Al-TamariFrom the work: DDS v3 schema · Jan–Aug 20262 min read
Data architecturePostgresMulti-agent

No app calls another app’s API for data. The database is the contract. It sounds conservative. It is the reason the platform grew from about 10 products to 35+ without integration work eating the project.

The arithmetic of a mesh

If every app can talk to every other app, the number of possible connections grows as n × (n − 1). With one shared contract it grows as n. The gap is small at five apps and absurd at thirty.

Connections needed as apps grow: an app-to-app mesh grows as n times n minus one, a shared database grows as n. At 28 apps that is 756 versus 28.
Mesh: n × (n − 1)Shared database: nx-axis: number of apps
Exact counts, not estimates: at 28 apps a mesh needs 756 connections; a shared schema needs 28

What 87 tables are made of

DDS v3 shipped as one 1,571-line SQL migration: 87 tables and 73 enum types.

Primary entities35
Junction tables41
Sync tables6
Gateway tables5
The 87 tables by kind. Junction tables outnumber entities on purpose: relationships are first-class

Decisions, written down and locked

Nine structural decisions were recorded before the first table was created, so nobody had to rediscover them under pressure. Among them:

UUID primary keysJunction tables over JSONB arraysIdentity-centric ownershipAudit log on every changeCHECK constraints over Postgres enumsSoft deletesEnvironment separationOptimistic concurrency
Some of the nine locked structural decisions

A schema that describes itself

A schema-discovery call returns every table with its columns, types, foreign keys, constraints and human-written descriptions as JSON. Every downstream app and every AI code generator reads that one document, so generated integration code never guesses a column name.

Schema + descriptionswritten by people
Discovery JSONone call, always current
Code generatorreads meaning, not just shape
Integration clientreal column names
From schema to working client, without anyone copying column names by hand

When sources disagree

A CRM, a meeting transcript and an agent can all claim a different value for the same field. A system-of-record priority model resolves those conflicts deterministically, field by field, so the answer doesn’t depend on which write arrived last.

A shared schema is slower to design and faster to live with. That trade is almost always worth it.

Read next · Build noteWhy my context engine leaves budget unspent
Contact

Let's build

Building something like this, or want me to write about your problem next? One email is enough to start.