# 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 { 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 { 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.