uecko-control-tiempos/docs/arquitectura/backend.md

48 lines
4.7 KiB
Markdown

# Arquitectura backend
## Objetivo
Definir responsabilidades, contratos y límites del backend. La dirección general de dependencias está en [visión general](vision-general.md).
## Capas y composición
Domain no debe conocer Application ni Infrastructure. Application debe depender de Domain, contratos y Core; no debe conocer Express, Sequelize ni modelos ORM. Infrastructure puede depender de Application, Domain y Core.
Cada módulo debe construir explícitamente sus dependencias en su composition root. No se permite Service Locator, DI implícita o mágica ni importar Infrastructure interna de otro módulo.
## Entrada, DTOs y contratos HTTP
Zod se establece como fuente única de verdad de los contratos HTTP. Los tipos deben inferirse mediante `z.infer<typeof Schema>` y no se mantienen interfaces manuales equivalentes. Cada endpoint debe disponer de DTOs de request y response; los decimales exactos no viajan como `number`.
Los schemas deben expresar semánticamente UUID, fechas, timestamps, porcentajes, importes y cantidades, y distinguir campo omitido, `null`, cadena vacía y colección vacía. No se mezclan validación de transporte y lógica de negocio; `.default()` y `.transform()` solo se usan en DTOs de negocio cuando estén justificados. Los subschemas solo se reutilizan cuando sean realmente estables y compartidos.
El flujo habitual es:
```text
RequestDTO → UseCase({ dto, context }) → InputMapper → Value Objects / Props
```
`RequestMapper → Command` es opcional y solo procede ante una adaptación HTTP real, como combinar `body`, `params` y `query`. No se crea solo para renombrar campos que Application volverá a transformar.
## Application y Domain
Los casos de uso deben orquestar, gestionar transacciones cuando corresponde, delegar en servicios de aplicación, devolver `Result` y construir respuestas mediante builders o mappers de salida. No contienen lógica HTTP, ORM, modelos Sequelize ni responsabilidades de controller, repositorio o Infrastructure; tampoco construyen agregados directamente desde DTOs HTTP.
Los servicios de aplicación deben ser pequeños, explícitos y de responsabilidad concreta. Se evitan nombres genéricos como `Manager`, `GenericService`, `CommandService`, `BaseRepository` o `Helper` sin semántica.
Se establecen los patrones del proyecto: `Result` para operaciones que pueden fallar, `Maybe` para ausencia semántica y `Collection` cuando el dominio requiere comportamiento propio para un conjunto homogéneo. Los fallos de negocio esperables no se representan mediante excepciones, `null`, arrays ni tipos ad hoc cuando corresponda uno de esos patrones.
`create()` representa creación funcional: valida invariantes, puede fallar y devuelve `Result`. `rehydrate()` reconstruye persistencia válida: no valida creación, no genera eventos y no devuelve `Result`. Infrastructure debe usar `rehydrate()`; Application y Domain deben usar `create()` para instancias nuevas. `update()` no usa ninguna de las dos, y los Value Objects se reconstruyen con `create()`.
## Repositorios, mappers y respuestas
Cada repositorio debe gestionar un agregado o responsabilidad concreta, no compartirse entre módulos ni exponerse como API pública. No se crean repositorios base genéricos sin necesidad demostrada. Los detalles de ORM deben permanecer en Infrastructure. Los mappers de persistencia e invariantes de almacenamiento se definen en [datos y persistencia](datos-persistencia.md).
Los snapshot builders deben materializar el DTO de salida exacto y tiparse con el tipo inferido del schema, de modo que un cambio incompatible del schema produzca un error de compilación. Son deterministas, sin lógica de negocio ni acceso a repositorios o Infrastructure; pueden transformar `Maybe` en `null`, Value Objects en primitivas y enums internos en valores del contrato. No se crean interfaces `I*Snapshot` que dupliquen DTOs.
## Errores, seguridad y API
Los errores esperables deben ser tipados y predecibles. Domain y Application se mapean a HTTP en Infrastructure sin exponer SQL, stack traces, detalles internos ni información sensible. Los errores deben conservar contexto suficiente para diagnosticar el fallo. No se ocultan fallos, no se convierten en éxito para continuar y el logging no sustituye su tratamiento. Las APIs y servicios deben mantener observabilidad suficiente para investigar fallos relevantes sin exponer información sensible.
Toda entrada externa debe validarse. La autorización se verifica además de la autenticación; no se confía en permisos, compañía o identificadores enviados por el cliente sin contrastarlos con el contexto autenticado. Las consultas y mutaciones deben preservar aislamiento multiempresa. La idempotencia se aplica a operaciones repetibles según su semántica.