Este sistema está fundamentado en los principios de Domain-Driven Design (DDD) combinado con Arquitectura Hexagonal (Puertos y Adaptadores). Cada microservicio representa un Bounded Context del dominio bancario, con su propio modelo de dominio, lenguaje ubicuo y reglas de negocio encapsuladas.
Principio fundamental: El dominio de negocio está en el centro, completamente aislado de detalles técnicos de infraestructura mediante puertos y adaptadores.
El sistema bancario BP permite a los usuarios gestionar sus finanzas mediante aplicaciones web (SPA) y móvil, con las siguientes capacidades principales:
En DDD, un Bounded Context es una frontera explícita dentro de la cual un modelo de dominio específico es válido. Cada contexto tiene su propio lenguaje ubicuo (Ubiquitous Language) y sus propias reglas de negocio.
En nuestro sistema bancario, identificamos 3 Bounded Contexts principales:
| Bounded Context | Responsabilidad de Dominio | Entidades Principales | Lenguaje Ubicuo |
|---|---|---|---|
| Context: Históricos | Gestión de consultas y reportes de transacciones históricas | Transaction, AccountStatement, MovementFilter | Movimiento, Saldo, Período, Filtro |
| Context: Transferencias | Ejecución de transferencias bancarias con consistencia transaccional | Transfer, Account, TransferSaga, Compensation | Transferencia, Débito, Crédito, Compensación |
| Context: Autenticación | Control de acceso, identidad y autorización de usuarios | User, Session, BiometricCredential, Token | Usuario, Sesión, Credencial, Autenticación |
Cada Bounded Context (microservicio) sigue la Arquitectura Hexagonal, que separa el dominio de negocio de los detalles de infraestructura mediante:
Responsabilidad: Lógica de negocio pura, independiente de frameworks y tecnología.
Componentes:
Responsabilidad: Contratos que definen cómo el dominio se comunica con el exterior.
Tipos de Puertos:
Responsabilidad: Implementaciones concretas de los puertos, conectando el dominio con tecnologías específicas.
Tipos de Adaptadores:
flowchart TD Users["USUARIOS FINALES"] WebSPA["SPA Web (ReactJS)
[Adaptador: UI]"] MobileApp["App Móvil (React Native)
[Adaptador: UI]"] WAF["WAF (Cloudflare/AWS) - DDoS Protection - Bot Protection - OWASP Top 10"] Gateway["APOLLO GATEWAY
.GraphQL Federation
.Puerto de Entrada Principal
"] subgraph BC1["🔷 BOUNDED CONTEXT: HISTÓRICOS"] MS1["Microservicio Históricos [Arquitectura Hexagonal] • Dominio: Transactions • Puertos: Queries • Adaptadores: GraphQL, MongoDB"] end subgraph BC2["🔷 BOUNDED CONTEXT: TRANSFERENCIAS"] MS2["Microservicio Transferencias [Arquitectura Hexagonal] • Dominio: Transfers + SAGA • Puertos: Commands • Adaptadores: GraphQL, PostgreSQL, Kafka"] end subgraph BC3["🔷 BOUNDED CONTEXT: AUTENTICACIÓN"] MS3["Microservicio Autenticación [Arquitectura Hexagonal] • Dominio: Identity • Puertos: AuthService • Adaptadores: REST, OAuth 2.0"] end Kafka["KAFKA MESSAGE BUS [Adaptador de Comunicación] Topics: SAGA, Domain Events, Logs"] Users --> WebSPA & MobileApp WebSPA & MobileApp -->|HTTPS/TLS 1.3| WAF WAF --> Gateway Gateway --> MS1 & MS2 & MS3 MS1 & MS2 & MS3 --> Kafka classDef default fill:#f9f9f9,stroke:#333,stroke-width:2px; classDef gateway fill:#e1f3d8,stroke:#82c91e; classDef services fill:#d0ebff,stroke:#339af0; classDef messaging fill:#fff3bf,stroke:#f59f00; class WAF,Gateway gateway; class MS1,MS2,MS3 services; class Kafka messaging;
Decisión: Adoptar DDD como filosofía de diseño principal
Justificación:
Decisión: Implementar arquitectura hexagonal en cada microservicio
Justificación:
Decisión: Cloudflare WAF o AWS WAF como primera línea de defensa
Justificación:
Decisión: Punto único de entrada con GraphQL como protocolo principal (95%)
Justificación:
Decisión: Cada Bounded Context es un microservicio independiente
Justificación:
| Componente | Tecnología | Propósito | Justificación |
|---|---|---|---|
| Frontend Web | React 18+ SPA | Interfaz de usuario web | Ecosistema maduro, reutilización de componentes con móvil |
| Frontend Móvil | React Native 0.73+ | Apps iOS/Android | Compartir código con web, menor time-to-market |
| API Gateway | Apollo Gateway | Punto de entrada único | GraphQL Federation, mejor que REST tradicional |
| Microservicios | Node.js + GraphQL | Lógica de negocio (Dominio DDD) | Worker Threads para paralelismo, GraphQL eficiente |
| Message Bus | Apache Kafka | Comunicación asíncrona entre contexts | Alta throughput, durabilidad, ideal para SAGA y Domain Events |
| Orquestador | Kubernetes | Gestión de contenedores | Autoescalado, self-healing, standard de industria |
| Bases de Datos | PostgreSQL + MongoDB | Persistencia (Database per Service) | CQRS: PostgreSQL para escritura, MongoDB para lectura |
| Caché | Redis | Caché + Sesiones + SAGA State | Performance, estado temporal de SAGAs |
| Observabilidad | ELK + Prometheus | Logs + Métricas | Stack estándar, integración con K8s |
sequenceDiagram actor U as Usuario (App) participant A as Adaptador UI participant W as WAF participant AG as Apollo Gateway (Puerto) participant GR as GraphQL Resolver (Adaptador Entrada) participant UC as Use Case (Puerto Entrada) participant DS as Domain Service (Dominio) participant RP as Repository Port (Puerto Salida) participant DB as DB Adapter (Adaptador Salida) participant Cache as Redis Cache U->>A: Solicita movimientos A->>W: GraphQL Query (HTTPS) Note over W: Valida seguridad W->>AG: ✓ Request limpia Note over AG: Puerto de entrada principal
Valida JWT AG->>GR: Enruta query Note over GR: Adaptador de entrada GraphQL GR->>UC: Ejecuta caso de uso Note over UC: Puerto de entrada
GetAccountMovements UC->>DS: Aplica lógica dominio Note over DS: DOMINIO PURO
Reglas de negocio DS->>RP: Solicita datos Note over RP: Puerto de salida
TransactionRepository interface RP->>Cache: Intenta cache Cache-->>RP: Cache miss RP->>DB: Query BD Note over DB: Adaptador MongoDB
Implementa Repository Port DB-->>RP: Retorna datos RP-->>DS: Entities de dominio DS-->>UC: Datos procesados UC-->>GR: DTO de salida GR-->>AG: GraphQL response AG-->>W: Datos optimizados W-->>A: 4 KB vs 11 KB REST A-->>U: Renderiza UI Note over U: 120ms vs 350ms latencia
El código usa exactamente los mismos términos que los expertos del dominio. No hay traducción entre negocio y técnico: "Transfer", "Account", "Compensation" son nombres tanto en el código como en las conversaciones de negocio.
Cada contexto (Históricos, Transferencias, Autenticación) tiene su propio modelo y no comparte entidades con otros contextos. La comunicación es via Domain Events.
La lógica de negocio está completamente aislada de frameworks, bases de datos, y APIs. El dominio no conoce GraphQL, ni PostgreSQL, ni Kafka.
Conjuntos de entidades relacionadas se agrupan en Agregados con una raíz que garantiza la consistencia. Ejemplo: TransferAggregate controla Transfer + TransferLine.
Los hechos importantes del dominio se modelan como eventos: TransferCompleted, AccountDebited, CompensationTriggered. Facilitan comunicación entre contextos.
El dominio define interfaces (puertos), y la infraestructura las implementa (adaptadores). Nunca al revés. El dominio no depende de nadie.
Toda comunicación con el exterior se hace mediante puertos (interfaces) y adaptadores (implementaciones). Podemos cambiar de GraphQL a gRPC sin tocar el dominio.
El dominio se testea con mocks de puertos, sin necesidad de levantar bases de datos o servidores. Tests rápidos y confiables.
Diseñar APIs antes de implementar, con GraphQL como protocolo principal para máxima eficiencia.
Todas las operaciones críticas son idempotentes (pueden ejecutarse múltiples veces sin efectos adversos).
Logs estructurados, métricas y traces implementados desde el primer día.
Seguridad integrada en cada capa, no agregada después.
Detectar fallos rápidamente y recuperarse automáticamente.
| Patrón | Aplicación | Beneficio |
|---|---|---|
| Bounded Context | División del sistema en contextos | Modelos de dominio independientes y mantenibles |
| Hexagonal Architecture | Estructura interna de microservicios | Dominio aislado de tecnología |
| CQRS | Separación lectura/escritura | Optimización de performance en consultas |
| SAGA | Transacciones distribuidas | Consistencia eventual con compensación |
| Event Sourcing | Auditoría y sincronización | Trazabilidad completa, replay de eventos |
| Domain Events | Comunicación entre Bounded Contexts | Desacoplamiento total entre contextos |
| Circuit Breaker | Llamadas entre servicios | Previene cascading failures |
| Bulkhead | Aislamiento de recursos | Fallo de un tipo de operación no afecta otros |
| API Gateway | Punto de entrada único | Seguridad centralizada, routing inteligente |
| Database per Service | Cada microservicio su BD | Independencia total, escalabilidad |
| Capa | Responsabilidad Principal | Tecnologías Clave | Flujo / Interacciones |
|---|---|---|---|
| Capa 1: Presentación 🖼️ | Interfaz con la que interactúa el usuario final (Cliente Web/Móvil). [Adaptador de UI] |
React, React Native,
Apollo Client
|
Envía peticiones del usuario hacia el API Gateway. |
| Capa 2: API Gateway 🚪 | Punto de entrada único que gestiona la seguridad y el enrutamiento. [Puerto Principal] |
Apollo Gateway, Kong,
NGINX
|
Recibe peticiones de la Capa 1 y las dirige a los servicios correspondientes. |
| Capa 3: Bounded Contexts 🧠 | Microservicios con arquitectura hexagonal. Contienen dominio + puertos + adaptadores. [Microservicios DDD] | Microservicios en Node.js |
Recibe peticiones del API Gateway y utiliza las capas de Mensajería y Persistencia. |
| Capa 4: Mensajería 📨 | Gestiona comunicación asíncrona y Domain Events entre Bounded Contexts. [Adaptador de Comunicación] | Apache Kafka |
Es utilizada por los Bounded Contexts para publicar/consumir Domain Events. |
| Capa 5: Persistencia 💾 | Almacenamiento y recuperación de datos. Cada contexto tiene su propia BD. [Adaptadores de BD] | PostgreSQL, MongoDB, Redis |
Provee los datos que los Domain Services necesitan para operar. |
| Capa 6: Infraestructura 🏗️ | Orquesta y ejecuta todos los servicios del backend en contenedores. | Kubernetes, Docker |
Es la base sobre la que se despliegan y operan las capas 3, 4 y 5. |
| Capa Transversal: Observabilidad 📊 | Monitorea la salud y el rendimiento de todo el sistema. |
Prometheus, Grafana,
ELK Stack
|
Recopila métricas y logs de todas las demás capas para análisis y alertas. |
Cada microservicio sigue esta estructura de carpetas que refleja las capas hexagonales:
ms-transferencias/
├── src/
│ ├── domain/ # 🟦 CAPA DE DOMINIO (Centro del Hexágono)
│ │ ├── entities/ # Entidades con identidad
│ │ │ ├── Transfer.ts # Entidad Transfer
│ │ │ ├── Account.ts # Entidad Account
│ │ │ └── TransferLine.ts
│ │ ├── value-objects/ # Objetos de valor inmutables
│ │ │ ├── Money.ts
│ │ │ ├── AccountNumber.ts
│ │ │ └── TransferId.ts
│ │ ├── aggregates/ # Raíces de agregado
│ │ │ └── TransferAggregate.ts
│ │ ├── events/ # Domain Events
│ │ │ ├── TransferCreated.ts
│ │ │ ├── TransferCompleted.ts
│ │ │ └── CompensationTriggered.ts
│ │ ├── services/ # Domain Services
│ │ │ ├── TransferValidator.ts
│ │ │ └── CompensationService.ts
│ │ └── exceptions/ # Excepciones de dominio
│ │ └── InsufficientFundsError.ts
│ │
│ ├── application/ # 🟩 PUERTOS (Casos de Uso)
│ │ ├── ports/
│ │ │ ├── in/ # Puertos de Entrada (Driving)
│ │ │ │ ├── CreateTransferUseCase.ts
│ │ │ │ ├── CancelTransferUseCase.ts
│ │ │ │ └── GetTransferStatusUseCase.ts
│ │ │ └── out/ # Puertos de Salida (Driven)
│ │ │ ├── TransferRepository.ts # Interface
│ │ │ ├── AccountRepository.ts # Interface
│ │ │ ├── EventPublisher.ts # Interface
│ │ │ └── SagaStateRepository.ts # Interface
│ │ ├── dto/ # 📦 DTOs (Data Transfer Objects)
│ │ │ ├── CreateTransferDto.ts # Input DTO
│ │ │ ├── TransferResponseDto.ts # Output DTO
│ │ │ ├── CancelTransferDto.ts
│ │ │ └── TransferStatusDto.ts
│ │ ├── mappers/ # Mappers: DTO ↔ Domain Entity
│ │ │ ├── TransferMapper.ts
│ │ │ └── AccountMapper.ts
│ │ └── usecases/ # Implementación de casos de uso
│ │ ├── CreateTransferUseCaseImpl.ts
│ │ └── CancelTransferUseCaseImpl.ts
│ │
│ ├── infrastructure/ # 🟨 ADAPTADORES
│ │ ├── adapters/
│ │ │ ├── in/ # Adaptadores de Entrada
│ │ │ │ ├── graphql/
│ │ │ │ │ ├── TransferResolver.ts
│ │ │ │ │ └── schema.graphql
│ │ │ │ └── kafka/
│ │ │ │ └── TransferEventConsumer.ts
│ │ │ └── out/ # Adaptadores de Salida
│ │ │ ├── postgres/
│ │ │ │ └── PostgresTransferRepository.ts
│ │ │ ├── kafka/
│ │ │ │ └── KafkaEventPublisher.ts
│ │ │ └── redis/
│ │ │ └── RedisSagaStateRepository.ts
│ │ ├── config/ # Configuración
│ │ │ ├── database.ts
│ │ │ └── kafka.ts
│ │ └── migrations/ # Migraciones de BD
│ │
│ └── shared/ # Código compartido
│ ├── utils/
│ └── types/
│
├── tests/
│ ├── unit/ # Tests del dominio (puros)
│ ├── integration/ # Tests de adaptadores
│ └── e2e/ # Tests end-to-end
│
└── package.json
Los DTOs son objetos simples que transfieren datos entre capas. Son cruciales en arquitectura hexagonal porque:
Flujo típico: GraphQL Request → DTO → Mapper → Domain Entity → Use Case → Domain Entity → Mapper → DTO → GraphQL Response
Ejemplo práctico:
// 1. GraphQL Resolver recibe el request (Adaptador de Entrada)
async createTransfer(args: CreateTransferInput) {
// 2. Convierte input a DTO
const dto = new CreateTransferDto(args);
// 3. Mapper convierte DTO → Domain Entity
const transfer = TransferMapper.toDomain(dto);
// 4. Ejecuta caso de uso con entidad de dominio
const result = await createTransferUseCase.execute(transfer);
// 5. Mapper convierte Domain Entity → DTO de respuesta
const responseDto = TransferMapper.toDto(result);
// 6. Retorna DTO al cliente
return responseDto;
}