DB partagée entre services¶
Anti-pattern. Service A et service B partagent une même base de données / une même table. Chaque déploiement de l'un risque de casser l'autre.
Symptômes¶
- Une migration Alembic dans A casse B.
- Une optimisation d'index dans B ralentit A.
- Un verrou de ligne dans A bloque B.
- Un DROP TABLE accidentel dans A perd les données de B.
Pourquoi c'est tentant¶
- Pas de réseau (performance locale).
- Pas d'API à versionner.
- Jointures SQL faciles.
- Transactions ACID cross-service "gratuites".
Pourquoi c'est mauvais¶
1. Couplage de schéma à l'exécution¶
Si A change la colonne users.email en users.contact_email, B
doit être déployé en même temps. Le moindre décalage casse les
deux services. La migration devient une opération coordonnée
multi-équipe.
2. Couplage de performance¶
Une transaction longue dans A bloque les écritures de B sur la même table. Vous découvrez ça en prod, sous charge.
3. Fuite de données¶
Le schéma SQL ne fait pas la différence entre champs publics et
internes. Un dev de B fait SELECT * FROM users et voit les hachages
bcrypt, les tokens API et les données personnelles.
4. Pas d'évolution indépendante¶
A veut migrer Postgres → CockroachDB. Impossible sans migrer B en même temps. Vous êtes mariés.
Le bon motif¶
Chaque service a sa propre DB. Communication par :
- Push HTTP signé entre services.
- Read-model dénormalisé dans un service dédié.
- Outbox transactionnel + worker de drain.
graph TB
subgraph "❌ Avant"
A1[Service A] -->|lit/écrit| DB1[(DB partagée)]
B1[Service B] -->|lit/écrit| DB1
end
subgraph "✅ Après"
A2[Service A] -->|lit/écrit| DB_A[(DB de A)]
B2[Service B] -->|lit/écrit| DB_B[(DB de B)]
A2 -->|POST /events<br/>JWT signé| B2
end
"Mais on a un cas légitime !"¶
Trois exceptions où une DB partagée peut être justifiée :
- Motif transactional outbox côté émetteur : la table
outbox_eventsdu service A est lue par un worker du service A (pas par un autre service) — c'est juste de la file interne. - Read-replica : service B lit une réplique read-only du service A pour de l'analytique. La lecture est read-only côté B. Mais c'est souvent mieux servi par un read-model dédié.
- Migration progressive : pendant qu'on découpe un monolithe en services, l'étape intermédiaire peut nécessiter une DB partagée. C'est transitoire, et chaque jour qui passe en DB partagée est un jour qui coûte.
Hors de ces cas, pas de DB partagée. La règle est simple parce que l'exception est dangereuse.
Référence en pratique¶
L'exception historique est api ↔ sync qui
partagent un schéma SQLModel — documenté dans
PATTERN_TRANSACTIONAL_OUTBOX.md.
Tous les autres inter-services utilisent HTTP signé
(mail-out, audit, search, extract).