← dev-notes
ARQUITECTURA · PARTE 1 DE 2

Arquitectura
Hexagonal

Fundamentos, el problema que resuelve, Ports & Adapters y la regla de dependencias

HEX-ARCH · P1
⬡ Ports & Adapters 🧅 Capas de la arquitectura 🔄 Regla de dependencias 🧪 Testabilidad
01 El problema que resuelve
❌ Arquitectura tradicional acoplada
La lógica de negocio depende de todo
El dominio importa directamente la BD (JPA, SQLAlchemy) — cambiar de Postgres a Mongo rompe la lógica de negocio.
Los controladores HTTP llaman directo a los repositorios — no puedes probar la lógica sin levantar un servidor.
El servicio envía emails usando SMTP directamente — para testear, o mandas emails reales o mockeas librerías externas.
Una migración de framework (de Spring a Quarkus) implica reescribir lógica de negocio.
Los tests son lentos porque dependen de bases de datos reales, queues reales y APIs externas.
✅ Con Arquitectura Hexagonal
El dominio no depende de nada externo
El dominio define interfaces (Ports) — la implementación real de la BD va afuera. Cambias Postgres por Mongo sin tocar el dominio.
Puedes testear toda la lógica de negocio con implementaciones en memoria, sin HTTP ni BD real.
El servicio de email es un Port. Para tests usas un FakeEmailAdapter, en prod usas SmtpEmailAdapter.
El framework es solo un Adapter. Migrás de Spring a Quarkus cambiando solo los controllers, no el dominio.
Los tests unitarios son rapidísimos porque el dominio no tiene dependencias externas.
02 Diagrama — Ports & Adapters
DOMAIN
Lógica de negocio
Entities · Use Cases
Value Objects
Port: Input (driving)
Port: Output (driven)
Port: Input
Port: Output
🌐 REST Controller
HTTP Adapter
⌨️ CLI Command
CLI Adapter
📨 Event Consumer
Queue Adapter
🗄 SQL Repository
DB Adapter
📧 SMTP Service
Email Adapter
S3 Storage
Storage Adapter
Domain Core
Lógica de negocio pura. No importa nada externo. Solo define interfaces (Ports). Es el hexágono central.
Capa de Aplicación
Orquesta los casos de uso. Coordina el dominio con los puertos. No contiene lógica de negocio.
Capa de Infraestructura
Implementaciones concretas (Adapters). BD, HTTP, email, queues. Conoce el dominio, pero el dominio no la conoce.
→ Driving (izquierda)
Adapters que invocan el dominio: HTTP, CLI, Consumers de eventos.
→ Driven (derecha)
Adapters que el dominio invoca: BD, email, storage, APIs externas.
03 Ports & Adapters — en detalle
Tipo
¿Qué es?
¿Quién lo define?
Ejemplos reales
Port de entrada
Driving Port
Interfaz que define cómo el mundo exterior puede invocar el dominio. Es el contrato de lo que la app puede hacer.
El dominio. Define la interfaz (ej: OrderService) sin saber quién la llama.
CreateOrderUseCase GetUserQuery ProcessPaymentUseCase
Adapter de entrada
Driving Adapter
Implementación concreta que traduce una señal externa al lenguaje del dominio e invoca el Port de entrada.
La infraestructura. Conoce el framework HTTP, CLI o queue. Llama al dominio.
RestOrderController GraphQLResolver KafkaConsumer
Port de salida
Driven Port
Interfaz que define lo que el dominio necesita del exterior (persistencia, notificaciones, etc.) sin saber cómo se implementa.
El dominio. Define la interfaz sin importar la tecnología de turno (JPA, Redis, SMTP).
OrderRepository EmailNotifier PaymentGateway
Adapter de salida
Driven Adapter
Implementación concreta del Port de salida. Contiene el código de JPA, SMTP, S3, Redis, etc.
La infraestructura. Implementa la interfaz del dominio usando tecnología específica.
JpaOrderRepository SmtpEmailNotifier StripePaymentGateway
04 La regla de dependencias — el principio fundamental
INFRAESTRUCTURA
Adapters
RestController · JpaRepository
SmtpEmailSender · KafkaProducer
S3StorageAdapter
depende de
APLICACIÓN
Use Cases
CreateOrderUseCase
ProcessPaymentUseCase
GetOrderQueryService
depende de
DOMINIO
Entities & Ports
Order · OrderItem · Customer
OrderRepository (interface)
PaymentGateway (interface)
🚫
no depende de nada
La regla en una frase: Las dependencias siempre apuntan hacia adentro. La infraestructura conoce la aplicación, la aplicación conoce el dominio, pero el dominio no conoce a nadie. Si en tu código el dominio importa algo de infraestructura, la arquitectura hexagonal está rota.
DIP (SOLID): Este límite es la aplicación arquitectónica del Principio de inversión de dependencias — el dominio (alto nivel) define puertos (abstracciones); los adaptadores (detalles) implementan esos contratos y dependen del dominio, no al revés. Infografía DIP →
05 Conceptos clave del dominio
🧱
01
Entity
Identidad única Mutable ID
Objeto del dominio con identidad única que persiste en el tiempo. Dos entidades son iguales si tienen el mismo ID, aunque sus atributos difieran.
Ejemplo: Order, User, Product. Una Order con id=42 es la misma Order aunque cambie su estado de PENDING a PAID.
💎
02
Value Object
Sin identidad Inmutable Igual por valor
Objeto que describe una característica pero sin identidad propia. Es inmutable — si necesitas "cambiar" un VO, creas uno nuevo. Dos VOs son iguales si sus valores son iguales.
Ejemplo: Money(100, "CLP"), Email("user@mail.com"), Address. Dos Money(100, "CLP") son exactamente iguales.
🌳
03
Aggregate
Aggregate Root Consistencia Boundary
Cluster de entidades y VOs que se tratan como una unidad. El Aggregate Root es la única puerta de entrada — nadie puede modificar los objetos internos sin pasar por él.
Ejemplo: Order es el Aggregate Root. Para añadir un OrderItem debes llamar a order.addItem(), nunca manipular el item directamente.
🎯
04
Use Case
Caso de uso Orquestación Application Service
Representa una operación específica del sistema desde el punto de vista del usuario. Vive en la capa de aplicación, orquesta entidades y puertos, pero no contiene lógica de negocio.
Ejemplo: CreateOrderUseCase: carga el usuario, valida el stock, crea la Order, llama al PaymentGateway (port), guarda en el OrderRepository (port), notifica por email.
📢
05
Domain Event
Inmutable Pasado Desacoplamiento
Representa algo que ocurrió en el dominio. Siempre en pasado. Permite que otros componentes reaccionen a cambios sin que el dominio conozca a sus suscriptores.
Ejemplo: OrderPlaced, PaymentProcessed, UserRegistered. Cuando se publica OrderPlaced, el servicio de email reacciona sin que el dominio lo sepa.
🗺
06
Bounded Context
DDD Límite explícito Lenguaje ubiquo
Límite explícito dentro del cual un modelo de dominio específico es válido. Dentro del contexto, los términos tienen un significado único y preciso (Ubiquitous Language).
Ejemplo: "Cliente" en el contexto de Ventas es distinto a "Cliente" en el contexto de Soporte. Cada Bounded Context tiene su propio modelo y su propio hexágono.
Un microservicio bien diseñado suele alinearse con un Bounded Context. Arquitectura Hexagonal + DDD es la combinación más frecuente en la práctica.