Orden de lectura recomendado: Hexagonal → DDD → CQRS → Hybrid
CQRS utiliza conceptos de DDD (write model rico, factory methods, value objects). Si lees CQRS antes que DDD, algunos patrones pueden parecer arbitrarios. Empieza siempre por Hexagonal para entender la base, luego DDD para el modelado del dominio, y finalmente CQRS para la separación de lectura y escritura.
Hexagonal Architecture
Idea principal
La arquitectura hexagonal busca que el núcleo del sistema sea el dominio y que todo lo demás sea intercambiable.
El dominio no debería saber si la app usa REST, colas, JPA, MongoDB o Kafka. Para eso existen los puertos y adaptadores:
- Puertos de entrada: lo que el sistema ofrece al exterior.
- Puertos de salida: lo que el dominio necesita del mundo externo.
- Adaptadores: implementaciones concretas de esos puertos.
Lo que suele costar entender
-
El controlador no contiene negocio
- Solo traduce HTTP ↔ dominio.
- No decide reglas de negocio.
-
Los puertos viven en el dominio
- No son “interfaces decorativas”.
- Representan necesidades reales del negocio.
-
La infraestructura no manda
- JPA, REST, colas, etc. son detalles intercambiables.
-
La entidad de dominio encapsula reglas
Orderprotege estados válidos.- Evita que la aplicación “haga trampas” con el estado.
Flujo visual
Datos accesibles del diagrama
Nodos
- A — HTTP request
- B — OrderController
- C — Input Port / CreateOrderUseCase / GetOrderUseCase / / UpdateOrderStatusUseCase
- D — Application Service
- E — Order / Aggregate Root
- F — Output Port / OrderRepository / OrderEventPublisher
- G — Adapter de infraestructura / JPA / Log
Conexiones
- A (HTTP request) → B (OrderController)
- B (OrderController) → C (Input Port / CreateOrderUseCase / GetOrderUseCase / / UpdateOrderStatusUseCase)
- C (Input Port / CreateOrderUseCase / GetOrderUseCase / / UpdateOrderStatusUseCase) → D (Application Service)
- D (Application Service) → E (Order / Aggregate Root)
- E (Order / Aggregate Root) → F (Output Port / OrderRepository / OrderEventPublisher)
- F (Output Port / OrderRepository / OrderEventPublisher) → G (Adapter de infraestructura / JPA / Log)
Pros
- Muy buen aislamiento del dominio.
- Fácil de probar sin Spring ni BD.
- Cambiar tecnología tiene menos impacto.
- Claridad de responsabilidades.
Contras
- Más clases e interfaces.
- Puede parecer “verbosa” en proyectos pequeños.
- Si se lleva al extremo, añade demasiada ceremonia.
Cuándo interesa usarla
- Sistemas con lógica de negocio real.
- Proyectos que cambiarán de infraestructura con el tiempo.
- Equipos que quieren separar bien negocio y técnica.
- Aplicaciones donde el dominio debe ser testeable de forma aislada.
Cuándo no conviene
- CRUDs simples sin reglas relevantes.
- Prototipos desechables.
- Sistemas muy pequeños donde el coste de abstracción supera el beneficio.
DDD
Idea principal
DDD no es una arquitectura de capas "bonita"; es una forma de modelar el software alrededor del lenguaje del negocio.
En lugar de pensar primero en tablas o endpoints, se piensa en:
- qué quiere hacer el negocio,
- cuáles son sus reglas,
- qué conceptos son importantes,
- y cómo se agrupan.
Lo que suele costar entender
-
Bounded Context
- Un mismo término puede significar cosas distintas según el contexto.
- Por eso existen
accountycustomercomo contextos separados.
-
Aggregate Root
- Es el guardián de las invariantes.
- En este repo,
Accountdecide si se puede depositar, retirar o transferir.
-
Value Objects
- No tienen identidad propia, solo valor.
Money,AccountId,CustomerIdson ejemplos perfectos.
-
Domain Service
- Se usa cuando la lógica no pertenece claramente a un solo aggregate.
TransferDomainServicecoordina una transferencia entre cuentas.
-
Domain Events
- Son hechos que ocurrieron en el dominio.
- Permiten desacoplar reacciones posteriores.
Flujo visual
Datos accesibles del diagrama
Nodos
- A — HTTP request
- B — Controller
- C — Application Service
- D — Aggregate Root / Domain Service
- E — Repository de dominio
- F — Persistencia JPA
Conexiones
- A (HTTP request) → B (Controller)
- B (Controller) → C (Application Service)
- C (Application Service) → D (Aggregate Root / Domain Service)
- D (Aggregate Root / Domain Service) → E (Repository de dominio)
- E (Repository de dominio) → F (Persistencia JPA)
Pros
- Excelente para dominios con reglas complejas.
- Favorece lenguaje común negocio-tecnología.
- Hace visibles los límites del sistema.
- Los invariantes viven donde deben vivir: en el dominio.
Contras
- Requiere entender bien el negocio.
- Puede parecer sobreingeniería si el dominio es simple.
- No te da automáticamente una solución técnica; exige criterio.
- Si se aplica mal, se convierte en puro "patrón decorativo".
Cuándo interesa usarlo
- Dominios complejos o cambiantes.
- Sistemas con reglas de negocio importantes.
- Productos donde la comprensión del negocio es clave.
- Equipos que quieren una base robusta y evolutiva.
Cuándo no conviene
- CRUDs triviales.
- Aplicaciones de poca vida útil.
- Cuando no existe complejidad de negocio real.
- Cuando el equipo aún no puede sostener el coste conceptual.
CQRS
Idea principal
CQRS separa quién escribe de quién consulta.
La idea no es "poner dos carpetas diferentes", sino aceptar que leer y escribir suelen tener necesidades muy distintas:
- Al escribir, importan reglas, validación e invariantes.
- Al leer, importan rapidez, filtros y formatos de consulta.
Lo que suele costar entender
-
Command no devuelve datos de lectura
- Un command expresa intención de cambio.
- No debería usarse como query disfrazada.
-
Read model no tiene por qué ser igual al write model
Productprotege la escritura.ProductViewoptimiza la consulta.
-
La separación no es solo conceptual
- Aquí hay APIs distintas:
/api/commands/products/api/queries/products
- Aquí hay APIs distintas:
-
Los buses desacoplan el transporte
CommandBusyQueryBusevitan que el controlador conozca handlers concretos.
Flujo visual
Datos accesibles del diagrama
Grupos
- COMMAND - Escritura
- QUERY - Lectura
Nodos
- C1 — HTTP POST/PATCH/DELETE
- C2 — ProductCommandController
- C3 — CommandBus
- C4 — CommandHandler específico
- C5 — Write model: Product
- C6 — ProductWriteRepositoryAdapter
- C7 — JPA / BD
- Q1 — HTTP GET
- Q2 — ProductQueryController
- Q3 — QueryBus
- Q4 — QueryHandler específico
- Q5 — Read model: ProductView
- Q6 — ProductReadRepositoryAdapter
- Q7 — JPA / BD
Conexiones
- C1 (HTTP POST/PATCH/DELETE) → C2 (ProductCommandController)
- C2 (ProductCommandController) → C3 (CommandBus)
- C3 (CommandBus) → C4 (CommandHandler específico)
- C4 (CommandHandler específico) → C5 (Write model: Product)
- C5 (Write model: Product) → C6 (ProductWriteRepositoryAdapter)
- C6 (ProductWriteRepositoryAdapter) → C7 (JPA / BD)
- Q1 (HTTP GET) → Q2 (ProductQueryController)
- Q2 (ProductQueryController) → Q3 (QueryBus)
- Q3 (QueryBus) → Q4 (QueryHandler específico)
- Q4 (QueryHandler específico) → Q5 (Read model: ProductView)
- Q5 (Read model: ProductView) → Q6 (ProductReadRepositoryAdapter)
- Q6 (ProductReadRepositoryAdapter) → Q7 (JPA / BD)
Pros
- Muy claro para sistemas con muchas más lecturas que escrituras.
- Permite optimizar consultas de forma independiente.
- Hace más fácil evolucionar el read model.
- Puede mejorar escalabilidad y rendimiento.
Contras
- Más complejidad que un CRUD clásico.
- Puede duplicar lógica o estructuras.
- Si el dominio es pequeño, puede sentirse excesivo.
- La consistencia entre escritura y lectura requiere disciplina.
Cuándo interesa usarlo
- Productos con muchas consultas.
- Paneles, catálogos y dashboards.
- Sistemas donde el modelo de lectura necesita ser distinto del de escritura.
- Dominios donde la carga de lectura y escritura es muy desigual.
Cuándo no conviene
- CRUDs sencillos.
- Sistemas con reglas de negocio mínimas.
- Equipos que aún no dominan bien la arquitectura base.
- Proyectos donde separar lectura/escritura no aporta un beneficio real.
Cuándo mezclar arquitecturas
Estas arquitecturas no compiten entre sí; muchas veces se complementan muy bien.
Hexagonal + DDD
Cuándo: casi siempre que el dominio tenga peso real.
Qué aporta la mezcla:
- DDD te dice cómo modelar el negocio.
- Hexagonal te dice cómo proteger ese modelo del mundo exterior.
Beneficio real en un proyecto:
- el aggregate no depende de Spring,
- los casos de uso se vuelven explícitos,
- y los adaptadores técnicos quedan fuera del núcleo.
Resultado típico: dominio fuerte, infraestructura intercambiable.
Hexagonal + CQRS
Cuándo: cuando escribir y leer empiezan a tener formas muy distintas.
Qué aporta la mezcla:
- Hexagonal separa el sistema de los detalles técnicos.
- CQRS separa el comportamiento de escritura del de lectura.
Beneficio real en un proyecto:
- puedes tener un write model rico y un read model simple,
- evitar que el controlador conozca la persistencia,
- y optimizar queries sin ensuciar el dominio.
Resultado típico: APIs más claras y mejor escalado de lectura.
DDD + CQRS
Cuándo: en dominios complejos donde además hay muchas consultas.
Qué aporta la mezcla:
- DDD modela bien la escritura.
- CQRS permite un read model adaptado a cada caso de uso.
Beneficio real en un proyecto:
- aggregates pequeños y claros,
- consultas rápidas y específicas,
- posibilidad de evolucionar lectura/escritura por separado.
Resultado típico: dominio rico con lecturas muy eficientes.
Las tres juntas: Hexagonal + DDD + CQRS
Cuándo: sistemas serios, con evolución prevista y suficiente complejidad.
Qué aporta la combinación:
- DDD define el lenguaje y las reglas.
- Hexagonal protege el dominio de la tecnología.
- CQRS optimiza la experiencia de lectura/escritura.
Beneficio real en un proyecto:
- código más mantenible a largo plazo,
- negocio mejor modelado,
- adaptadores intercambiables,
- consultas eficientes,
- y un núcleo de dominio muy testable.
Pero ojo: no hay que usar las tres por moda. La combinación solo tiene sentido cuando el problema lo justifica.
Regla práctica para elegir
- Si el sistema es pequeño y simple → probablemente un CRUD bien hecho sea suficiente.
- Si el dominio importa y la infraestructura puede cambiar → Hexagonal.
- Si la escritura y la lectura son claramente distintas → CQRS.
- Si hay reglas de negocio complejas y vocabulario del negocio importante → DDD.
- Si el sistema es complejo y además necesita lectura optimizada → combinar Hexagonal + DDD + CQRS.
APIs de ejemplo
Hexagonal - Pedidos
CQRS - Productos
DDD - Cuentas bancarias
Hybrid - Suscripciones combinando Hexagonal + CQRS + DDD
¿Qué aporta esta mezcla en un proyecto real?
- El modelo de negocio queda limpio y protegido.
- Las consultas pueden optimizarse sin tocar la escritura.
- La infraestructura puede cambiar con poco impacto en el núcleo.
- Cada parte del sistema tiene una razón clara de existir.
Diagramas de Arquitectura
Arquitectura Hexagonal - Componentes
Datos accesibles del diagrama
Grupos
- Entry Layer
- Application Layer
- Domain Layer
- Infrastructure Layer
Nodos
- REST — REST Controller
- CLI — CLI Adapter
- MQ — Message Queue Adapter
- InputPorts — Input Ports / Use Cases
- AppServices — Application Services
- Aggregates — Aggregates / Order, Customer
- VOs — Value Objects / Money, OrderId
- OutputPorts — Output Ports / Repositories, Event Publishers
- JPA — JPA Adapter
- EventPublisher — Event Publisher Adapter
- Log — Logging Adapter
Conexiones
- REST (REST Controller) → InputPorts (Input Ports / Use Cases)
- CLI (CLI Adapter) → InputPorts (Input Ports / Use Cases)
- MQ (Message Queue Adapter) → InputPorts (Input Ports / Use Cases)
- InputPorts (Input Ports / Use Cases) → AppServices (Application Services)
- AppServices (Application Services) → Aggregates (Aggregates / Order, Customer)
- AppServices (Application Services) → OutputPorts (Output Ports / Repositories, Event Publishers)
- Aggregates (Aggregates / Order, Customer) → OutputPorts (Output Ports / Repositories, Event Publishers)
- OutputPorts (Output Ports / Repositories, Event Publishers) → JPA (JPA Adapter)
- OutputPorts (Output Ports / Repositories, Event Publishers) → EventPublisher (Event Publisher Adapter)
- OutputPorts (Output Ports / Repositories, Event Publishers) → Log (Logging Adapter)
Arquitectura CQRS - Componentes
Datos accesibles del diagrama
Grupos
- Command Side
- Query Side
- Database
Nodos
- CController — Command Controller
- CBus — Command Bus
- CHandlers — Command Handlers
- WModel — Write Model / Product
- WRepo — Write Repository
- QController — Query Controller
- QBus — Query Bus
- QHandlers — Query Handlers
- RModel — Read Model / ProductView
- RRepo — Read Repository
- WDB — Write DB / Products Table
- RDB — Read DB / ProductViews Table
Conexiones
- CController (Command Controller) → CBus (Command Bus)
- CBus (Command Bus) → CHandlers (Command Handlers)
- CHandlers (Command Handlers) → WModel (Write Model / Product)
- CHandlers (Command Handlers) → WRepo (Write Repository)
- WRepo (Write Repository) → WDB (Write DB / Products Table)
- QController (Query Controller) → QBus (Query Bus)
- QBus (Query Bus) → QHandlers (Query Handlers)
- QHandlers (Query Handlers) → RModel (Read Model / ProductView)
- QHandlers (Query Handlers) → RRepo (Read Repository)
- RRepo (Read Repository) → RDB (Read DB / ProductViews Table)
Comparativa de Arquitecturas
Tabla comparativa accesible
| Aspecto | Hexagonal | CQRS | DDD | Hybrid |
|---|---|---|---|---|
| Enfoque principal | Aislamiento del dominio | Separación lectura/escritura | Lenguaje del negocio | Combinación de las tres |
| Complejidad | Media | Media-Alta | Alta | Muy alta |
| Curva de aprendizaje | Moderada | Moderada | Alta | Muy alta |
| Ideal para | Dominios con infraestructura variable | Sistemas con muchas lecturas | Dominios complejos | Sistemas serios y evolutivos |
| No ideal para | CRUDs simples | CRUDs simples | Dominios simples | Prototipos |
| Componentes clave | Ports, Adapters, Aggregates | Commands, Queries, Buses | Bounded Contexts, Ubiquitous Language | Todo lo anterior |
| Testing | Fácil (dominio aislado) | Fácil (separado) | Fácil (dominio rico) | Fácil pero verboso |
| Escalabilidad | Buena | Muy buena (lectura) | Buena | Excelente |
Diagrama de Decisión
Datos accesibles del diagrama
Nodos
- A — ¿El sistema es simple?
- B — CRUD clásico
- C — ¿La infraestructura cambiará?
- D — Hexagonal
- E — ¿Lectura y escritura son distintas?
- F — CQRS
- G — ¿El dominio es complejo?
- H — DDD
- I — Arquitectura en capas
- J — ¿También necesita CQRS?
- K — Hexagonal + CQRS
- L — Hexagonal solo
- M — ¿También necesita DDD?
- N — DDD + CQRS
- O — CQRS solo
- P — ¿También necesita Hexagonal?
- Q — Hexagonal + DDD
- R — DDD solo
- S — ¿También necesita DDD?
- T — Híbrido / Hexagonal + CQRS + DDD
- U — Combinación parcial
Conexiones
- A (¿El sistema es simple?) → B (CRUD clásico) — Sí
- A (¿El sistema es simple?) → C (¿La infraestructura cambiará?) — No
- C (¿La infraestructura cambiará?) → D (Hexagonal) — Sí
- C (¿La infraestructura cambiará?) → E (¿Lectura y escritura son distintas?) — No
- E (¿Lectura y escritura son distintas?) → F (CQRS) — Sí
- E (¿Lectura y escritura son distintas?) → G (¿El dominio es complejo?) — No
- G (¿El dominio es complejo?) → H (DDD) — Sí
- G (¿El dominio es complejo?) → I (Arquitectura en capas) — No
- D (Hexagonal) → J (¿También necesita CQRS?)
- J (¿También necesita CQRS?) → K (Hexagonal + CQRS) — Sí
- J (¿También necesita CQRS?) → L (Hexagonal solo) — No
- F (CQRS) → M (¿También necesita DDD?)
- M (¿También necesita DDD?) → N (DDD + CQRS) — Sí
- M (¿También necesita DDD?) → O (CQRS solo) — No
- H (DDD) → P (¿También necesita Hexagonal?)
- P (¿También necesita Hexagonal?) → Q (Hexagonal + DDD) — Sí
- P (¿También necesita Hexagonal?) → R (DDD solo) — No
- K (Hexagonal + CQRS) → S (¿También necesita DDD?)
- N (DDD + CQRS) → S (¿También necesita DDD?)
- Q (Hexagonal + DDD) → S (¿También necesita DDD?)
- S (¿También necesita DDD?) → T (Híbrido / Hexagonal + CQRS + DDD) — Sí
- S (¿También necesita DDD?) → U (Combinación parcial) — No
Diagrama visual de la mezcla
Datos accesibles del diagrama
Nodos
- A — DDD / negocio + reglas + bounded contexts
- B — Hexagonal / puertos + adaptadores + aislamiento técnico
- C — CQRS - Commands / escribir / cambiar
- D — CQRS - Queries / leer / consultar
- E — Write Model / reglas de negocio
- F — Read Model / vistas optimizadas
- G — Infraestructura / REST / JPA / H2
Conexiones
- A (DDD / negocio + reglas + bounded contexts) → B (Hexagonal / puertos + adaptadores + aislamiento técnico) — modela el dominio
- B (Hexagonal / puertos + adaptadores + aislamiento técnico) → C (CQRS - Commands / escribir / cambiar) — expone casos de uso
- B (Hexagonal / puertos + adaptadores + aislamiento técnico) → D (CQRS - Queries / leer / consultar) — expone casos de uso
- C (CQRS - Commands / escribir / cambiar) → E (Write Model / reglas de negocio)
- D (CQRS - Queries / leer / consultar) → F (Read Model / vistas optimizadas)
- E (Write Model / reglas de negocio) → G (Infraestructura / REST / JPA / H2)
- F (Read Model / vistas optimizadas) → G (Infraestructura / REST / JPA / H2)
