Dependency Structure
Pregunta y alcance
Dependency Structure responde qué dependencias arquitectónicamente relevantes existen entre unidades, cuáles permitimos y en qué dirección. Una flecha A → B expresa que A depende de alguna propiedad, contrato o capacidad de B. Sin identificar la relación observada, el dibujo no permite evaluar cambio ni continuidad.
decomposition establece unidades y fronteras; este eje examina obligaciones que las conectan. communication describe interacción e intercambio. Dividir Commerce en Orders, Payments, Inventory y Shipping no decide todavía qué detalles puede conocer cada parte ni qué necesita disponible para completar trabajo.
El objetivo no es minimizar flechas. Es hacer visibles restricciones relevantes, proteger decisiones que deberían permanecer locales y reconocer dependencias necesarias. Una arquitectura con pocas relaciones dibujadas puede esconder conocimiento compartido en tablas, configuración o acuerdos de release.
Elegir la relación y el nivel
Un gráfico útil declara qué significa su flecha, qué unidades representa y qué frontera se revisa. Dependencia de código y necesidad de una capacidad en ejecución no son intercambiables. Tampoco tienen siempre la misma granularidad: un paquete, un módulo de negocio y un proceso representan niveles diferentes.
Podemos investigar, según el problema:
| Relación | Ejemplo que interesa revisar |
|---|---|
| Código fuente / compilación | Orders importa un contrato o tipo de Payments. |
| Runtime | Completar una operación requiere un resultado de Payments. |
| Contrato | Un consumidor interpreta estados y garantías de una API o evento. |
| Datos / schema | Inventory conoce columnas privadas de otro módulo. |
| Deployment | Una versión necesita que otra se entregue primero. |
| Temporal | Un flujo necesita que cierto participante responda en una ventana. |
| Organizacional | Un cambio requiere decisiones o trabajo de otro equipo. |
No es una taxonomía universal de coupling. Seleccionar relaciones depende del cambio o fallo que queremos comprender. Mezclarlas en un solo grafo sin distinguirlas puede sugerir independencia donde solo desapareció una referencia de código.
Dependencias frente a flujo de ejecución
En un ejemplo propio, Orders Application necesita autorizar un pago. Puede depender de un PaymentPort definido en su frontera; un adaptador implementa ese contrato e integra Payments.
Dependencias de código fuente:
Orders Application → PaymentPort
Payment Adapter → PaymentPort
Flujo de ejecución ilustrativo:
Orders Application → Payment Adapter → PaymentsEl punto de composición externo suministra la implementación. Application no necesita importar el tipo concreto del adaptador para ejecutarlo a través del contrato. Pero sí conserva una necesidad operacional de pago y expectativas sobre su resultado.
La formulación original de Clean Architecture distingue precisamente dirección de dependencias de código y flujo de control a través de boundaries. El ejemplo no obliga a adoptar Clean: demuestra por qué dependency graph no equivale a runtime call graph.
Una llamada puede regresar un resultado sin crear una referencia de código en sentido contrario. Un callback puede invertir control mediante un contrato ya conocido. Hay que observar imports, tipos, ensamblaje y garantías, no deducir la dependencia únicamente del orden de ejecución.
Contratos públicos y conocimiento privado
Orders puede depender del contrato público de Payments: identificador de operación, importe, moneda y estados admitidos. Es diferente de conocer tablas internas, códigos del proveedor o clases concretas que Payments quería poder reemplazar.
interfaces permite hacer explícita la superficie de la que depende un consumidor. El contrato público también crea dependencia. Su estabilidad no significa ausencia de coupling: cambiar semántica de autorizado, omitir una garantía o alterar compatibilidad puede afectar al consumidor aunque ningún método desaparezca. SEI: Documenting Interfaces trata documentación semántica además de sintaxis de interfaces.
Una abstracción útil conserva una obligación significativa y oculta decisiones que no necesita el consumidor. Una interfaz que reproduce cada detalle del proveedor cambia de forma, no necesariamente de exposición. Introducir interfaces para todas las clases puede aumentar indirection y mantenimiento sin proteger una variación real.
Dirección y reglas permitidas
layered-architecture permite expresar restricciones entre responsabilidades técnicas. Una variante puede permitir Presentation → Application → Domain; otra conoce Data Access concreto desde negocio. Las reglas necesitan especificar qué relaciones se autorizan, incluidas excepciones y bypasses.
Con inversión, Application conoce un contrato interior e Infrastructure lo implementa. Eso cambia qué parte importa conocimiento tecnológico. No elimina persistencia ni obliga a que el flujo de ejecución avance solamente hacia adentro.
ports-and-adapters y clean-architecture ofrecen ejemplos de protección del núcleo y orientación de dependencias. Son formulaciones de estructura interna, no sinónimos de este eje general. Carpetas hexagonales o capas concéntricas no demuestran cumplimiento: mirar accesos, tipos y construcción de colaboradores.
Fowler separa configuración y uso de colaboradores en Dependency Injection. Un contenedor puede participar en el ensamblaje; no impone por sí solo la dirección elegida. Application que instancia el adaptador concreto sigue conociéndolo aunque después lo guarde en una variable de interfaz.
Dependencias directas y transitivas
Si A depende de B y B de C, conviene investigar si C condiciona indirectamente a A. La propagación depende del tipo de relación y las garantías que B preserve; no todas las cadenas tienen el mismo impacto.
Si Orders importa un contrato estable y Payments cambia una representación privada manteniendo ese contrato, el cambio puede quedarse en Payments. Si la representación aparece en respuestas que Orders interpreta, la frontera ya no protege esa variación.
En runtime, Payments podría necesitar una integración externa. Una degradación puede afectar pedidos que esperan autorización y no consultas que usan estado ya registrado. La cadena debe indicar población, operación y alternativas disponibles. Una relación transitiva potencial no demuestra que todo el sistema falle siempre junto.
Dependencias mediante datos compartidos
Supongamos que Inventory consulta tablas privadas de Payments para decidir si conserva una reserva. No existe una llamada a Payments API y sí conocimiento de su schema, estados y migraciones. Un cambio de columna o significado puede alcanzar Inventory.
Orders y Shipping pueden parecer independientes por compilar separados mientras usan el mismo stored procedure o asumen que una migración se aplica simultáneamente. La dependencia no desaparece porque el diagrama de llamadas esté vacío.
Compartir infraestructura de almacenamiento no equivale automáticamente a compartir todos los modelos y permisos. Hay que identificar accesos efectivos, contratos y autoridad. data-ownership desarrolla quién define y cambia estado autoritativo; este eje examina qué conocimiento y obligaciones crea el acceso, sin resolver ambas preguntas mediante una regla universal de base de datos por servicio.
Ciclos y acyclicidad
Un ciclo aparece cuando, para una relación definida, A → B, B → C y C → A. Puede dificultar aislamiento de cambios, inicialización, build o release, y volver confusa la responsabilidad. El efecto depende de alcance, lenguaje, relación y mecanismos.
Una estructura acíclica puede facilitar orden y razonamiento. No autoriza afirmar que todas las arquitecturas deban ser DAGs. Una colaboración bidireccional en runtime puede coexistir con dependencias de código acíclicas; responder al solicitante no demuestra un ciclo de imports.
Tampoco acyclicidad demuestra bajo coupling. Un módulo central con muchos consumidores puede concentrar cambios críticos aunque no haya ciclos. Conviene examinar qué conocimiento comparten, qué decisiones quedan expuestas y cuánto trabajo requiere alterar el contrato.
Si un ciclo causa un problema concreto, revisar responsabilidades, intermediación o inversión puede ayudar. Romper una flecha con un broker y conservar las mismas expectativas semánticas no garantiza que se haya eliminado la dependencia importante.
Procesos separados y contratos remotos
microservices puede reemplazar una referencia de compilación por dependencia de API o schema de eventos. Orders que consume PaymentAuthorized necesita interpretar su significado y compatibilidad. La ausencia de project reference no implica ausencia de dependencia.
architectural-quantum [ADVANCED] distingue requisitos estructurales para funcionar de coupling durante un workflow. Esa lente permite examinar independencia efectiva sin equiparar todos los imports, dependencias operacionales y llamadas remotas.
HTTP no crea independencia y eventos no eliminan coupling. Puede reducirse conocimiento directo de la implementación mientras permanecen necesidad de resultados, versiones compatibles y acuerdos entre equipos. Una frontera de red cambia mecanismos; no borra obligaciones.
En un modular-monolith, contratos locales también pueden proteger conocimiento privado. La comparación con servicios necesita datos, cambios y relaciones reales, no declarar que las dependencias locales son fuertes y las remotas débiles por definición.
Ejemplo de revisión y compromisos
architecture-erosion aparece como pregunta si la realización debilita dependencias intencionalmente restringidas. fitness-functions puede comprobar relaciones representadas mediante análisis estático o tests, sin capturar por sí sola dependencias dinámicas. Comparar contra intención vigente evita rechazar una reorganización justificada con reglas obsoletas.
Manteniendo las cuatro responsabilidades de Commerce, contrastemos Orders → implementación concreta de Payments con Orders → PaymentPort y un adaptador exterior. La segunda alternativa puede localizar integración y pruebas; agrega contratos, traducciones, ensamblaje y mantenimiento. No reduce automáticamente necesidad operacional ni costo total.
modifiability permite evaluar propagación de un cambio: incorporar otro proveedor sin alterar consumidores de estados preservados. deployability permite comprobar si modificar un contrato exige entregas coordinadas. Los resultados requieren escenarios y evidencia, no contar interfaces.
Para revisar la estructura, elegir una relación y registrar consumidores, contrato, accesos prohibidos y consecuencias de cambio o ausencia. Inspeccionar código y datos, contrastar con runtime y verificar compatibilidad. Menos flechas no es una medida de calidad; dependencias explícitas y justificadas permiten explicar el compromiso y revisarlo.