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

  1. El controlador no contiene negocio

    • Solo traduce HTTP ↔ dominio.
    • No decide reglas de negocio.
  2. Los puertos viven en el dominio

    • No son “interfaces decorativas”.
    • Representan necesidades reales del negocio.
  3. La infraestructura no manda

    • JPA, REST, colas, etc. son detalles intercambiables.
  4. La entidad de dominio encapsula reglas

    • Order protege estados válidos.
    • Evita que la aplicación “haga trampas” con el estado.

Flujo visual

Flujo de arquitectura hexagonal desde una petición HTTP por los puertos y el dominio hasta un adaptador de infraestructura
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

  1. Bounded Context

    • Un mismo término puede significar cosas distintas según el contexto.
    • Por eso existen account y customer como contextos separados.
  2. Aggregate Root

    • Es el guardián de las invariantes.
    • En este repo, Account decide si se puede depositar, retirar o transferir.
  3. Value Objects

    • No tienen identidad propia, solo valor.
    • Money, AccountId, CustomerId son ejemplos perfectos.
  4. Domain Service

    • Se usa cuando la lógica no pertenece claramente a un solo aggregate.
    • TransferDomainService coordina una transferencia entre cuentas.
  5. Domain Events

    • Son hechos que ocurrieron en el dominio.
    • Permiten desacoplar reacciones posteriores.

Flujo visual

Flujo DDD desde una petición HTTP por el controlador y el servicio de aplicación hasta el repositorio de dominio y la persistencia JPA
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

  1. Command no devuelve datos de lectura

    • Un command expresa intención de cambio.
    • No debería usarse como query disfrazada.
  2. Read model no tiene por qué ser igual al write model

    • Product protege la escritura.
    • ProductView optimiza la consulta.
  3. La separación no es solo conceptual

    • Aquí hay APIs distintas:
      • /api/commands/products
      • /api/queries/products
  4. Los buses desacoplan el transporte

    • CommandBus y QueryBus evitan que el controlador conozca handlers concretos.

Flujo visual

Flujo CQRS con cadenas separadas de comandos y consultas desde el transporte HTTP hasta los adaptadores de base de datos de escritura y lectura
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

Capas de arquitectura hexagonal que conectan adaptadores de entrada, servicios de aplicación, componentes de dominio y adaptadores de infraestructura
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

Componentes CQRS separados en lado de comandos, lado de consultas y sus bases de datos de escritura y lectura
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 de las arquitecturas Hexagonal, CQRS, DDD e Hybrid
Tabla comparativa accesible
AspectoHexagonalCQRSDDDHybrid
Enfoque principalAislamiento del dominioSeparación lectura/escrituraLenguaje del negocioCombinación de las tres
ComplejidadMediaMedia-AltaAltaMuy alta
Curva de aprendizajeModeradaModeradaAltaMuy alta
Ideal paraDominios con infraestructura variableSistemas con muchas lecturasDominios complejosSistemas serios y evolutivos
No ideal paraCRUDs simplesCRUDs simplesDominios simplesPrototipos
Componentes clavePorts, Adapters, AggregatesCommands, Queries, BusesBounded Contexts, Ubiquitous LanguageTodo lo anterior
TestingFácil (dominio aislado)Fácil (separado)Fácil (dominio rico)Fácil pero verboso
EscalabilidadBuenaMuy buena (lectura)BuenaExcelente

Diagrama de Decisión

Árbol de decisión arquitectónica desde la simplicidad del sistema por infraestructura, separación de lectura y escritura, complejidad del dominio y combinaciones híbridas
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

Arquitectura híbrida donde DDD modela el dominio, los puertos hexagonales exponen casos de uso, CQRS separa comandos y consultas y ambos usan la infraestructura
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)