Skip to main content

Inicia sesión en CleanKata

Sigue tu progreso, gana XP y desbloquea todas las lecciones.

Al iniciar sesión aceptas nuestros Términos de uso y Política de privacidad.

Arquitectura Limpia70 XP7 min

SDP: El Principio de Dependencias Estables

Depende en la dirección de la estabilidad — un componente volátil nunca debe ser dependido por uno estable, o esa estabilidad queda comprometida permanentemente.

Por qué importa

El Principio de Dependencias Estables (SDP) gobierna qué componentes pueden depender de cuáles otros componentes. La regla es simple: depende en la dirección de la estabilidad. Nunca dejes que un componente estable dependa de uno volátil.

La estabilidad se mide, no se asume. Un componente es estable si es difícil de cambiar. Se vuelve difícil de cambiar cuando muchos otros componentes dependen de él — porque cualquier cambio requeriría actualizar todos esos dependientes. La métrica es Inestabilidad: I = fan-out / (fan-in + fan-out). Fan-in cuenta las dependencias entrantes (componentes que dependen de este). Fan-out cuenta las dependencias salientes (componentes de los que este depende). I≈0 significa estable; I≈1 significa volátil.

La regla del SDP: la Inestabilidad (I) de un componente dependido debe ser mayor o igual a la I del componente dependiente. Los componentes estables (I≈0) deben depender solo de otros componentes estables. Los componentes volátiles (I≈1) pueden depender de cualquier cosa — están en la hoja del árbol de dependencias.

Cuando un componente estable depende de uno volátil, la estabilidad del componente estable se ve socavada. Cada vez que el componente volátil cambia, el componente estable también debe cambiar — y también todos sus dependientes. La violación del SDP propaga el cambio a través del sistema como una onda de choque.

✗El problema

A stable AuthService (imported by 10 modules) directly imports ExperimentalFeatureFlag — a volatile class that changes weekly. One change to the experiment breaks every module depending on Auth.

Bad

# auth_service.py  ← imported by 10 other modules
from experimental.feature_flag import ExperimentalFeatureFlag  # volatile!

class AuthService:
    def __init__(self):
        self._flag = ExperimentalFeatureFlag()  # coupling to volatile concrete

    def can_access(self, user_id: str, feature: str) -> bool:
        if not self._flag.is_enabled(feature):   # volatile call inside stable class
            return False
        return self._is_authorised(user_id)

# experimental/feature_flag.py  ← changes every week, nobody depends on it
class ExperimentalFeatureFlag:
    def is_enabled(self, feature: str) -> bool:
        return feature in {"new_dashboard", "beta_checkout"}

# Instability(AuthService)  ≈ 0  (many depend on it — should be stable)
# Instability(FeatureFlag)  ≈ 1  (nobody depends on it — volatile)
# SDP violated: stable depends on volatile.
// AuthService.ts  ← imported by 10 other modules
import { ExperimentalFeatureFlag } from "./experimental/ExperimentalFeatureFlag";

export class AuthService {
  private flag = new ExperimentalFeatureFlag(); // coupling to volatile concrete

  canAccess(userId: string, feature: string): boolean {
    if (!this.flag.isEnabled(feature)) return false; // volatile call inside stable
    return this.isAuthorised(userId);
  }

  private isAuthorised(userId: string): boolean { return true; }
}

// ExperimentalFeatureFlag.ts  ← changes every week, nobody depends on it
export class ExperimentalFeatureFlag {
  isEnabled(feature: string): boolean {
    return ["new_dashboard", "beta_checkout"].includes(feature);
  }
}

// Instability(AuthService)      ≈ 0  (many depend on it — should be stable)
// Instability(ExperimentalFlag) ≈ 1  (nobody depends on it — volatile)
// SDP violated: stable depends on volatile.

✓La solución

AuthService depends on a FeatureFlagPort abstract interface (stable). ExperimentalFeatureFlag implements it and stays at the volatile edge. Auth's stability is preserved.

Good

# ports/feature_flag_port.py  ← stable abstract interface
from abc import ABC, abstractmethod

class FeatureFlagPort(ABC):
    @abstractmethod
    def is_enabled(self, feature: str) -> bool: ...

# auth_service.py  ← depends only on the abstract port (stable)
class AuthService:
    def __init__(self, flags: FeatureFlagPort):
        self._flags = flags

    def can_access(self, user_id: str, feature: str) -> bool:
        if not self._flags.is_enabled(feature):
            return False
        return self._is_authorised(user_id)

# experimental/feature_flag.py  ← volatile concrete at the unstable edge
class ExperimentalFeatureFlag(FeatureFlagPort):
    def is_enabled(self, feature: str) -> bool:
        return feature in {"new_dashboard", "beta_checkout"}

# Instability(FeatureFlagPort)          ≈ 0  — abstract, depended upon
# Instability(ExperimentalFeatureFlag)  ≈ 1  — concrete, at the edge
# SDP satisfied: dependencies flow toward stability.
// ports/FeatureFlagPort.ts  ← stable abstract interface
export interface FeatureFlagPort {
  isEnabled(feature: string): boolean;
}

// AuthService.ts  ← depends only on the stable interface
import { FeatureFlagPort } from "./ports/FeatureFlagPort";

export class AuthService {
  constructor(private readonly flags: FeatureFlagPort) {}

  canAccess(userId: string, feature: string): boolean {
    if (!this.flags.isEnabled(feature)) return false;
    return this.isAuthorised(userId);
  }

  private isAuthorised(userId: string): boolean { return true; }
}

// experimental/ExperimentalFeatureFlag.ts  ← volatile concrete at the edge
import { FeatureFlagPort } from "../ports/FeatureFlagPort";

export class ExperimentalFeatureFlag implements FeatureFlagPort {
  isEnabled(feature: string): boolean {
    return ["new_dashboard", "beta_checkout"].includes(feature);
  }
}

// Instability(FeatureFlagPort)          ≈ 0  — abstract, depended upon
// Instability(ExperimentalFeatureFlag)  ≈ 1  — concrete, at the edge
// SDP satisfied: dependencies flow toward stability.

💡Conclusión clave

La estabilidad no se trata de frecuencia de cambio — se trata de cuántos componentes dependen de ti. Un componente estable (I≈0) es difícil de cambiar porque el cambio se propaga a todos los dependientes. Nunca dejes que un componente estable importe a uno volátil; inserta una interfaz abstracta en la frontera para que la flecha de dependencia apunte hacia la estabilidad, no alejándose de ella.

🔧 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: La estabilidad no se trata de la frecuencia de cambio — se trata de lo difícil que es cambiar. Un componente con muchos dependientes es difícil de cambiar, independientemente de si cambia frecuentemente.

✗ Tu versión