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

SAP: El Principio de Abstracciones Estables

Un componente debe ser tan abstracto como estable — los componentes más estables deben ser interfaces puras para poder extenderse sin modificarse.

Por qué importa

El Principio de Abstracciones Estables (SAP) establece que un componente debe ser tan abstracto como estable. Combinado con el SDP, el SAP entrega el Principio de Inversión de Dependencias a escala de componente. La fórmula: Abstracción A = clases abstractas / total de clases en el componente. Un componente con A≈1 es completamente abstracto (todas interfaces). Un componente con A≈0 es completamente concreto.

Los componentes estables (I≈0) deben tener A≈1. Si un componente es muy dependido y no puede cambiar, debe ser extensible sin modificación. El Principio Abierto/Cerrado se aplica a nivel de componente: las interfaces puras pueden extenderse agregando nuevas implementaciones, nunca modificando la interfaz misma. Así es como las políticas de negocio de alto nivel permanecen estables mientras el comportamiento se extiende.

Los componentes volátiles (I≈1) pueden ser concretos (A≈0). Si un componente tiene pocos dependientes, es libre de cambiar su implementación concreta. Los detalles de bajo nivel como drivers de bases de datos, enviadores de correo y adaptadores HTTP deben vivir aquí — volátiles y concretos, fáciles de intercambiar.

✗El problema

A highly stable PaymentProcessor component imported by 20 modules contains only concrete classes. Adding ApplePayPayment requires modifying this stable component, rippling changes across all 20 dependents.

Bad

# payment_processor/__init__.py  ← imported by 20 modules (stable, I≈0)
class StripePayment:
    def charge(self, amount: float, token: str) -> bool:
        return True   # Stripe-specific implementation

class PayPalPayment:
    def charge(self, amount: float, token: str) -> bool:
        return True   # PayPal-specific implementation

class CryptoPayment:
    def charge(self, amount: float, token: str) -> bool:
        return True   # Crypto-specific implementation

# A≈0 (all concrete), I≈0 (many depend on it) → Pain Zone
# To add ApplePayPayment: edit this widely-depended-upon component.
# All 20 dependents must be re-tested. OCP violated at component scale.
// payment-processor/index.ts  ← imported by 20 modules (stable, I≈0)
export class StripePayment {
  charge(amount: number, token: string): boolean { return true; }
}

export class PayPalPayment {
  charge(amount: number, token: string): boolean { return true; }
}

export class CryptoPayment {
  charge(amount: number, token: string): boolean { return true; }
}

// A≈0 (all concrete), I≈0 (many depend on it) → Pain Zone
// To add ApplePayPayment: edit this widely-depended-upon component.
// All 20 dependents must be re-tested. OCP violated at component scale.

✓La solución

The stable payment-processor component exports only the PaymentGateway abstract interface and a data type. Concrete implementations live in a volatile payment-adapters component. Adding Apple Pay touches only the volatile side.

Good

# payment_processor/__init__.py  ← stable (I≈0), abstract (A≈1)
from abc import ABC, abstractmethod
from dataclasses import dataclass

@dataclass
class PaymentResult:
    success: bool
    transaction_id: str

class PaymentGateway(ABC):
    @abstractmethod
    def charge(self, amount: float, token: str) -> PaymentResult: ...

# payment_adapters/stripe.py  ← volatile (I≈1), concrete (A≈0)
class StripeGateway(PaymentGateway):
    def charge(self, amount: float, token: str) -> PaymentResult:
        return PaymentResult(success=True, transaction_id="stripe_txn_123")

# payment_adapters/applepay.py  ← new: touches only the volatile component
class ApplePayGateway(PaymentGateway):
    def charge(self, amount: float, token: str) -> PaymentResult:
        return PaymentResult(success=True, transaction_id="apple_txn_456")

# 20 dependents import PaymentGateway (abstract) — completely untouched.
# A(payment_processor) ≈ 1, I ≈ 0  → on the Main Sequence.
// payment-processor/index.ts  ← stable (I≈0), abstract (A≈1)
export interface PaymentResult { success: boolean; transactionId: string; }

export interface PaymentGateway {
  charge(amount: number, token: string): Promise;
}

// payment-adapters/StripeGateway.ts  ← volatile (I≈1), concrete (A≈0)
import { PaymentGateway, PaymentResult } from "../payment-processor";

export class StripeGateway implements PaymentGateway {
  async charge(amount: number, token: string): Promise {
    return { success: true, transactionId: "stripe_txn_123" };
  }
}

// payment-adapters/ApplePayGateway.ts  ← new: only touches volatile component
import { PaymentGateway, PaymentResult } from "../payment-processor";

export class ApplePayGateway implements PaymentGateway {
  async charge(amount: number, token: string): Promise {
    return { success: true, transactionId: "apple_txn_456" };
  }
}

// 20 dependents import PaymentGateway (interface) — completely untouched.
// A(payment-processor) ≈ 1, I ≈ 0  → on the Main Sequence.

💡Conclusión clave

Los componentes más estables de tu sistema deben contener nada más que interfaces y clases abstractas. El código concreto es inherentemente volátil — cambia cuando cambian los detalles de implementación. Pon tus abstracciones en el centro estable, tus implementaciones en el borde volátil, y deja que el Principio de Inversión de Dependencias fluya naturalmente por todo el grafo de componentes.

🔧 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: Los componentes más estables de tu sistema deben contener solo interfaces y clases abstractas — el código concreto es inherentemente volátil.

✗ Tu versión