Diseño de Arquitecturas Hexagonales en Sistemas Distribuidos
Patrones prácticos para aislar la lógica de dominio central de la infraestructura, adapters externos y protocolos de transporte.
La evolución de sistemas monolíticos hacia servicios distribuidos a menudo introduce acoplamientos accidentales si no se establecen fronteras arquitectónicas claras. En Klyntex Studio, el patrón de Puertos y Adaptadores (Arquitectura Hexagonal) constituye el estándar fundamental para desacoplar las reglas de negocio de los detalles de implementación tecnológica.
1. El Núcleo de Dominio Libre de Dependencias
El principio rector es la inversión de dependencias: el núcleo de dominio (Domain Entities, Value Objects, Domain Events) nunca debe importar librerías de persistencia (como Prisma, Drizzle o TypeORM), ni frameworks de transporte (Express, Fastify o gRPC).
// src/core/domain/entities/order.entity.ts
export interface OrderProps {
id: string;
customerId: string;
totalAmount: number;
status: 'PENDING' | 'SETTLED' | 'CANCELLED';
}
export class Order {
private constructor(private readonly props: OrderProps) {}
public static create(customerId: string, totalAmount: number): Order {
if (totalAmount <= 0) {
throw new Error('El importe total debe ser estrictamente positivo');
}
return new Order({
id: crypto.randomUUID(),
customerId,
totalAmount,
status: 'PENDING',
});
}
public settle(): void {
if (this.props.status !== 'PENDING') {
throw new Error('Solo órdenes en estado pendiente pueden ser liquidadas');
}
this.props.status = 'SETTLED';
}
public get snapshot(): Readonly<OrderProps> {
return Object.freeze({ ...this.props });
}
}
2. Puertos (Inbound & Outbound)
Los puertos definen los contratos formales a través de interfaces TypeScript estrictas:
- Primary / Driving Ports (Inbound): Casos de uso invocados por clientes HTTP, colas o tareas programadas.
- Secondary / Driven Ports (Outbound): Interfaces que el dominio requiere para persistir datos o emitir notificaciones.
// src/core/ports/order-repository.port.ts
import type { Order } from '../domain/entities/order.entity';
export interface OrderRepositoryPort {
findById(id: string): Promise<Order | null>;
save(order: Order): Promise<void>;
}
3. Beneficios Observados en Producción
- Testabilidad pura: Las suites unitarias del dominio se ejecutan en microsegundos sin necesidad de levantar bases de datos ni contenedores Docker.
- Sustituibilidad de infraestructura: Es posible migrar de PostgreSQL a DynamoDB o introducir capas de caché Redis modificando únicamente adaptadores aislados.
- Mantenibilidad a largo plazo: La deuda técnica se mantiene controlada al evitar filtraciones de abstracción.