El Principio de Responsabilidad Única (SRP, Single Responsibility Principle) establece que una clase, módulo o componente debe tener un solo motivo para cambiar. En otras palabras, cada unidad de código debe concentrarse en resolver un problema específico del dominio.
SRP es el primer principio del acrónimo SOLID y funciona como una brújula para el diseño orientado a objetos en proyectos reales. Aplicarlo minimiza cambios colaterales, hace que el código sea más comprensible y prepara el terreno para los demás principios.
Una interpretación común dice que la responsabilidad equivale a una razón de cambio. Otra agrega que cada clase debe representar un actor del sistema: un rol dentro de la organización o un proceso del negocio. Ambas ideas se complementan y permiten evaluar si la clase forma parte de una única historia de usuario.
Al dividir responsabilidades, el código gana claridad, la cobertura de pruebas crece y los equipos pueden estimar tareas con mayor precisión. Además, la arquitectura se vuelve más flexible para aplicar otros principios SOLID como OCP o DIP.
Veamos una clase que gestiona usuarios, registra auditorías y envía correos en un mismo lugar. Cada responsabilidad pertenece a un área distinta del sistema.
class UsuarioService {
void crearUsuario(Usuario usuario) {
validar(usuario);
guardar(usuario);
registrarAuditoria(usuario);
enviarMailBienvenida(usuario);
}
private void validar(Usuario usuario) { /* ... */ }
private void guardar(Usuario usuario) { /* ... */ }
private void registrarAuditoria(Usuario usuario) { /* ... */ }
private void enviarMailBienvenida(Usuario usuario) { /* ... */ }
}
Esta clase viola SRP porque mezcla validación, persistencia, auditoría y comunicación con clientes. Cualquier cambio en uno de esos procesos obliga a modificar el servicio principal.
El primer paso consiste en extraer cada responsabilidad a un colaborador especializado. De esta manera, el servicio central coordina la creación de usuarios sin implementar los detalles concretos.
interface ValidadorUsuarios {
void validar(Usuario usuario);
}
interface RepositorioUsuarios {
void guardar(Usuario usuario);
}
interface AuditorUsuarios {
void registrarAlta(Usuario usuario);
}
interface NotificadorUsuarios {
void enviarBienvenida(Usuario usuario);
}
class UsuarioService {
private final ValidadorUsuarios validador;
private final RepositorioUsuarios repositorio;
private final AuditorUsuarios auditor;
private final NotificadorUsuarios notificador;
UsuarioService(ValidadorUsuarios validador,
RepositorioUsuarios repositorio,
AuditorUsuarios auditor,
NotificadorUsuarios notificador) {
this.validador = validador;
this.repositorio = repositorio;
this.auditor = auditor;
this.notificador = notificador;
}
void crearUsuario(Usuario usuario) {
validador.validar(usuario);
repositorio.guardar(usuario);
auditor.registrarAlta(usuario);
notificador.enviarBienvenida(usuario);
}
}
La clase refactorizada cumple con SRP porque solo se ocupa de orquestar el proceso. Si necesitamos introducir un nuevo canal de comunicación, podemos hacerlo agregando una implementación de NotificadorUsuarios
sin modificar UsuarioService
.
Una clase con responsabilidad única facilita la creación de pruebas específicas. En el ejemplo de arriba, podemos simular cada dependencia con mocks y verificar que el servicio orquesta las colaboraciones en el orden correcto. Esto reduce el tiempo de diagnóstico ante fallos.
El principio no se limita a clases. También aplica a módulos, microservicios y capas completas. Por ejemplo, un microservicio debería tener un único propósito dentro del dominio. Si maneja reporting, facturación y gestión de usuarios, probablemente está violando SRP a gran escala.
Adoptar SRP desde las primeras iteraciones del proyecto crea un lenguaje común dentro del equipo y allana el camino para los siguientes principios SOLID. En los capítulos próximos veremos cómo se complementa con el principio abierto/cerrado.