Saltar a contenido

0002. Stack de herramientas y librerías de svc-identidad

Estado: Aceptado Fecha: 2026-08-31 Autor: Duval Alcivar Módulo(s) afectado(s): svc-identidad (todo el servicio)

Contexto

El ADR 0001 define svc-identidad como un microservicio FastAPI dentro del core de plataforma, dueño de usuarios, empresas, sucursales y permisos. Su implementación toca datos sensibles (credenciales, tokens, tenencia), así que la elección de librerías y herramientas determina directamente la seguridad, la mantenibilidad y la capacidad de testearla. Falta fijar el stack concreto: ORM, migraciones, validación, autenticación, gestión de dependencias, testing, calidad de código, documentación y contenedores — consistente con el resto del backend (ADR 0001 y 0005 globales).

Decisión

svc-identidad usa el siguiente stack, uniforme con el resto de los microservicios y fijado por norma (no por preferencia de cada developer):

Área Herramienta Justificación
Lenguaje Python 3.12+ versión LTS del proyecto (ADR 0005 global); typing moderno (X \| None) y mejor rendimiento de asyncio
Framework FastAPI estándar del proyecto; emite el JWT (ADR 0006) y expone el JWKS; OpenAPI autogenerado
ORM SQLAlchemy 2.0 mapeo declarativo con integración a FastAPI; consultas siempre parametrizadas (inmunes a inyección SQL)
Migraciones Alembic cada cambio de esquema es un archivo versionado y reproducible
Validación Pydantic v2 contrato tipado en toda frontera de entrada/salida; interopera con FastAPI
Autenticación JWT (access + refresh) + Argon2 tokens firmados RS256 (ADR 0006 transversal) y hash de contraseñas Argon2id (ADR 0011)
Dependencias Poetry + pyproject.toml gestión de venv + dependencias + lockfile (poetry.lock) reproducible en un solo comando (ADR 0005 global)
Testing pytest + pytest-cov + httpx unitario e integración con cobertura; httpx asíncrono para probar endpoints sin levantar servidor
Calidad Ruff + Black + mypy lint, formato y tipado estático en CI: catálogo de errores antes de producir
Documentación OpenAPI (Swagger / Redoc) autogenerada por FastAPI desde los schemas Pydantic
Contenedores Docker + docker-compose entornos locales reproducibles idénticos a los del pipeline
CI Pipeline (lint + type-check + tests) puerta de cada pull request; nada se mergea sin pasar las 3 etapas

Detalles por herramienta

ORM — SQLAlchemy 2.0. Todo acceso a datos pasa por el repository (ADR 0001 global) usando consultas parametrizadas (posición de parámetros o text() con bind params). Queda prohibido concatenar valores del usuario en un string SQL. El aislamiento por empresa lo garantizan RLS + el middleware (ADR 0012): el middleware setea app.current_empresa_id una vez por request; los repositorios no inyectan ni filtran por empresa_id en cada query (no escriben WHERE empresa_id ni SET). RLS filtra automáticamente.

Migraciones — Alembic. Una migración por cambio de esquema, versionada en alembic/versions/ y aplicada por el pipeline. Queda prohibido alterar el esquema por fuera de Alembic (ej. create_table manual en producción).

Validación — Pydantic v2. Cada request y cada response del servicio se define con un schema Pydantic (estructura del ADR 0001/0004 global). FastAPI valida entrada/salida automáticamente y alimenta el OpenAPI.

Autenticación — JWT + Argon2. Firma RS256 del JWT y hashing Argon2id de contraseñas, según el detalle del ADR 0011 (login) y el ADR 0006 global. svc-identidad es el único que firma; los demás validan (ADR 0006).

Gestión de dependencias — Poetry. Poetry gestiona el venv, las dependencias (declaradas en pyproject.toml, sección [tool.poetry]) y el lockfile (poetry.lock) reproducible en un solo comando (ADR 0005 global). Cada desarrollador corre poetry install en el proyecto y queda con el mismo entorno que CI.

Testing — pytest + pytest-cov + httpx. Estructura tests/unit/<dominio> e tests/integration/<dominio> (ADR 0001). pytest-cov exige cobertura mínima; httpx prueba los endpoints FastAPI con un AsyncClient sin necesidad de arrancar el servidor.

Calidad — Ruff + Black + mypy. Ruff para lint (reemplaza flake8 + isort), Black para formato y mypy en modo estricto para tipado estático. Los tres corren en CI antes que los tests.

Documentación — OpenAPI. FastAPI genera Swagger (/docs) y Redoc (/redoc) desde los schemas Pydantic, sin código extra; es el contrato que consume el frontend (ADR 0007 global).

Contenedores — Docker + docker-compose. Dockerfile multi-etapa (build → runtime) y docker-compose.yml que levanta el servicio + Postgres local. El entorno local equivale al de producción, eliminando el "en mi máquina funciona".

CI — lint + type-check + tests en cada PR. El pipeline ejecuta, en orden: Ruff → Black --check → mypy → pytest (con cobertura). Un fallo en cualquier etapa bloquea el merge del PR.

Alternativas descartadas

  • ORM: Django ORM — descartado: arrastra Django REST Framework y su modelo de app completo; sobre FastAPI ya elegido (ADR 0001) añade peso sin ventaja.
  • ORM: Tortoise-ORM / asyncpg a pelo — descartados: menor ecosistema y documentación que SQLAlchemy 2.0; SQL crudo dificulta el testeo y el refactor del repository.
  • Gestión de dependencias: pip + requirements.txt — descartado por no fijar resolución reproducible; no separa dev/runtime ni genera un lockfile como Poetry (poetry.lock).
  • Testing: unittest / doctest — descartado: no manejan bien el pytest avanzado (fixtures, parametrize, asyncio) ni la cobertura con scripts propios.
  • Calidad: flake8 + isort por separado — descartado: herramientas duplicadas y configuración repartida; Ruff unifica lint + formato en una sola con velocidad Rústica.
  • No usar mypy — descartado: sin tipado estático, los contratos Pydantic/SQLAlchemy no se verifican en compile-time y los errores escalan a producción.

Consecuencias

  • Un solo stack estandarizado reduce la curva de onboarding y hace que cualquier developer del equipo pueda abrir y tocar svc-identidad.
  • La seguridad por defecto: consultas parametrizadas (sin inyección SQL), Argon2id (sin claves en texto plano/usables) y JWT firmado (sin tokens falsificables por servicios no autorizados).
  • La CI actúa como puerta obligatoria: ningún PR sin pasar Ruff + Black + mypy + pytest se mergea.
  • Los parámetros concretos de Argon2id y los tiempos de expiración de tokens quedan resueltos en el ADR 0020; el catálogo inicial de permisos por módulo quedó resuelto en el ADR 0014 (definido en el 0007).
  • El stack es común al resto del backend: cualquier decisión de este ADR que se cambie en un servicio debe replicarse aquí (o compartirse en el core/Autores ADR global).

Este ADR fija el cómo se construye svc-identidad; el ADR 0011 define el cómo se autentica (login, firma JWT, claves) y el ADR 0001 define el qué domina.