Cómo se aplican los principios SOLID en arquitecturas modernas
La evolución del desarrollo de software ha traído consigo nuevas formas de estructurar sistemas: desde arquitecturas monolíticas limpias hasta entornos distribuidos con microservicios, eventos y dominios altamente desacoplados. En todos estos enfoques, los principios SOLID siguen siendo una brújula técnica para lograr modularidad, flexibilidad y mantenibilidad.
Aunque originalmente fueron concebidos para el diseño orientado a objetos, los principios SOLID tienen implicaciones directas en la arquitectura de sistemas completos. Este artículo explora cómo se aplican estos principios en distintas arquitecturas modernas, y por qué siguen siendo esenciales incluso más allá del nivel del código.
Arquitectura Limpia (Clean Architecture)
Clean Architecture propone una separación estricta entre el núcleo del dominio (que no debe conocer ningún detalle de infraestructura) y las capas externas (como bases de datos, frameworks web o mensajerías).
SRP – Separación clara de responsabilidades por capa
Cada clase y capa tiene una única responsabilidad. Los casos de uso no deben hacer validaciones de entrada, ni persistencia, ni rendering. Por ejemplo, un RegistrarUsuarioUseCase
solo coordina lógica de negocio; la validación se delega a otra clase.
OCP – Nuevas funcionalidades sin romper el núcleo
Agregar un nuevo canal de notificación o motor de base de datos solo requiere extender un adaptador, sin tocar la lógica de negocio central.
LSP – Implementaciones intercambiables
Los adaptadores de entrada o salida pueden ser sustituidos sin romper el flujo (por ejemplo, un nuevo sistema de almacenamiento que implementa la misma interfaz RepositorioDeUsuarios
).
ISP – Interfaces por caso de uso
Cada caso de uso define interfaces específicas. En vez de tener un RepositorioGeneral
, cada agregado tiene su propia interfaz minimalista, lo que evita acoplamientos innecesarios.
DIP – Dependencias invertidas desde el dominio
La lógica del negocio define las interfaces, y los detalles las implementan. Esto significa que el dominio no depende de frameworks, motores de base de datos o controladores HTTP.
Microservicios
En arquitecturas de microservicios, el sistema se divide en servicios pequeños, autónomos y desplegables independientemente, cada uno centrado en un único dominio.
SRP – Cada servicio con un solo propósito
Un microservicio bien diseñado representa una única capacidad de negocio. Por ejemplo, ServicioDePagos
no se encarga de GestiónDeUsuarios
.
OCP – Evolución sin modificar lo existente
Los microservicios pueden extenderse agregando nuevos endpoints o eventos, sin modificar la lógica actual, permitiendo una evolución segura.
LSP – Compatibilidad con consumidores
Una nueva versión del servicio debe ser compatible con los contratos anteriores. Si se rompe la capacidad de sustitución, los consumidores fallarán.
ISP – APIs específicas por consumidor
En vez de exponer una API genérica para todos, se puede construir una fachada personalizada o usar GraphQL para que cada cliente consuma lo necesario.
DIP – Servicios internos desacoplados por abstracciones
Incluso dentro de un microservicio, se inyectan puertos (interfaces) en lugar de depender directamente de bases de datos, colas o integraciones externas.
Domain-Driven Design (DDD)
DDD promueve el modelado explícito del dominio a través de entidades, agregados, servicios de dominio, repositorios y contexto delimitado.
SRP – Agregados autocontenidos
Cada agregado encapsula su consistencia y lógica. Por ejemplo, un Pedido
tiene su lógica de cambio de estado, sin depender de lógica externa.
OCP – Nuevas reglas sin romper agregados
Las reglas de negocio pueden extenderse agregando nuevos servicios de dominio que operan sobre los agregados sin modificar su estructura base.
LSP – Sustitución coherente de implementaciones
Repositorios, servicios de validación o políticas de negocio pueden cambiar manteniendo la interfaz esperada. Esto es fundamental cuando se hacen pruebas o se alternan reglas entre contextos.
ISP – Contratos por agregado
Los consumidores externos o internos del dominio interactúan con interfaces reducidas: IPedidoRepository
, ICalculadorDeImpuestos
, etc., en lugar de una API genérica.
DIP – Aislando el dominio del entorno
El dominio no depende de detalles como bases de datos o frameworks. Todo se conecta a través de interfaces, y los detalles se resuelven desde el exterior.
Arquitectura basada en eventos
Los sistemas orientados a eventos comunican sus componentes a través de mensajes asincrónicos, permitiendo bajo acoplamiento y alta escalabilidad.
SRP – Manejadores por tipo de evento
Cada clase que consume eventos está especializada en un solo propósito, como ManejadorEventoCompraCreada
.
OCP – Nuevos consumidores sin romper emisores
Se pueden agregar nuevos suscriptores a un evento sin modificar el código del emisor original.
LSP – Sustitución de consumidores
Un consumidor de eventos puede ser reemplazado por otra implementación (por ejemplo, una versión que hace reintentos o logging) sin cambiar la semántica del flujo.
ISP – Contratos de eventos reducidos
Los eventos contienen solo los datos mínimos necesarios para ser procesados. Los consumidores no deben depender de campos innecesarios que podrían desaparecer.
DIP – Productores y consumidores desacoplados
Ambos lados interactúan a través de un contrato (como un schema Avro o JSON), pero no conocen la implementación del otro extremo. Esto permite escalar e integrar distintos lenguajes o tecnologías.
Arquitecturas modulares o monolíticas limpias
Incluso en sistemas monolíticos, SOLID permite crear capas y módulos independientes que pueden evolucionar y mantenerse con facilidad.
SRP – Módulos por responsabilidad funcional
Cada módulo (por ejemplo, usuarios, facturación, reportes) se aísla en código y lógica. Esto facilita la prueba y el mantenimiento.
OCP – Agregar funcionalidades sin alterar el core
Puedes extender comportamientos creando nuevos controladores, servicios o eventos sin tocar el núcleo del sistema.
LSP – Sustitución de implementaciones internas
Permite cambiar servicios de almacenamiento, lógica o validaciones sin alterar el código que los usa.
ISP – Interfaces internas bien definidas
Los distintos módulos se comunican a través de contratos específicos, sin exponer su implementación interna.
DIP – Inversión de dependencias por capas
El core del sistema no depende de frameworks, solo de contratos definidos internamente. Los detalles como bases de datos, autenticación o redes se inyectan desde afuera.
Conclusión
Los principios SOLID no son exclusivos del diseño de clases orientadas a objetos. Son una guía atemporal para construir sistemas que sobreviven al cambio, escalan sin dolor y mantienen la coherencia entre múltiples equipos y tecnologías.
En cada arquitectura moderna, SOLID cumple un rol clave:
- En Clean Architecture, permite separar detalles del dominio.
- En Microservicios, ayuda a mantener la autonomía y claridad.
- En DDD, refuerza el modelado rico y desacoplado.
- En event-driven, sostiene el bajo acoplamiento entre componentes.
- En monolitos modulares, ayuda a mantener orden y escalabilidad.
La aplicación consciente de estos principios no es una receta rígida, sino una práctica estratégica para crear software sostenible y alineado con la evolución del negocio.