metis-desk/.agents/skills/rdx-solid/referencias/arquitectura-backend.md

177 lines
9.4 KiB
Markdown
Raw Normal View History

# Arquitectura backend
> Referencia especializada del skill `rdx-solid`. Consulta primero [arquitectura.md](arquitectura.md) y aplica las convenciones de `SKILL.md` y `AGENTS.md` del proyecto.
## 1. Propósito y alcance
Este documento orienta el diseño y evolución de servicios backend, APIs, procesos asíncronos, persistencia e integraciones. Está pensado principalmente para TypeScript y Node.js, pero sus principios son transferibles a otros entornos.
**Objetivo:** construir servicios correctos, seguros, verificables y mantenibles con una complejidad proporcional a sus requisitos. No impongas arquitecturas hexagonales, DDD ni una capa por concepto cuando no aporten valor.
## 2. Reconocer la arquitectura existente
Antes de modificar código:
1. Identifica framework, estructura modular, puntos de entrada y convenciones.
2. Localiza los casos de uso, modelos, contratos y consumidores afectados.
3. Examina gestión de errores, autenticación, persistencia y pruebas existentes.
4. Comprueba si el cambio afecta a contratos públicos, datos persistidos o transacciones.
5. Limita las modificaciones al ámbito de la tarea.
Para sistemas heredados, consulta además [arquitectura-legacy.md](arquitectura-legacy.md). Para decisiones estructurales importantes, consulta [arquitectura-decisiones.md](arquitectura-decisiones.md).
## 3. Organización modular y responsabilidades
Prefiere módulos funcionales cuando faciliten la cohesión, pero conserva la organización del proyecto si ya resulta adecuada. Un módulo puede combinar presentación, aplicación, dominio e infraestructura sin exigir directorios físicos para todas las capas.
```text
src/
modules/
orders/
order-routes.ts
create-order.ts
order.ts
order-repository.ts
postgres-order-repository.ts
order-contracts.ts
customers/
...
infrastructure/
database/
logging/
```
Esta distribución es ilustrativa. Divide un módulo cuando su tamaño o responsabilidades reales lo justifiquen, no porque falten directorios de una plantilla.
- **Transporte/presentación:** adapta HTTP, CLI, mensajería o RPC a entradas y respuestas de aplicación.
- **Aplicación:** coordina casos de uso, autorización específica y límites transaccionales.
- **Dominio:** aplica reglas, estados e invariantes.
- **Infraestructura:** implementa persistencia, proveedores e integraciones externas.
Evita controladores que contengan toda la lógica de negocio y servicios que solo deleguen a otros servicios sin responsabilidad concreta. No coloques en `shared` reglas de negocio cuya propiedad corresponde a un módulo.
## 4. APIs y contratos
- Diseña endpoints y operaciones alrededor de recursos y casos de uso comprensibles.
- Define formatos de entrada, salida y error explícitos; no expongas entidades de base de datos por conveniencia.
- Valida datos no confiables en el límite de entrada. Los tipos TypeScript no validan JSON en ejecución.
- Utiliza códigos HTTP, semántica de errores y cabeceras apropiados; no devuelvas éxito para ocultar fallos.
- Evalúa paginación, límites, filtrado y orden estable en listados potencialmente grandes.
- Considera versionado y compatibilidad cuando haya consumidores independientes.
- No presupongas que frontend y backend se despliegan simultáneamente.
```typescript
interface CreateOrderRequest {
customerId: string;
items: Array<{ productId: string; quantity: number }>;
}
async function createOrderHandler(request: HttpRequest): Promise<HttpResponse> {
const input = createOrderSchema.parse(request.body);
const result = await createOrder.execute(input);
return { status: 201, body: { orderId: result.orderId } };
}
```
El esquema, `HttpRequest`, `HttpResponse` y el caso de uso son colaboradores ilustrativos. Adapta los contratos a las herramientas del proyecto y no introduzcas validadores duplicados sin necesidad.
Consulta [arquitectura-shared.md](arquitectura-shared.md) al publicar tipos o esquemas consumidos por frontend.
## 5. Persistencia y transacciones
- Modela el acceso a datos según las necesidades del dominio; no crees repositorios genéricos de forma automática.
- Mantén SQL y detalles de drivers fuera de las reglas de negocio cuando su acoplamiento dificulte evolucionar el sistema.
- Utiliza transacciones cuando varias escrituras deban confirmarse o revertirse como una unidad.
- Diseña restricciones de integridad también en la base de datos cuando corresponda.
- Define migraciones reproducibles, compatibles y con procedimientos de recuperación cuando el riesgo lo exija.
- Evita consultas N+1, cargas excesivas e índices especulativos: mide y evalúa el uso real.
- No prometas atomicidad entre base de datos y servicios externos sin un mecanismo que la garantice.
```typescript
async function confirmOrder(orderId: OrderId): Promise<void> {
await database.transaction(async (tx) => {
const order = await orderRepository.findForUpdate(tx, orderId);
order.confirm();
await orderRepository.save(tx, order);
await outboxRepository.enqueue(tx, {
type: 'ORDER_CONFIRMED',
orderId: orderId.value,
});
});
}
```
El ejemplo requiere implementaciones transaccionales compatibles. Outbox es útil cuando hay que publicar eventos fiables tras una confirmación, pero no es obligatorio en sistemas que no lo necesitan.
## 6. Seguridad y aislamiento
- Distingue **autenticación** (identidad) de **autorización** (permiso para una operación o recurso).
- Autoriza en el servidor, incluso si la interfaz oculta acciones no permitidas.
- En sistemas multiempresa, comprueba el ámbito de datos y permisos en cada operación relevante; no confíes en un `tenantId` enviado por el cliente sin validación.
- Usa consultas parametrizadas y controles contra inyección.
- Protege secretos mediante mecanismos adecuados; nunca los incluyas en código ni registros.
- Valida límites de tamaño, cargas, archivos y tipos de contenido.
- Aplica privilegio mínimo a cuentas técnicas y conexiones.
- Gestiona información sensible y errores sin filtrar datos internos.
No elimines controles existentes para simplificar los casos de uso o las pruebas.
## 7. Asincronía, concurrencia y resiliencia
- Usa correctamente `async/await`; evita promesas no esperadas salvo trabajo explícitamente desacoplado y supervisado.
- Establece plazos de espera, cancelación y tratamiento de fallos en llamadas remotas.
- Diseña reintentos solo para operaciones seguras o idempotentes, con límites y política de espera.
- Considera claves de idempotencia para operaciones que puedan repetirse por red o colas.
- Evita operaciones bloqueantes costosas en el hilo de eventos de Node.js.
- Define consistencia, orden, duplicados y errores para consumidores de mensajes.
- Protege cambios concurrentes cuando pueden vulnerar invariantes (bloqueos, versiones u otras técnicas adecuadas).
- Distingue fallos transitorios de fallos permanentes; no reintentes indiscriminadamente.
## 8. Observabilidad y operación
- Usa logging estructurado donde aporte utilidad diagnóstica.
- Conserva identificadores de correlación cuando existan flujos entre servicios.
- Expón métricas y trazas proporcionales a la criticidad y operación.
- Prepara comprobaciones de salud que reflejen correctamente el estado del servicio.
- Cierra conexiones y recursos de forma controlada.
- Evita registrar credenciales, tokens, datos personales o grandes cargas completas.
- Define procedimientos de despliegue, migración y reversión cuando correspondan.
## 9. Pruebas y verificación
Elige pruebas según el riesgo, no por cuotas fijas:
- **Dominio:** reglas, invariantes y casos límite mediante pruebas rápidas.
- **Aplicación:** coordinación, permisos, tratamiento de errores y transacciones.
- **Infraestructura:** integración real con base de datos, consultas y adaptadores.
- **API:** validación, autenticación, autorización y contratos.
- **E2E:** flujos críticos y efectos entre componentes.
Prueba especialmente los casos de concurrencia, idempotencia y fallo parcial cuando existan. Consulta [testing.md](testing.md) y [tdd.md](tdd.md).
## 10. Señales de alerta
- Controladores con reglas de negocio extensas.
- Entidades que importan directamente controladores o detalles de HTTP.
- Transacciones repartidas entre operaciones sin un responsable definido.
- Servicios que acceden a datos de otra empresa sin comprobar ámbito y permisos.
- Promesas sin supervisión y errores capturados y descartados.
- Operaciones de pago, envío o notificación repetibles sin garantías definidas.
- Repositorios, interfaces y capas creados únicamente por plantilla.
- Un cambio pequeño que obliga a modificar muchos módulos sin motivo real.
Cada señal exige análisis, no refactorización automática.
## 11. Lista de comprobación
- [ ] ¿He identificado el contrato y los consumidores afectados?
- [ ] ¿Las reglas de negocio están en el componente apropiado?
- [ ] ¿Las entradas externas se validan en ejecución?
- [ ] ¿La autorización se comprueba en el servidor y en el ámbito correcto?
- [ ] ¿Las escrituras y llamadas externas tienen garantías claras ante errores?
- [ ] ¿He considerado concurrencia e idempotencia cuando corresponden?
- [ ] ¿Se respeta el alcance y la estructura del proyecto?
- [ ] ¿Se han ejecutado las verificaciones pertinentes y comunicado las pendientes?
**Principio final:** el backend debe preservar invariantes, contratos y seguridad con el mínimo acoplamiento y complejidad justificados.