
Git-first y DB-first: dos canales editoriales con una sola autoridad por dominio
Cuando una web empieza como un conjunto de Markdown, Git parece suficiente para todo. Es sencillo, auditable, portable y encaja de forma natural con editores como Obsidian y con generadores estáticos como Astro. Cuando esa misma web empieza a adquirir entidades, relaciones, estados, revisiones, APIs y automatizaciones, PostgreSQL empieza a ofrecer propiedades que los ficheros no deberían tener que imitar.
La tentación es escoger un único bando: todo en Git o todo en base de datos.
En jesuserro.com he preferido otra respuesta: mantener dos canales editoriales y decidir la autoridad por dominio.
Git-first DB-first
Obsidian Obsidian / UI / agente
↓ ↓
Markdown application service
↓ ↓
Git PostgreSQL
↓ ↓
publicación proyección
↓
Git
La condición que hace viable este modelo no es tecnológica. Es una regla de propiedad:
cada dominio tiene una sola autoridad de escritura.
Puede existir una copia en el otro sistema. Puede existir incluso para consultas, búsqueda o grafo. Pero no puede existir una segunda verdad editable.
Git-first: cuando el contenido es principalmente un documento
Un artículo, una ADR o un runbook son fundamentalmente documentos. Su forma natural es un fichero legible por una persona:
Obsidian
↓
Markdown
↓
Git
↓
PR / CI
↓
Astro
Aquí Git ofrece casi todo lo que necesito:
- diff textual;
- historial;
- branches;
- revisión humana;
- rollback;
- portabilidad;
- despliegue sencillo.
Si corrijo un párrafo de un artículo, el cambio que quiero conservar es precisamente ese cambio de texto. No necesito una transacción de negocio ni un current_revision dentro del dominio. El commit ya representa adecuadamente la evolución del documento.
Para este tipo de contenido, convertir PostgreSQL en requisito de escritura sólo añadiría fricción.
DB-first: cuando el contenido se comporta como una aplicación
Una Review empezó siendo también Markdown, pero dejó de ser únicamente texto cuando empezó a tener:
- identidad estable;
- Work o Edition como sujeto;
- Readings relacionadas;
- Topics;
- References;
- SourceRecords;
- estado de publicación;
- revisiones editoriales;
- concurrencia y automatización futura.
En ese momento, tratar el fichero como si fuese una base de datos obliga a construir alrededor de YAML y directorios mecanismos que PostgreSQL ya resuelve mejor.
Por eso Reviews y Readings hicieron un authority pivot: PostgreSQL pasó a ser la fuente canónica y los Markdown públicos se convirtieron en proyecciones generadas.
El flujo cambia:
working copy
↓
PLAN
↓
application service
↓
PostgreSQL
↓
projection apply
↓
Markdown generado
↓
Git
↓
Astro
Git no desaparece. Cambia de función.
Antes guardaba la verdad editorial. Después del pivot guarda una representación pública versionada y el artefacto que terminará desplegándose.
Estar en PostgreSQL no significa ser DB-first
Ésta es una distinción especialmente importante.
Un artículo Git-first puede terminar también en PostgreSQL.
Por ejemplo, podría existir una tabla derivada con:
article_id
title
slug
topics
published_at
body
Eso permitiría resolver consultas transversales, alimentar un grafo, una API o un agente. Pero mientras la autoridad siga siendo Git, esa fila no es el lugar donde debe editarse el artículo.
Git/Markdown = write authority
PostgreSQL = read model derivado
El sistema puede por tanto tener una PostgreSQL muy rica sin afirmar que todo el proyecto es DB-first.
Canonical model y read model responden preguntas diferentes
Me resulta útil separar dos preguntas:
¿Dónde cambio la verdad?
Ésa es la autoridad canónica.
Para una Review DB-first:
PostgreSQL
Para un Article Git-first:
Markdown/Git
¿Dónde consulto eficientemente?
Ése es el read model.
Y aquí PostgreSQL puede ser útil para ambos.
Git-first Articles ──ingest──┐
├── PostgreSQL read model
DB-first Reviews ────────────┘
Desde esa capa se pueden construir:
- búsqueda;
- knowledge graph;
- APIs;
- agentes;
- analítica;
- consultas que crucen dominios.
No necesito implantar un CQRS formal para aprovechar esta separación. Basta con no confundir el lugar donde se consulta con el lugar donde se escribe.
El error que quiero evitar: dos autoridades editables
El diseño peligroso es éste:
Git <────────> PostgreSQL
editable
Al principio parece flexible. Después aparecen preguntas difíciles:
- ¿qué ocurre si alguien cambia los dos lados?
- ¿gana el timestamp más reciente?
- ¿se fusionan campos?
- ¿qué pasa con el body?
- ¿qué ocurre si un agente y Obsidian editan a la vez?
La respuesta suele terminar en reconciliación compleja o en last write wins, que puede destruir una edición perfectamente válida.
Prefiero una flecha inequívoca.
Git-first
Git ─────────► PostgreSQL
authority derived
DB-first
PostgreSQL ─────────► Git
authority derived
Si la copia derivada cambia por fuera del flujo soportado, eso no es una nueva versión: es drift.
Obsidian puede vivir encima de los dos canales
Esta arquitectura no obliga a renunciar a Obsidian.
En Git-first funciona como siempre:
Obsidian → Markdown authoritative
En DB-first cambia ligeramente:
PostgreSQL
↓ checkout
working copy
↓
Obsidian
↓ plan/apply
application service
↓
PostgreSQL Revision N+1
La experiencia humana sigue siendo escribir un Markdown cómodo. Lo que cambia es el contrato al guardar.
En vez de copiar el fichero directamente sobre la web pública, la working copy se transforma en una mutación controlada del dominio.
Esta diferencia permite mantener una interfaz de escritura agradable sin sacrificar transacciones, revisionado o concurrency control.
Una Review puede tener dos historias distintas
El modelo DB-first también permite separar algo que en Git suele quedar mezclado.
PostgreSQL ReviewRevision
= historia editorial del contenido
Git commit
= historia técnica/publicada del repositorio
Una Review puede pasar de Revision 3 a Revision 4 aunque ese cambio termine compartiendo un commit con el manifest y otras proyecciones técnicas.
El historial intelectual de la reseña ya no depende de interpretar el historial completo del repositorio.
Migrar más dominios en el futuro
Esta arquitectura no decide que Articles deban quedarse Git-first para siempre.
Si dentro de unos años un artículo empieza a necesitar workflows, varias superficies de edición, relaciones fuertes o revisionado explícito, puede hacerse otro authority pivot.
El proceso que quiero repetir es incremental:
1. identificar baseline
2. definir contrato DB
3. expandir schema
4. backfill
5. demostrar paridad
6. introducir writer DB
7. crear proyección + drift detection
8. declarar PostgreSQL authority
9. deshabilitar el viejo writer Git
Lo importante es el paso 9. Una migración no está terminada cuando los datos existen en PostgreSQL. Está terminada cuando existe una sola ruta autorizada para cambiar la verdad.
No todo debe migrarse porque pueda migrarse
PostgreSQL es una herramienta magnífica, pero no convierte automáticamente un documento en una entidad de aplicación.
Mi regla práctica queda así:
Si algo es principalmente documento, empieza Git-first. Si algo es principalmente entidad con ciclo de vida, relaciones y mutaciones, DB-first puede ser el modelo correcto.
Y después observar.
El enfoque incremental que describo en Modernizar una base de datos legacy sin reescribirla encaja bien aquí: primero dejo que las necesidades reales demuestren qué complejidad existe y después consolido el modelo que la soporta.
Plan antes de mutar
El canal DB-first también conecta con el Rehearsal Pattern: observar, calcular un PLAN, comprobar precondiciones, aplicar y verificar.
state
↓
plan
↓
approval
↓
mutation
↓
verification
En un sistema donde PostgreSQL es autoridad, esta separación es más importante que un simple botón de guardar. Permite detectar una revisión obsoleta, un cambio inesperado o una proyección con drift antes de mutar el estado canónico.
Git sigue siendo esencial en DB-first
DB-first no significa «sin Git».
Git continúa aportando:
- versionado del código y schema;
- manifest e integridad de proyecciones;
- review mediante PR;
- CI;
- deployment;
- trazabilidad técnica.
En el Director–Implementer Loop R4.1 GitHub también es bus de handoff y audit trail. Pero transporte y auditoría no equivalen a autoridad editorial.
Esta separación de responsabilidades es precisamente la idea común entre ambos diseños.
Una arquitectura híbrida, no una transición inacabada
Tener Git-first y DB-first al mismo tiempo no significa que la migración esté a medias.
Puede ser el estado correcto durante mucho tiempo:
AUTHORING
│
┌────────────┴────────────┐
│ │
Git-first DB-first
│ │
▼ ▼
Git PostgreSQL
authority authority
│ │
└──────────┬──────────────┘
▼
unified read model
│
┌────────────┼────────────┐
▼ ▼ ▼
graph API agents
La arquitectura puede evolucionar hacia más DB-first si el producto lo necesita. Pero no necesito pagar hoy el coste de convertir cada documento en una entidad transaccional.
La decisión que sí quiero conservar desde el principio es más pequeña y más fuerte:
un dominio puede tener muchas representaciones, pero sólo una autoridad de escritura.
A partir de ahí, Git y PostgreSQL dejan de competir. Cada uno hace el trabajo para el que resulta mejor.