Read in English · Lire en français · Auf Deutsch lesen
Sección 3.4 de 3.8 · Intermedio · 25 min de lectura
Gestionar las dependencias: dirección, ciclos y estabilidad
Videolección
5 min · narrado en inglés, con subtítulosSolo escuchar (audio)
Leer la transcripción
Toda dependencia tiene una dirección, y esa dirección decide a quién afectan los cambios de quién. En esta lección aprenderás tres principios para las dependencias entre componentes, las métricas que los respaldan y cómo comprobarlos automáticamente.
Piensa en un edificio. Los pisos superiores descansan sobre los inferiores, y los cimientos no descansan sobre nada de lo que tienen encima. Puedes redecorar el último piso sin tocar los cimientos. Pero si excavas bajo los cimientos, todo lo que hay encima corre peligro. Ahora imagina dos pisos que se sostienen el uno al otro. Nunca podrías reconstruir ninguno de los dos. El buen software coloca las partes que cambian a menudo encima de partes sólidas que rara vez cambian, y nunca deja que dos partes se sostengan mutuamente en un bucle.
Dibuja una dependencia como una flecha del módulo que necesita algo al módulo que necesita: de Pago a Precios. Si Precios cambia, puede que Pago tenga que cambiar. Si Pago cambia, a Precios no le afecta. Los cambios viajan en sentido contrario a las flechas. Entre servicios, la dirección se manifiesta como conocimiento. Si Pedidos llama a la API de Facturación, Pedidos depende de Facturación. Si Pedidos publica un evento que Facturación consume, la flecha se invierte, y Pedidos ni siquiera sabe que Facturación existe.
Robert C. El primer principio de Martin es el Principio de dependencias acíclicas: ningún ciclo entre componentes. Aquí, Pedidos llama a Notificaciones para enviar una confirmación, y Notificaciones vuelve a llamar a Pedidos para leer el pedido. Ninguno puede compilarse, probarse ni lanzarse sin el otro. El primer arreglo invierte una dependencia. Notificaciones declara una pequeña interfaz, OrderLookup, que describe exactamente lo que necesita, y Pedidos la implementa. Ahora Notificaciones no depende de nada de Pedidos. La interfaz pertenece al módulo que la usa. El segundo arreglo extrae lo que necesitan ambas partes, aquí los eventos de pedidos, a un componente nuevo del que dependen las dos. Ese componente no depende de ninguna, así que es más estable que cualquiera de ellas.
El segundo principio es el Principio de dependencias estables: depende en la dirección de la estabilidad. Un componente pensado para ser fácil de cambiar no debería tener como dependiente a uno difícil de cambiar. Martin lo mide como inestabilidad: las dependencias salientes divididas entre todas las dependencias, entrantes más salientes. Cero significa solo dependencias entrantes: máximamente estable, y responsable ante muchos. Uno significa solo salientes: nada depende de él, así que es libre de cambiar. Así que, a lo largo de cada flecha, la inestabilidad debería bajar. Cada componente debería depender de componentes más estables que él.
El tercer principio es el Principio de abstracciones estables: un componente debe ser tan abstracto como estable. El grado de abstracción es la proporción de sus clases que son clases abstractas o interfaces. Representa el grado de abstracción frente a la inestabilidad y obtienes este cuadrado. La diagonal es la secuencia principal: estable y abstracto arriba a la izquierda, inestable y concreto abajo a la derecha. La distancia a ella va de cero, sobre la línea, a uno, en una esquina lejana. Cerca de la esquina inferior izquierda está la zona de dolor: estable, porque mucho depende de ello, pero concreto, así que los cambios son necesarios y dolorosos. Un esquema de base de datos que todos los módulos leen directamente vive aquí. Cerca de la esquina superior derecha está la zona de inutilidad: abstracciones de las que nada depende, a menudo interfaces que quedaron tras una refactorización.
Probémoslo con una tienda. Su componente payments-api tiene seis dependencias entrantes y una saliente, así que la inestabilidad es un séptimo, unos cero punto catorce. Cuatro de sus cinco clases son interfaces. Está casi exactamente sobre la secuencia principal. orders-core tiene ocho entrantes y dos salientes, así que la inestabilidad es cero punto dos. Pero solo dos de sus veinte clases son abstractas, así que su distancia a la secuencia principal es cero punto siete. Estable, pero concreto: está en la zona de dolor. La interfaz de usuario web depende de otros nueve y nada depende de ella. Totalmente inestable, totalmente concreta y justo sobre la línea. El remedio traslada los tipos que usan los dependientes de orders-core a un componente pequeño y mayoritariamente abstracto, y deja el código concreto en otro aparte. Ambos acaban a una distancia de cero punto veinticinco. Los números son invitaciones a mirar, no objetivos.
La organización en capas es la regla de dependencia más antigua. En la organización en capas estricta, cada capa solo puede usar la capa inmediatamente inferior. Cada capa oculta todo lo que tiene debajo, pero las capas intermedias pueden limitarse a reenviar llamadas. En la organización en capas relajada, una capa puede usar cualquier capa inferior. Eso elimina el código de reenvío, pero un cambio abajo del todo puede afectar a todas las capas superiores. Elijas las reglas que elijas, compruébalas automáticamente. Herramientas como ArchUnit para Java, dependency-cruiser para JavaScript e import-linter para Python se ejecutan en la compilación y la hacen fallar cuando se incumple una regla.
La sección completa tiene reglas de ejemplo para cada herramienta y una guía de decisión para corregir una dependencia no deseada. A continuación, en la Sección tres punto cinco, aplicamos los principios SOLID a componentes y servicios. Nos vemos allí.
Lo que necesitas saber
- Una dependencia apunta del módulo que necesita algo al módulo que necesita; los cambios fluyen en sentido contrario a las flechas, por lo que la dirección de las dependencias decide a quién afectan los cambios de quién.
- El Principio de dependencias acíclicas prohíbe los ciclos entre componentes, porque todos los componentes de un ciclo deben compilarse, probarse y lanzarse juntos.
- El Principio de dependencias estables dice que hay que depender en la dirección de la estabilidad; la inestabilidad es I = Ce / (Ca + Ce), de 0 (solo dependencias entrantes) a 1 (solo salientes).
- El Principio de abstracciones estables dice que los componentes estables deben ser abstractos; el grado de abstracción A es el número de tipos abstractos dividido entre el total de tipos, y la distancia a la secuencia principal es D = |A + I - 1|.
- Los ciclos se rompen mediante la inversión de dependencias (el módulo del que se depende es dueño de una interfaz que el otro implementa) o extrayendo la parte compartida a un componente nuevo y más estable.
- La organización en capas estricta solo permite llamadas a la capa inmediatamente inferior, y la relajada, a cualquier capa inferior; ambas, y cualquier otra regla de dependencia, deben comprobarse automáticamente con herramientas como ArchUnit, dependency-cruiser o import-linter.
Por qué importa la dirección
Existe una dependencia cuando un módulo necesita otro para compilarse, ejecutarse o comportarse correctamente. Dibújala como una flecha del módulo que depende al módulo del que depende: de Pago a Precios. La flecha te dice algo importante sobre el cambio. Si Precios cambia su interfaz o su comportamiento, puede que Pago tenga que cambiar; si Pago cambia, a Precios no le afecta. Los cambios viajan en sentido contrario a las flechas. Así que la dirección de cada dependencia es una decisión sobre quién se verá afectado por los cambios de quién.
A los arquitectos les importan dos tipos de dirección que es fácil confundir. El flujo de control es quién llama a quién en tiempo de ejecución. La dependencia de código fuente es quién importa, referencia o se compila contra quién. Normalmente apuntan en el mismo sentido, pero no tienen por qué. Un framework llama a tu código sin depender de él (la Sección 0.3 lo llamó inversión de control), y, con una interfaz, un módulo de alto nivel puede llamar a uno de bajo nivel mientras la dependencia de código fuente apunta en sentido contrario. Esa libertad para elegir la dirección de la dependencia de código fuente con independencia del flujo de control es la herramienta más potente de esta sección.
Entre servicios, la dirección se manifiesta como conocimiento. Si Pedidos llama a la API de Facturación, Pedidos depende de Facturación y tiene que cambiar cuando cambia la API de Facturación. Si Pedidos publica un evento que Facturación consume, la dependencia se invierte: Facturación depende del contrato de eventos de Pedidos, y Pedidos no sabe que Facturación existe (Sección 3.2). Por tanto, elegir entre una llamada y un evento es en parte una elección sobre la dirección de la dependencia.
El Principio de dependencias acíclicas
Robert C. Martin formuló tres principios para las dependencias entre componentes, primero en artículos y ponencias a mediados de la década de 1990 y después en Agile Software Development: Principles, Patterns, and Practices (2002) y Clean Architecture (2017). El primero, el Principio de dependencias acíclicas (ADP), dice que el grafo de dependencias entre componentes no debe contener ciclos: debe ser un grafo acíclico dirigido.
Un ciclo de dependencias, como que Pedidos dependa de Notificaciones, Notificaciones dependa de Clientes y Clientes dependa de Pedidos, convierte tres componentes en uno. Ninguno puede compilarse ni probarse sin los demás; un cambio en cualquiera puede propagarse por el bucle hasta volver a él mismo; ninguno puede lanzarse por separado; y nadie puede decir cuál compilar primero. Martin presenta el principio con lo que él llama el síndrome de la mañana siguiente: te vas a casa con código que funciona y vuelves al día siguiente para encontrarlo roto porque otra persona cambió algo de lo que dependes. Su remedio es dividir el sistema en componentes que se lanzan por separado, y eso solo funciona si las dependencias entre ellos no contienen ciclos. Los ciclos también hacen imposible extraer más adelante un componente como biblioteca o servicio, porque arrastra consigo todo el bucle.
El Java Platform Module System impone el ADP entre módulos: la Java Language Specification establece que es un error de compilación que un módulo dependa, directa o indirectamente, de sí mismo. La mayoría de los demás lenguajes permiten ciclos entre paquetes, así que los equipos los detectan con herramientas. Los ciclos entre clases dentro de un mismo componente pequeño son menos dañinos, y Martin Fowler, entre otros, los tolera ahí; el principio importa sobre todo entre componentes que construyen y lanzan personas distintas.
Romper ciclos
Todo ciclo puede romperse, y hay dos técnicas habituales. Ambas se muestran en el diagrama siguiente para un ciclo en el que Pedidos llama a Notificaciones para enviar una confirmación y Notificaciones vuelve a llamar a Pedidos para leer los detalles del pedido.
Inversión de dependencias. Notificaciones declara una interfaz que describe exactamente lo que necesita, por ejemplo OrderLookup con un solo método, y Pedidos la implementa. La flecha de Notificaciones a Pedidos apunta ahora en sentido contrario: Pedidos depende de Notificaciones (para llamarlo y para implementar su interfaz), y Notificaciones no depende de nada de Pedidos. Esto es la inversión de dependencias, que la Sección 3.5 trata como la D de SOLID. El detalle clave es la propiedad: la interfaz pertenece al módulo que la usa, no al que la implementa.
Extraer un componente nuevo. Averigua qué necesitan realmente las dos partes la una de la otra, a menudo un pequeño modelo o contrato compartido, y trasládalo a un componente nuevo del que dependan ambas. Aquí, Pedidos publica un tipo de evento definido en un componente de eventos de pedidos, y Notificaciones lee los detalles del pedido del evento en lugar de volver a llamar. Ambos dependen del nuevo componente y este no depende de ninguno, así que es más estable que cualquiera de los dos: exactamente lo que pide el siguiente principio.
Dos formas de romper un ciclo
El Principio de dependencias estables y la inestabilidad
Algunos componentes cambian a menudo (interfaces de usuario, integraciones con socios, funciones nuevas); otros deberían cambiar rara vez (conceptos centrales del dominio, contratos compartidos, gestión del dinero). El Principio de dependencias estables (SDP) dice: depende en la dirección de la estabilidad. Un componente pensado para ser fácil de cambiar no debería tener como dependiente a un componente difícil de cambiar, o también se volverá difícil de cambiar.
Martin mide la estabilidad a través de las dependencias, no de la frecuencia con que se edita el código. Un componente del que dependen muchos otros es estable en el sentido de que es costoso de cambiar: cada cambio corre el riesgo de romper a sus dependientes. Un componente que depende de muchos otros y no tiene dependientes es inestable: nada le impide cambiar, y sus dependencias pueden obligarle a hacerlo. Usando el acoplamiento aferente (Ca, entrante) y el acoplamiento eferente (Ce, saliente) de la Sección 3.2, la inestabilidad es:
I = Ce / (Ca + Ce)
I va de 0 a 1. I = 0 significa solo dependencias entrantes: máximamente estable, responsable ante muchos, sin depender de nada. I = 1 significa solo dependencias salientes: máximamente inestable, nada depende de él. El SDP, expresado como métrica, dice que I debería disminuir a lo largo de cada flecha de dependencia: cada componente debería depender de componentes con una I menor que la suya. Una dependencia de un componente estable (I = 0.1) a uno volátil (I = 0.8) es una señal de alerta; el arreglo habitual vuelve a ser la inversión de dependencias, colocando una interfaz en un lugar estable.
El Principio de abstracciones estables y la secuencia principal
La estabilidad tiene un costo: un componente con una I cercana a 0 es difícil de cambiar. Si está lleno de código concreto, el sistema se vuelve rígido. El Principio de abstracciones estables (SAP) lo resuelve: un componente debe ser tan abstracto como estable. Los componentes estables deben consistir sobre todo en interfaces y tipos abstractos, para que su comportamiento pueda ampliarse con nuevas implementaciones en otro lugar sin modificarlos; los componentes inestables pueden ser concretos, porque de todos modos son fáciles de cambiar. El SDP y el SAP juntos equivalen al principio de inversión de dependencias aplicado a componentes enteros: las dependencias apuntan hacia la estabilidad, y la estabilidad va unida a la abstracción.
El grado de abstracción se mide como A = Na / Nc, donde Na es el número de clases abstractas e interfaces del componente y Nc el número total de clases. A = 0 significa totalmente concreto; A = 1 significa nada más que abstracciones. Representar A frente a I da un cuadrado. La línea que va de (I = 0, A = 1) a (I = 1, A = 0) es la secuencia principal: estable y abstracto en un extremo, inestable y concreto en el otro, con mezclas equilibradas entre ambos. La distancia a ella es:
D = |A + I - 1|
D va de 0 (sobre la línea) a 1 (en una esquina lejana). La esquina cercana a (0, 0) es la zona de dolor: estable, porque mucho depende de ello, y concreto, así que cualquier cambio es a la vez necesario y doloroso. Un esquema de base de datos que todos los módulos leen directamente vive aquí. Martin señala que algunos componentes están ahí sin causar daño, como una biblioteca de utilidades concreta y madura que nunca necesita cambiar; la zona solo duele en los componentes que son volátiles. La esquina cercana a (1, 1) es la zona de inutilidad: abstracciones de las que nada depende, normalmente interfaces que quedaron tras una refactorización o que se diseñaron para una extensión que nunca llegó.
La secuencia principal
Ejemplo práctico
Ejemplo práctico: calcular I, A y D para tres componentes
Una tienda tiene tres componentes. Siguiendo la definición de Martin, Ca cuenta las clases de fuera del componente que dependen de clases de dentro, y Ce cuenta las clases de dentro que dependen de clases de fuera. Los recuentos proceden de una herramienta de dependencias.
| payments-api (5 clases, 4 de ellas interfaces) | Ca = 6, Ce = 1. I = 1 / (6 + 1) = 0.14. A = 4 / 5 = 0.80. D = |0.80 + 0.14 - 1| = 0.06. |
|---|---|
| orders-core (20 clases, 2 abstractas) | Ca = 8, Ce = 2. I = 2 / (8 + 2) = 0.20. A = 2 / 20 = 0.10. D = |0.10 + 0.20 - 1| = 0.70. |
| web-ui (12 clases, ninguna abstracta) | Ca = 0, Ce = 9. I = 9 / (0 + 9) = 1.00. A = 0 / 12 = 0.00. D = |0 + 1 - 1| = 0.00. |
| Comprobar el SDP a lo largo de las flechas | web-ui (1.00) depende de orders-core (0.20) y de payments-api (0.14); orders-core (0.20) depende de payments-api (0.14). I disminuye a lo largo de cada flecha, así que se cumple el SDP. |
| Diagnóstico | orders-core tiene D = 0.70: estable (muchos dependientes) pero concreto. Si sus reglas cambian a menudo, cada cambio pone en riesgo ocho clases dependientes. Está en la zona de dolor. |
| Remedio y nuevo cálculo | Extrae los cuatro tipos que usan sus dependientes a orders-api (3 interfaces y 1 tipo de valor: A = 0.75; Ca es 8 o más, ya que ahora las clases que lo implementan también dependen de él, y Ce = 0, así que I = 0; D = |0.75 + 0 - 1| = 0.25). Las 16 clases concretas restantes pasan a ser orders-impl, del que solo depende el código de conexión del arranque (Ca = 1) y que depende de orders-api y de otros dos (Ce = 3): I = 3 / 4 = 0.75, A = 0, D = 0.25. |
El remedio acerca mucho ambas partes a la secuencia principal (de 0.70 a 0.25 cada una). Los dependientes se apoyan ahora en un componente pequeño, estable y mayoritariamente abstracto, y el código concreto volátil está donde es barato cambiarlo.
Los números son heurísticas, no objetivos. Una D de 0.70 es un motivo para mirar, no la prueba de un problema; si orders-core nunca cambiara, no haría ningún daño donde está.
Organización en capas estricta y relajada
La organización en capas (layering) es la regla de dependencia más antigua y común: organizar los módulos en capas, como presentación, aplicación o dominio, y persistencia, y permitir que cada capa use solo las capas inferiores. Las capas inferiores no saben nada de las superiores, así que puede añadirse una nueva interfaz de usuario sin cambiar el dominio. La organización en capas es un estilo arquitectónico por derecho propio (Sección 5.2); aquí lo que interesa es la regla de dependencia que impone.
En la organización en capas estricta (cerrada), cada capa solo puede usar la capa inmediatamente inferior. Así, cada capa oculta todo lo que tiene debajo, y sustituir una capa inferior solo afecta a otra capa; el precio es el código de paso, en el que la capa intermedia se limita a reenviar llamadas a las que no tiene nada que añadir. En la organización en capas relajada (abierta), una capa puede usar cualquier capa inferior. Eso elimina el código repetitivo, pero significa que un cambio en la capa inferior puede afectar a todas las capas superiores. Muchos sistemas reales mezclan ambas y marcan como abiertas capas concretas, como una capa compartida de logging o de utilidades que todos pueden usar.
La organización en capas clásica tiene una debilidad conocida: la capa de dominio depende de la capa de persistencia, así que las reglas de negocio más importantes dependen de detalles de la base de datos. Las arquitecturas hexagonal, de cebolla y limpia (Sección 5.6) aplican la inversión de dependencias para corregirlo: el dominio es dueño de interfaces como OrderRepository y el código de persistencia las implementa, de modo que todas las dependencias de código fuente apuntan hacia dentro, hacia el dominio.
Organización en capas estricta y relajada
Comprobar automáticamente las reglas de dependencia
Las reglas de dependencia escritas solo en una wiki se incumplen en cuestión de semanas. La forma fiable de mantenerlas es expresarlas como comprobaciones ejecutables que se ejecutan en la compilación y la hacen fallar cuando se viola una regla. Una comprobación así es una función de aptitud (fitness function) arquitectónica, una idea presentada en la Sección 2.8 y desarrollada en la Sección 11.3. Tres herramientas de código abierto muy usadas cubren los principales ecosistemas.
ArchUnit (Java y otros lenguajes de la JVM) te permite escribir reglas de arquitectura como pruebas unitarias normales: qué paquetes pueden acceder a cuáles, definiciones de capas y ausencia de ciclos entre porciones del código base. dependency-cruiser (JavaScript y TypeScript) analiza las importaciones y las valida contra reglas de un archivo de configuración, y su configuración inicial ya prohíbe las dependencias circulares. import-linter (Python) comprueba contratos declarados en un archivo de configuración; entre sus tipos de contrato están las importaciones prohibidas, las capas, la independencia entre módulos hermanos, los módulos protegidos y los hermanos acíclicos.
@ArchTest
static final ArchRule layers = layeredArchitecture()
.consideringAllDependencies()
.layer("Web").definedBy("..web..")
.layer("Domain").definedBy("..domain..")
.layer("Persistence").definedBy("..persistence..")
.whereLayer("Web").mayNotBeAccessedByAnyLayer()
.whereLayer("Domain").mayOnlyBeAccessedByLayers("Web", "Persistence")
.whereLayer("Persistence").mayNotBeAccessedByAnyLayer();
@ArchTest
static final ArchRule noCycles =
slices().matching("com.shop.(*)..").should().beFreeOfCycles();// .dependency-cruiser.js
module.exports = {
forbidden: [
{ name: "no-circular", severity: "error",
from: {}, to: { circular: true } },
{ name: "domain-not-to-db", severity: "error",
comment: "Domain code must use repository interfaces, not the database",
from: { path: "^src/domain" },
to: { path: "^src/db" } },
],
};# .importlinter
[importlinter]
root_package = shop
[importlinter:contract:layers]
name = Web above domain above adapters
type = layers
layers =
shop.web
shop.domain
shop.adapters
[importlinter:contract:features-independent]
name = Feature packages do not import each other
type = independence
modules =
shop.catalogue
shop.ordering
shop.paymentsObserva que la regla de ArchUnit anterior permite que Persistence acceda a Domain pero no al revés: es la organización centrada en el dominio que acabamos de describir, no la organización en capas clásica. Reglas como estas son baratas de añadir, se ejecutan en segundos y convierten un diagrama de arquitectura en algo que la compilación defiende. Cuando un código base existente ya incumple una regla, la mayoría de las herramientas pueden registrar las infracciones actuales como línea base y fallar solo con las nuevas, de modo que la regla impide que las cosas empeoren desde el día en que se introduce.
Guía de decisión
Guía de decisión: qué hacer con una dependencia no deseada
Úsala cuando falle una comprobación de dependencias o una revisión encuentre una dependencia que apunta en el sentido equivocado.
- ¿Forma parte la dependencia de un ciclo entre componentes?SíRompe primero el ciclo (ADP): sigue abajo para elegir cómo.NoPasa a la pregunta sobre la estabilidad.
- ¿Necesita una de las partes solo un servicio pequeño y bien definido de la otra?SíInvierte la dependencia: haz que la parte que usa el servicio sea dueña de una interfaz, y que la otra parte la implemente.NoPasa a la siguiente pregunta.
- ¿Dependen ambas partes de un modelo, un contrato o un fragmento de lógica compartidos?SíExtráelo a un componente nuevo del que dependan ambas, y haz que ese componente sea pequeño, estable y mayoritariamente abstracto.NoPlantéate si los dos componentes son en realidad uno: fusiónalos y vuelve a dividirlos por un límite mejor (Sección 3.1).
- ¿Depende un componente estable (I baja) de uno volátil (I alta)?SíEsto incumple el SDP. Coloca una interfaz en el componente estable, o en uno nuevo que sea estable, y traslada detrás de ella el código volátil.NoLa dirección es aceptable. Registra la regla en una función de aptitud para que siga así.
Cómo gestionan las dependencias los profesionales
Los arquitectos con experiencia tienen una imagen del grafo de dependencias entre componentes y la consultan con regularidad, generada a partir del código en lugar de dibujada a mano. Deciden qué componentes deben ser estables (conceptos del dominio, contratos compartidos) y cuáles volátiles (interfaces de usuario, integraciones), y hacen que las flechas apunten de lo volátil a lo estable. Tratan los ciclos nuevos como fallos de compilación, no como cuestiones de estilo. Y aplican el mismo razonamiento entre servicios: las bibliotecas compartidas y los esquemas de eventos compartidos son los componentes estables de un sistema distribuido, que merecen superficies mínimas y un versionado cuidadoso, mientras que las partes internas de cada servicio siguen siendo libres de cambiar.
Nada de esto exige calcular las métricas con precisión. Su valor está en las preguntas que obligan a hacerse: ¿en qué sentido debería apuntar esta flecha?, ¿qué les pasa a todos los que están aguas arriba si esto cambia?, ¿es este componente tan abstracto como exige el número de cosas que dependen de él? La Sección 3.5 continúa con los principios SOLID, que aplican las mismas ideas dentro de los componentes y entre ellos.
Comprueba lo que has entendido
¿Por qué todos los componentes de un ciclo de dependencias deben lanzarse juntos?
Cada uno depende, directa o indirectamente, de todos los demás, así que un cambio en cualquiera puede exigir cambios en el resto; ninguno puede compilarse ni probarse sin los demás.
Un componente tiene Ca = 3 y Ce = 9. ¿Cuál es su inestabilidad y qué significa?
I = 9 / 12 = 0.75. Depende de mucho más de lo que depende de él, así que es relativamente inestable: fácil de cambiar, pero expuesto a los cambios de sus dependencias.
¿Qué hay en la zona de dolor y cuándo duele de verdad?
Componentes que son a la vez estables (muchos dependientes) y concretos (pocas abstracciones). Duele cuando un componente así necesita cambiar a menudo; un componente concreto que nunca cambia hace poco daño ahí.
¿Cuál es la diferencia entre la organización en capas estricta y la relajada?
En la organización en capas estricta, una capa solo puede usar la capa inmediatamente inferior; en la relajada, puede usar cualquier capa inferior.
Fuentes y lecturas recomendadas
¿Tienes una pregunta sobre esta sección? ArchitectBot, el asistente de estudio con IA del curso, responde primero a partir del curso.
Pregunta: “¿Cómo encuentro y rompo los ciclos de dependencias en un código base grande sin detener el desarrollo de funcionalidades?”