# Arquitectura de software (Software Architecture) La arquitectura de software define cómo se organizan los componentes de un sistema, qué responsabilidades asumen, cómo colaboran y qué dependencias existen entre ellos. Su objetivo es facilitar el desarrollo, mantenimiento y evolución del software, respetando sus requisitos funcionales y no funcionales. Este documento desarrolla los principios establecidos en el `SKILL.md` principal y constituye el punto de entrada a las recomendaciones arquitectónicas especializadas. **La arquitectura debe adaptarse a las necesidades reales del proyecto. No existe un estilo arquitectónico universalmente adecuado para todos los sistemas.** ## 1. Objetivos de la arquitectura Una arquitectura adecuada debe permitir: 1. **Añadir** funcionalidades con un esfuerzo razonable. 2. **Modificar** comportamientos existentes de forma segura. 3. **Eliminar** funcionalidades sin provocar dependencias innecesarias. 4. **Probar** los comportamientos mediante mecanismos de verificación apropiados. 5. **Desplegar** el sistema de forma fiable, incluyendo despliegues independientes cuando sean necesarios. 6. **Comprender** las responsabilidades y relaciones entre componentes. 7. **Evolucionar** el sistema sin introducir complejidad accidental desproporcionada. También debe responder a los requisitos no funcionales relevantes: - Seguridad. - Rendimiento. - Escalabilidad. - Disponibilidad. - Integridad y consistencia de datos. - Observabilidad. - Compatibilidad. - Recuperación ante fallos. - Coste de operación y mantenimiento. **La calidad arquitectónica debe evaluarse respecto a los requisitos del sistema, no por la cantidad de capas, módulos, interfaces o patrones utilizados.** --- ## 2. Principios arquitectónicos generales ### 2.1. Responsabilidades claras Organiza el sistema mediante componentes con responsabilidades coherentes. - Agrupa los elementos que evolucionen conjuntamente. - Separa responsabilidades que cambien por motivos independientes. - Evita componentes que acumulen funcionalidades sin relación clara. - No fragmentes el código en clases o módulos que no aporten una responsabilidad real. Aplica el principio de responsabilidad única (SRP) con criterio profesional. ### 2.2. Cohesión y acoplamiento Favorece componentes con alta cohesión y dependencias controladas. - Mantén próximos los elementos relacionados. - Evita dependencias innecesarias entre funcionalidades. - Reduce el conocimiento de detalles internos ajenos. - Define contratos cuando aporten estabilidad o desacoplamiento. - Evita interfaces y capas que únicamente deleguen operaciones sin añadir valor. **El objetivo no es eliminar todas las dependencias, sino mantener únicamente las necesarias y apropiadas.** ### 2.3. Organización modular Utiliza límites modulares cuando faciliten la comprensión y evolución del sistema. Una organización puede basarse en: - Funcionalidades. - Dominios de negocio. - Responsabilidades técnicas. - Capas. - Subsistemas. - Una combinación de estos criterios. Prefiere la organización por funcionalidades cuando permita mantener cohesionados los componentes relacionados. No obstante: - No reorganices automáticamente un proyecto existente. - No impongas módulos independientes para operaciones triviales. - No introduzcas estructuras de directorios que no aporten claridad. - Respeta los límites y convenciones existentes. ### 2.4. Separación de responsabilidades técnicas Distingue las responsabilidades de presentación, aplicación, dominio e infraestructura cuando el sistema lo justifique. - **Presentación:** interacción con usuarios o consumidores. - **Aplicación:** coordinación de casos de uso. - **Dominio:** reglas, conceptos e invariantes de negocio. - **Infraestructura:** persistencia, comunicaciones e integraciones externas. Estas responsabilidades no obligan a crear cuatro capas físicas en todas las aplicaciones. **La separación conceptual es más importante que la cantidad de directorios o clases utilizados.** ### 2.5. Dirección de las dependencias Evita que las reglas de negocio dependan innecesariamente de detalles técnicos. En arquitecturas limpias, hexagonales o equivalentes: - Las dependencias del código deben orientarse hacia las políticas de mayor nivel. - La infraestructura puede implementar contratos definidos en la aplicación o el dominio. - Los componentes internos deben permanecer desacoplados de tecnologías externas cuando corresponda. Distingue entre: - **Dependencias del código:** qué módulos conocen y utilizan otros módulos. - **Flujo de ejecución:** qué componentes intervienen al ejecutar una operación. Ambos pueden seguir direcciones diferentes. No introduzcas interfaces únicamente para cumplir formalmente una regla de dependencias. ### 2.6. Contratos y compatibilidad Los contratos delimitan cómo colaboran los componentes. Pueden expresarse mediante: - Interfaces y tipos. - Funciones. - APIs. - Eventos. - Esquemas de datos. - Protocolos. - Pruebas de contrato. Antes de modificar un contrato público: 1. Identifica sus consumidores. 2. Comprueba su comportamiento actual. 3. Evalúa el impacto del cambio. 4. Mantén la compatibilidad requerida. 5. Define una estrategia de migración cuando sea necesaria. 6. Verifica las implementaciones afectadas. No presupongas que compartir una interfaz garantiza que dos implementaciones sean funcionalmente sustituibles. ### 2.7. Arquitectura proporcional Selecciona la estructura arquitectónica según los requisitos reales. No impongas automáticamente: - Clean Architecture. - Arquitectura hexagonal. - Microservicios. - Arquitectura orientada a eventos. - Múltiples capas de servicios. - Contenedores de inyección de dependencias. - Repositorios para cualquier acceso a datos. Estos mecanismos pueden aportar ventajas cuando existe una necesidad identificada. **Una arquitectura sencilla y modular puede ser preferible a una estructura más sofisticada que incremente innecesariamente el coste de mantenimiento.** --- ## 3. Estilos arquitectónicos Conoce los estilos arquitectónicos habituales y selecciona el más adecuado según el contexto. | Estilo | Características | Cuándo considerarlo | |---|---|---| | **Arquitectura por capas** | Organiza responsabilidades técnicas en capas. | Aplicaciones con flujos y responsabilidades relativamente sencillos. | | **Organización por funcionalidades** | Agrupa los componentes relacionados con una capacidad. | Sistemas con funcionalidades que evolucionan independientemente. | | **Monolito modular** | Módulos cohesionados dentro de una unidad de despliegue. | Aplicaciones empresariales que necesitan modularidad sin distribución operativa. | | **Arquitectura hexagonal** | Utiliza puertos y adaptadores para desacoplar mecanismos externos. | Sistemas con integraciones o dependencias de infraestructura relevantes. | | **Clean Architecture** | Separa políticas de negocio y detalles técnicos mediante límites explícitos. | Sistemas cuyo dominio y necesidades de evolución justifican esa separación. | | **Microservicios** | Distribuye capacidades en servicios desplegables independientemente. | Sistemas con necesidades reales de autonomía, escalabilidad u operación distribuida. | | **Arquitectura orientada a eventos** | Comunica componentes mediante eventos. | Procesos asíncronos o desacoplamiento temporal justificado. | Los estilos no son necesariamente excluyentes. Por ejemplo, un monolito modular puede organizarse por funcionalidades y aplicar principios hexagonales en determinados módulos. ### Criterios de selección Antes de adoptar una arquitectura: - Identifica los requisitos funcionales. - Evalúa los requisitos no funcionales. - Considera el tamaño y la evolución prevista del sistema. - Analiza las capacidades del equipo. - Evalúa el coste de desarrollo y operación. - Considera los riesgos de migración. - Comprueba si la arquitectura existente ya satisface las necesidades. No cambies de estilo arquitectónico únicamente por tendencias tecnológicas. --- ## 4. Arquitectura en proyectos existentes Al trabajar sobre un proyecto existente: 1. Identifica su estructura y estilo arquitectónico actual. 2. Examina sus módulos, contratos y convenciones. 3. Comprende el comportamiento que debe preservarse. 4. Localiza los componentes directamente afectados. 5. Evalúa el impacto de las modificaciones. 6. Introduce cambios incrementales. 7. Verifica los resultados. ### Reglas de evolución - Respeta los límites existentes salvo que exista una necesidad justificada de modificarlos. - No introduzcas nuevas capas para una modificación puntual si no son necesarias. - No combines cambios funcionales con reestructuraciones arquitectónicas extensas. - Evita modificar componentes ajenos al alcance. - Mantén compatibilidad con consumidores existentes cuando corresponda. - Documenta las decisiones arquitectónicas significativas. **Una arquitectura existente debe mejorarse para resolver problemas reales, no sustituirse automáticamente por otra considerada más moderna.** --- ## 5. Documentación arquitectónica especializada Las recomendaciones específicas se encuentran distribuidas en documentos independientes. **Consulta únicamente los documentos relevantes para la tarea, combinándolos cuando sea necesario.** ### 5.1. Arquitectura backend **[arquitectura-backend.md](arquitectura-backend.md)** Consulta este documento cuando trabajes con: - Servicios backend. - Casos de uso. - Lógica de dominio. - APIs. - Persistencia. - Transacciones. - Autenticación y autorización del servidor. - Procesamiento asíncrono. - Eventos y mensajería. - Integraciones externas. - Observabilidad y operaciones. Incluye recomendaciones para organizar módulos y servicios backend sin introducir capas o abstracciones innecesarias. ### 5.2. Arquitectura frontend **[arquitectura-frontend.md](arquitectura-frontend.md)** Consulta este documento cuando trabajes con: - Componentes de interfaz. - Aplicaciones React u otros frameworks frontend. - Organización por funcionalidades. - Hooks y lógica de presentación. - Gestión del estado. - Formularios y cuadros de diálogo. - Consumo de APIs. - Internacionalización. - Accesibilidad. - Rendimiento y pruebas de interfaz. Incluye criterios para evitar componentes monolíticos y separar responsabilidades sin trasladar innecesariamente patrones propios del backend. ### 5.3. Código compartido entre backend y frontend **[arquitectura-shared.md](arquitectura-shared.md)** Consulta este documento cuando trabajes con: - Paquetes compartidos. - Monorepos. - Tipos y contratos utilizados por frontend y backend. - Esquemas de validación. - DTO. - Enumeraciones contractuales. - Utilidades comunes. - Generación de tipos. - Compatibilidad entre aplicaciones. - Dependencias entre paquetes. Su objetivo es permitir una reutilización segura sin acoplar el frontend a implementaciones internas del backend. ### 5.4. Arquitectura de sistemas heredados **[arquitectura-legacy.md](arquitectura-legacy.md)** Consulta este documento cuando trabajes con: - Código legado. - Refactorización de sistemas existentes. - Modernización de aplicaciones. - Migraciones tecnológicas. - Sustitución progresiva de componentes. - Compatibilidad con contratos antiguos. - Migraciones de persistencia. - Pruebas de caracterización. - Adaptadores y capas anticorrupción. - Convivencia entre sistemas antiguos y nuevos. Su prioridad es preservar el comportamiento y controlar el riesgo durante la evolución del software. ### 5.5. Decisiones arquitectónicas **[arquitectura-decisiones.md](arquitectura-decisiones.md)** Consulta este documento cuando necesites: - Seleccionar tecnologías. - Comparar alternativas arquitectónicas. - Introducir dependencias importantes. - Diseñar nuevos módulos. - Modificar límites existentes. - Evaluar requisitos no funcionales. - Analizar costes y compromisos. - Documentar decisiones mediante ADR. - Planificar cambios arquitectónicos significativos. No es necesario consultarlo para modificaciones rutinarias que no impliquen decisiones arquitectónicas relevantes. --- ## 6. Selección de referencias según la tarea Antes de aplicar recomendaciones especializadas: 1. Identifica el ámbito del cambio. 2. Determina qué componentes y contratos están afectados. 3. Consulta los documentos correspondientes. 4. Combina las recomendaciones cuando la tarea abarque varios ámbitos. 5. Aplica las convenciones específicas del proyecto. 6. Evita trasladar reglas de un ámbito a otro sin analizar su adecuación. ### Ejemplos | Tarea | Referencias recomendadas | |---|---| | Crear un endpoint backend | `arquitectura-backend.md` | | Refactorizar un componente React | `arquitectura-frontend.md` | | Compartir un esquema de validación | `arquitectura-shared.md`, `arquitectura-backend.md` y `arquitectura-frontend.md` | | Migrar un servicio heredado | `arquitectura-legacy.md` y `arquitectura-backend.md` | | Modificar un contrato consumido por frontend y backend | `arquitectura-shared.md`, `arquitectura-backend.md` y `arquitectura-frontend.md` | | Elegir una nueva base de datos | `arquitectura-backend.md` y `arquitectura-decisiones.md` | | Rediseñar límites entre módulos | `arquitectura-decisiones.md` y los documentos correspondientes a los módulos afectados | | Modernizar una aplicación frontend heredada | `arquitectura-legacy.md` y `arquitectura-frontend.md` | Los documentos especializados son complementarios, no categorías excluyentes. --- ## 7. Relación con otros documentos del skill La documentación de arquitectura debe centrarse en: - Organización del sistema. - Límites entre componentes. - Dependencias. - Contratos. - Selección de estilos. - Evolución arquitectónica. Para profundizar en otros aspectos, utiliza: - **[principios-solid.md](principios-solid.md):** principios SOLID y diseño de dependencias. - **[diseño-de-objetos.md](diseño-de-objetos.md):** responsabilidades, entidades, objetos de valor y agregados. - **[patrones-de-diseño.md](patrones-de-diseño.md):** patrones de diseño y criterios para aplicarlos. - **[complejidad.md](complejidad.md):** control de la complejidad y prevención de sobreingeniería. - **[code-smells.md](code-smells.md):** detección de problemas estructurales. - **[testing.md](testing.md):** estrategias y niveles de pruebas. - **[tdd.md](tdd.md):** desarrollo guiado por pruebas. Evita duplicar explicaciones completas de estos conceptos dentro de los documentos arquitectónicos. --- ## 8. Señales de alerta arquitectónicas Revisa la arquitectura cuando detectes: - Dependencias circulares problemáticas. - Reglas de negocio acopladas innecesariamente a infraestructura. - Módulos con responsabilidades poco claras. - Cambios pequeños que requieren modificar numerosos componentes. - Dependencias sobre detalles internos de otros módulos. - Estado mutable compartido difícil de controlar. - Contratos públicos inestables. - Paquetes compartidos que acumulan responsabilidades inconexas. - Capas que únicamente delegan sin aportar valor. - Abstracciones destinadas a requisitos hipotéticos. - Duplicación de conocimiento entre módulos. - Dificultad para verificar comportamientos relevantes. Estas señales justifican un análisis, pero no obligan automáticamente a realizar una refactorización. **La refactorización debe estar motivada por un problema real y mantenerse dentro del alcance autorizado.** --- ## 9. Lista de comprobación arquitectónica ### Antes de implementar - [ ] ¿Comprendo los requisitos y restricciones del sistema? - [ ] ¿He identificado la arquitectura existente? - [ ] ¿Conozco los módulos y contratos afectados? - [ ] ¿He consultado las referencias especializadas necesarias? - [ ] ¿La solución propuesta es proporcional al problema? - [ ] ¿Estoy evitando abstracciones innecesarias? ### Durante la implementación - [ ] ¿Las responsabilidades permanecen cohesionadas? - [ ] ¿Las dependencias están justificadas? - [ ] ¿Se respetan los contratos existentes? - [ ] ¿Estoy evitando cambios arquitectónicos ajenos a la tarea? - [ ] ¿Las modificaciones mantienen la compatibilidad requerida? - [ ] ¿La implementación sigue las convenciones del proyecto? ### Al finalizar - [ ] ¿Se satisfacen los requisitos? - [ ] ¿Se ha preservado el comportamiento que debía mantenerse? - [ ] ¿Se han ejecutado las verificaciones pertinentes? - [ ] ¿Los límites entre módulos siguen siendo coherentes? - [ ] ¿He evitado introducir complejidad accidental? - [ ] ¿Se han documentado las decisiones arquitectónicas significativas? - [ ] ¿He comunicado las limitaciones y riesgos pendientes? --- ## Principio final > "La arquitectura debe facilitar la evolución del software, no imponer una complejidad superior a la que requieren sus necesidades." Selecciona estructuras, módulos, capas y contratos según los requisitos y el contexto del proyecto. Respeta la arquitectura existente y modifica sus límites únicamente cuando exista una razón técnica o funcional justificada. **La mejor arquitectura es aquella que satisface los requisitos, mantiene responsabilidades claras, controla las dependencias y permite evolucionar el sistema de forma segura con la menor complejidad accidental razonable.**