← dev-notes
SOLID · 01 / 05

SRP
SINGLE RESPONSIBILITY

Una sola razón para cambiar por módulo — cohesión y coste del cambio

Cohesión Acoplamiento por cambio Separación de intereses
01 Definición formal
FORMAL · DEFINICIÓN

Un módulo (clase, componente o función) debe tener una, y solo una, razón para cambiar: un único actor o eje de negocio/técnico que pueda forzar su modificación. Si dos preocupaciones distintas provocan cambios en el mismo código, el módulo viola el SRP.

Robert C. Martin lo formula así para acotar el blast radius de un requisito nuevo: cuando el equipo de facturación pide un cambio y el equipo de marketing otro, no quieres que ambos toquen la misma clase por accidente.

02 Idea clave

«Responsabilidad» aquí no es «una sola función pública», sino un solo eje de variación. Orquestar un caso de uso puede ser una responsabilidad; persistir otra; notificar al cliente otra. Mezclarlas en un solo servicio barre el suelo con cambios en cadena cada vez que cambia el proveedor de correo o el esquema de la tabla.

03 Cuando se rompe · Java

Servicio de pedidos que también envía el correo y escribe métricas: tres razones para cambiar (dominio, canal de notificación, observabilidad).

ROMPE SRP — TODO EN UNA CLASE
public class OrderService {

    private final OrderRepository orders;

    public OrderService(OrderRepository orders) {
        this.orders = orders;
    }

    public void placeOrder(OrderId id, Money total) {
        var order = new Order(id, total, Instant.now());
        orders.save(order);                    // persistencia

        SmtpEmailClient.sendMime(             // infraestructura de correo (detalle)
            order.customerEmail(),
            buildOrderConfirmationMime(order));

        Files.writeString(                   // otro eje (formato/ruta de logs)
            Path.of("/var/log/orders.log"),
            order.id() + "," + total + "\n",
            StandardOpenOption.APPEND);
    }
}
04 Aplicación adecuada · Java

El caso de uso orquesta; persistencia, notificación y auditoría viven detrás de abstracciones pequeñas (cada una evoluciona por su cuenta).

RESPETA SRP — ROLES SEPARADOS
public final class PlaceOrder {

    private final OrderRepository orders;
    private final OrderNotifier notifier;
    private final OrderAudit audit;

    public PlaceOrder(OrderRepository orders,
                      OrderNotifier notifier,
                      OrderAudit audit) {
        this.orders = orders;
        this.notifier = notifier;
        this.audit = audit;
    }

    public void execute(OrderId id, Money total) {
        var order = new Order(id, total, Instant.now());
        orders.save(order);
        notifier.orderConfirmed(order);
        audit.recordPlaced(order);
    }
}

OrderNotifier puede ser correo, SMS o webhook sin tocar PlaceOrder. El log estructurado puede pasar a OpenTelemetry cambiando solo OrderAudit.

05 Relación con patrones
PUENTE SOLID ↔ GOF
  • Facade: el caso de uso expone una entrada simple y delega a componentes con responsabilidades separadas.
  • Observer: notificaciones (correo/webhook) salen del flujo principal sin mezclar preocupación de comunicación.
  • Evita sobreingeniería: no crees diez clases si solo hay una razón de cambio real hoy.