ADP: El Principio de Dependencias Acíclicas
No se permiten ciclos en el grafo de dependencias de componentes — los ciclos causan el síndrome de la mañana siguiente e impiden compilaciones y pruebas aisladas.
Por qué importa
El ADP exige que el grafo de dependencias de componentes sea un Grafo Acíclico Dirigido (DAG). En el momento en que aparece un ciclo — el componente A depende de B, B depende de C, y C depende de vuelta de A — varias cosas se rompen simultáneamente. Ya no puedes compilar ninguno de ellos sin compilar primero todos los demás. No puedes probar ninguno de forma aislada. Y experimentas el síndrome del día siguiente: corriges un bug en tu componente, te vas a casa, y a la mañana siguiente tu corrección está rota de nuevo porque el cambio de alguien más en un componente del que dependes indirectamente se propagó a través del ciclo.
Martin identifica dos técnicas para romper ciclos. La primera aplica DIP: crea una interfaz en el componente de bajo nivel de la que depende el componente de alto nivel, invirtiendo la dirección de la arista problemática. La segunda extrae un nuevo componente del que dependen ambos componentes cíclicos, llevando las abstracciones compartidas a un módulo separado del que todos dependen. De cualquier manera, el objetivo es un grafo que pueda recorrerse en orden topológico — es decir, siempre hay al menos un componente del que nadie más depende, que puedes compilar, probar y publicar primero.
✗El problema
A three-module cycle — auth imports user, user imports permissions, permissions imports auth — makes it impossible to build or test any one of them independently.
Bad
# auth/tokens.py
from user.models import User # auth depends on user
# user/models.py
from permissions.roles import Role # user depends on permissions
# permissions/roles.py
from auth.tokens import validate_token # permissions depends on auth — CYCLE!
# To test auth, you must import user.
# To import user, you must import permissions.
# To import permissions, you must import auth.
# You can never test any one of them alone.
// auth/tokens.ts
import { User } from "../user/models"; // auth → user
// user/models.ts
import { Role } from "../permissions/roles"; // user → permissions
// permissions/roles.ts
import { validateToken } from "../auth/tokens"; // permissions → auth CYCLE!
// Jest/Vitest will report circular dependency warnings.
// A bug in auth also forces re-testing of user and permissions.
✓La solución
A new auth-types package holds shared abstractions. All three modules depend on auth-types — no module depends on another, forming a clean DAG.
Good
# auth_types/interfaces.py — abstract types only, no deps on other modules
# class UserIdentity: ...
# class TokenValidator(ABC): ...
# auth/tokens.py
from auth_types.interfaces import UserIdentity, TokenValidator # no cycle
# user/models.py
from auth_types.interfaces import UserIdentity # no cycle
# permissions/roles.py
from auth_types.interfaces import TokenValidator # no cycle
# Each module depends only on auth_types — a true DAG.
# auth, user, and permissions can all be tested completely independently.
// auth-types/index.ts — interfaces only, no dependencies
export interface UserIdentity { id: string; email: string; }
export interface TokenValidator {
validate(token: string): UserIdentity | null;
}
// auth/tokens.ts
import type { UserIdentity } from "../auth-types"; // auth → auth-types only
// user/models.ts
import type { UserIdentity } from "../auth-types"; // user → auth-types only
// permissions/roles.ts
import type { TokenValidator } from "../auth-types"; // permissions → auth-types only
// DAG: auth-types <- auth, user, permissions — no cycles.
// Each module is testable with a simple mock of auth-types interfaces.
💡Conclusión clave
Si no puedes probar un módulo sin importar toda la aplicación, hay un ciclo de dependencias en algún lugar. Rómpelo con DIP — crea una interfaz abstracta que invierta la arista problemática — o extrae las abstracciones compartidas a un nuevo componente del que ambas partes dependan.
🔧 Algunos ejercicios pueden tener errores. Si algo parece incorrecto, usa el botón Feedback (abajo a la derecha) para reportarlo — nos ayuda a corregirlo rápido.
Pista: Si no puedes probar un módulo sin importar toda la aplicación, hay un ciclo de dependencias en algún lugar.
✗ Tu versión