ADR-003 — Alembic para migraciones de base de datos¶
| Campo | Valor |
|---|---|
| Estado | Aceptado |
| Fecha | 27 de enero de 2026 |
| Decisores | Equipo Custodiam (Marcos Val Sanz, Rodrigo Mulero García) |
Contexto¶
El backend custodiam-api necesita un mecanismo de migraciones de base de datos versionadas que permita evolucionar el esquema PostgreSQL conforme el modelo de dominio cambia, sin perder datos y de forma reproducible entre máquinas. La elección de SQLModel como ORM unificado (sobre SQLAlchemy 2.0 por debajo) deja abierta la decisión de la herramienta de migraciones.
Decisión¶
Alembic como herramienta de migraciones, instalada como dependencia del proyecto.
# Alembic detecta cambios en modelos y genera migración automáticamente
alembic revision --autogenerate -m "add municipio column to voluntarios"
# Aplica la migración a la BD
alembic upgrade head
La plantilla script.py.mako se ajusta para importar sqlmodel y permitir que el --autogenerate resuelva correctamente tipos como sqlmodel.sql.sqltypes.AutoString.
Justificación¶
-
Integración nativa con SQLAlchemy/SQLModel. Alembic es el proyecto hermano de SQLAlchemy desarrollado por el mismo autor. Lee los modelos directamente desde
SQLModel.metadatay genera migraciones a partir del diff entre el modelo declarado en Python y el estado real de la BD. -
Autogeneración madura.
alembic revision --autogenerateproduce migraciones funcionales para la mayoría de cambios (ADD COLUMN,CREATE TABLE,CREATE INDEX, renames con--rename). Los cambios delicados (data migrations, constraints complejos, downgrades) se editan a mano sobre el archivo generado. -
Data migrations versionadas. Los catálogos pre-poblados (ADR-025) viven como
INSERT INTO ... VALUES (...)dentro de las migraciones Alembic, no como configuración volátil externa. Una restauración de BD desde el repo restaura también los datos canónicos. -
Integración con CI. El workflow de CI ejecuta
alembic upgrade headen una BD efímera al inicio de la suite de tests, garantizando que las migraciones aplican limpias sobre una BD vacía y que el estado tras aplicarlas coincide con lo que los tests esperan. -
Estándar histórico en Python. Alembic es la herramienta de facto para migraciones en proyectos Python con SQLAlchemy desde aproximadamente 2010. Documentación extensa, ecosistema maduro, gran cantidad de recetas para casos avanzados (PostgreSQL-specific, particionado, JSONB, etc.).
Alternativas evaluadas y descartadas¶
A. Liquibase¶
- Pros: agnóstico al lenguaje, soporta múltiples BDs, formato declarativo con XML / YAML / SQL.
- Contras: escrito en Java — añade JVM al stack del backend. Sintaxis declarativa más prolija que Alembic. La integración con SQLAlchemy/SQLModel no es nativa: hay que mantener manualmente el paralelismo entre los modelos Python y los changelogs Liquibase.
- Descartado por: arrastra JVM al backend sin beneficio sobre Alembic para un stack 100 % Python.
B. Flyway¶
- Pros: simple, basado en archivos SQL versionados (
V1__init.sql,V2__add_column.sql). - Contras: igualmente Java — mismo problema que Liquibase. Sin autogeneración a partir de modelos: cada migración se escribe a mano.
- Descartado por: misma razón que Liquibase, y peor ergonomía sin autogeneración.
C. Migraciones manuales con SQL plano sin herramienta¶
- Pros: máximo control, cero dependencias.
- Contras: sin versionado automático, sin checks de orden, sin downgrade, sin integración con CI. Requiere escribir y mantener manualmente la tabla de versiones aplicadas.
- Descartado por: reinventar Alembic sin beneficios.
Implicaciones operativas¶
- Estructura del repo: la carpeta
alembic/vive en la raíz decustodiam-apiconenv.pyconfigurado para leer elDATABASE_URLde las variables de entorno (no delalembic.ini, que se mantiene minimal). Las migraciones generadas viven enalembic/versions/. - Plantilla con
sqlmodel: la líneaimport sqlmodelse añade ascript.py.makopara que los tipossqlmodel.sql.sqltypes.*se resuelvan en las migraciones autogeneradas. - CI: el job de tests ejecuta
alembic upgrade headantes de correrpytest. Esto garantiza que cualquier cambio de schema en una PR pasa por la migración antes que por los tests. - Convención de nombres: las migraciones siguen el patrón
<revision>_<descripcion_corta>.pycon descripción en snake_case y verbo imperativo (add_municipio_column,create_acreditaciones_table).
Referencias¶
- Documentación oficial de Alembic — guía completa.
- Alembic + SQLModel — integración recomendada.
- ADR-002 SQLModel — ORM unificado sobre el que opera Alembic.
- ADR-008 psycopg3 — driver PostgreSQL que Alembic usa por debajo.