Principio Objetivo Beneficio
DRY Eliminar duplicación Código más mantenible y consistente
KISS Mantener simplicidad Código más fácil de entender y mantener
YAGNI No implementar hasta necesitar Menos complejidad y desarrollo más eficiente
Separation of Concerns Una responsabilidad por módulo Mantenimiento independiente de cada capa
Composition over Inheritance Flexibilidad sobre rigidez Diseños más flexibles y modulares
Fail Fast Detectar errores temprano Debugging más fácil y rápido
Law of Demeter Minimizar conocimiento entre objetos Bajo acoplamiento y mayor robustez

DRY - Don’t Repeat Yourself

Principio

“Cada pieza de conocimiento debe tener una representación única, no ambigua y autoritativa dentro del sistema”

Propósito

Evitar la duplicación de código y conocimiento, logrando:

  • Una sola fuente de verdad para cada funcionalidad
  • Mantenimiento más fácil y consistente
  • Reducción de inconsistencias en el sistema

Diagrama

classDiagram
    namespace ViolacionDRY {
        class UserController_Duplicate {
            <<Viola DRY>>
            +createUser(request)
            -validateEmail(email)
            -validateName(name)
        }
        
        class AdminController_Duplicate {
            <<Viola DRY>>
            +createAdminUser(request)
            -validateEmail(email)
            -validateName(name)
        }
    }
    
    namespace AplicacionCorrecta {
        class UserController {
            +createUser(request)
        }
        
        class AdminController {
            +createAdminUser(request)
        }
        
        class UserValidator {
            +validateEmail(email)
            +validateName(name)
            +validate(user)
        }
    }
    
    UserController --> UserValidator
    AdminController --> UserValidator

KISS - Keep It Simple, Stupid

Principio

“La simplicidad debe ser un objetivo clave en el diseño, y la complejidad innecesaria debe evitarse”

Propósito

Mantener el código simple y comprensible, logrando:

  • Soluciones directas sin over-engineering
  • Código fácil de entender y mantener
  • Menor probabilidad de bugs por complejidad

Ejemplos

❌ Violación KISS - Over-engineering

// Solución compleja para calcular descuento
public class DiscountCalculationEngine {
    private DiscountStrategyFactory factory;
    private DiscountContextBuilder contextBuilder;
    private DiscountValidationChain validationChain;
    
    public BigDecimal calculateDiscount(BigDecimal amount, double percentage) {
        DiscountContext context = contextBuilder
            .withAmount(amount)
            .withPercentage(percentage)
            .withValidationChain(validationChain)
            .build();
        
        AbstractDiscountStrategy strategy = factory
            .createStrategy(DiscountType.PERCENTAGE)
            .configure(context.getParameters());
            
        DiscountResult result = strategy.execute(context);
        return result.getFinalAmount();
    }
}

✅ Aplicación Correcta - Solución Simple

// Solución simple y directa
public class DiscountService {
    public BigDecimal calculateDiscount(BigDecimal amount, double percentage) {
        return amount.multiply(BigDecimal.valueOf(percentage / 100));
    }
}

YAGNI - You Aren’t Gonna Need It

Principio

“No implementes algo hasta que realmente lo necesites”

Propósito

Enfocarse en lo que realmente se necesita ahora, evitando:

  • Desarrollar funcionalidades especulativas
  • Complejidad prematura innecesaria
  • Desperdicio de tiempo y esfuerzo

Propósito Extendido

YAGNI va más allá de evitar funcionalidades “por si acaso”. También significa:

  • No implementar funcionalidades que sabes que vendrán, pero aún no son necesarias
  • Dedicar tiempo solo a lo que se necesita ahora
  • Cada funcionalidad tendrá su momento apropiado para ser desarrollada
  • Evitar la tentación de “preparar el terreno” para futuras funcionalidades

Separation of Concerns

Principio

“Separar un programa en secciones distintas, cada una abordando una preocupación separada”

Propósito

Organizar el código en responsabilidades bien definidas a nivel arquitectónico, logrando:

  • Cada capa/módulo maneja un aspecto específico del sistema
  • Separación clara entre presentación, lógica y datos
  • Mantenimiento independiente de cada capa

Diagrama

graph TB
    subgraph "Aplicacion Correcta - Capas Separadas"
        subgraph "Presentation Layer"
            A[Controllers]
            A1[DTOs]
            A2[Mappers]
        end
        
        subgraph "Business Layer"
            B[Services]
            B1[Validators]
            B2[Business Rules]
        end
        
        subgraph "Data Layer"
            C[Repositories]
            C1[Entities]
            C2[Database Config]
        end
        
        subgraph "Infrastructure Layer"
            D[Email Service]
            D1[Audit Service]
            D2[External APIs]
        end
    end
    
    A --> B
    B --> C
    B --> D

Composition over Inheritance

Principio

“Favorecer la composición de objetos sobre la herencia de clases”

Propósito

Crear diseños flexibles y modulares, logrando:

  • Relaciones más flexibles entre objetos
  • Evitar jerarquías rígidas de herencia
  • Mayor facilidad para cambios y extensiones

Diagrama

classDiagram
    namespace HerenciaRigida {
        class Vehicle_Inheritance {
            <<Herencia Rigida>>
            +start()
            +stop()
        }
        
        class Car_Inheritance {
            +openTrunk()
        }
        
        class ElectricCar_Inheritance {
            +charge()
        }
    }
    
    namespace ComposicionFlexible {
        class Vehicle_Composition {
            -engine: Engine
            -storage: List~Storage~
            +start()
            +stop()
            +openStorage()
        }
        
        class Engine {
            <<interface>>
            +start()
            +stop()
        }
        
        class Storage {
            <<interface>>
            +open()
            +close()
        }
        
        class GasolineEngine {
            +start()
            +stop()
        }
        
        class ElectricEngine {
            +start()
            +stop()
            +charge()
        }
        
        class Trunk {
            +open()
            +close()
        }
    }
    
    Vehicle_Inheritance <|-- Car_Inheritance
    Car_Inheritance <|-- ElectricCar_Inheritance
    Vehicle_Composition --> Engine
    Vehicle_Composition --> Storage
    Engine <|.. GasolineEngine
    Engine <|.. ElectricEngine
    Storage <|.. Trunk

Fail Fast

Principio

“Detectar y reportar errores tan pronto como sea posible”

Propósito

Detectar problemas en el momento que ocurren, logrando:

  • Errores reportados inmediatamente
  • Debugging más fácil y rápido
  • Prevenir estados inconsistentes del sistema

Diagrama

sequenceDiagram
    participant Client
    participant Service
    participant Validator
    participant Repository
    participant Database
    
    Client->>Service: processOrder(orderData)
    Service->>Validator: validateInput(orderData)
    
    alt Input inválido
        Validator-->>Service: ValidationError
        Service-->>Client: Error: Invalid input ❌
    else Input válido
        Validator-->>Service: Valid
        Service->>Repository: checkInventory(items)
        
        alt Sin stock
            Repository-->>Service: OutOfStockError
            Service-->>Client: Error: No stock ❌
        else Stock disponible
            Repository-->>Service: Available
            Service->>Database: saveOrder()
            Database-->>Service: Success
            Service-->>Client: Order created ✅
        end
    end

Law of Demeter

Principio

“Un objeto debe hablar solo con sus amigos inmediatos, no con extraños”

Propósito

Minimizar el conocimiento entre objetos, logrando:

  • Bajo acoplamiento entre componentes
  • Evitar cadenas largas de llamadas
  • Mayor robustez ante cambios internos

Ejemplos

❌ Violación Law of Demeter - Cadenas largas

// Cliente conoce demasiado sobre la estructura interna
public class OrderService {
    public String getCustomerCity(Order order) {
        // Violación: cadena larga de llamadas
        return order.getCustomer().getAddress().getCity();
    }
    
    public void processOrder(Order order) {
        // Más violaciones
        String email = order.getCustomer().getContactInfo().getEmail();
        String phone = order.getCustomer().getContactInfo().getPhone();
        // Si cambia la estructura interna, este código se rompe
    }
}

✅ Aplicación Correcta - Métodos delegados

// Cada objeto expone solo lo necesario
public class Order {
    private Customer customer;
    
    public String getCustomerCity() {
        return customer.getCity();
    }
    
    public String getCustomerEmail() {
        return customer.getEmail();
    }
}

public class OrderService {
    public String getCustomerCity(Order order) {
        // Simple y desacoplado
        return order.getCustomerCity();
    }
    
    public void processOrder(Order order) {
        String email = order.getCustomerEmail();
        // Cambios internos no afectan este código
    }
}