DDD no empieza con una carpeta `domain`
Eric Evans presenta DDD como un conjunto de patrones y definiciones para trabajar con modelos de dominio, mientras Martin Fowler pone el foco en modelar dominios complejos y desarrollar un lenguaje compartido entre desarrolladores y expertos de negocio. [1][2][4]
Por tanto, una estructura `domain/application/infrastructure` no convierte por sí sola una aplicación en DDD. Puede haber carpetas perfectamente nombradas y seguir trabajando solo con DTO y lógica procedural. También puede aplicarse DDD estratégico sin una estructura canónica por capas.
DDD estratégico y DDD táctico operan en niveles distintos
El DDD estratégico trata la división de un dominio amplio en subdominios y Bounded Contexts y sus relaciones. Microsoft describe el análisis de dominio como la identificación de subdominios y contextos, y Fowler sitúa Bounded Context como patrón central del diseño estratégico. [3][7]
El DDD táctico trabaja dentro de un contexto concreto e incluye Entities, Value Objects, Aggregates y Domain Services. Microsoft separa explícitamente el análisis estratégico del modelado táctico. [8]
En frontend suele ser mejor comenzar por el nivel estratégico y decidir después qué contextos necesitan realmente un modelo táctico rico.
Un Bounded Context no es una capa técnica
Fowler describe un Bounded Context como una frontera en la que un modelo permanece coherente y sus términos tienen significado no ambiguo. Un mismo concepto, como `Customer` o `Product`, puede tener significados distintos en diferentes contextos. [3]
Por eso `frontend`, `backend`, `React app`, `route` o `micro-frontend` no son automáticamente Bounded Contexts. Los límites nacen del modelo y del lenguaje del dominio, no de la tecnología. Guías prácticas de DDD en frontend hacen la misma advertencia. [15][16]
Cuándo DDD en frontend tiene sentido
| Señal | Dirección | Por qué |
|---|---|---|
| Muchas reglas de negocio cambiantes | DDD | Un modelo de dominio evita dispersar las reglas |
| Los mismos conceptos significan cosas distintas según el área | DDD | Bounded Contexts permiten varios modelos coherentes |
| Colaboran varios equipos y expertos de dominio | DDD | Ubiquitous Language reduce ambigüedad |
| CRUD simple y formularios | Modularidad más simple | El coste de DDD táctico puede superar el beneficio |
| El frontend refleja principalmente una API | Modularidad más simple | Hay poca lógica de dominio propia del cliente |
| Aplicación pequeña con un dominio coherente | Modularidad más simple | Capas y límites adicionales pueden aportar poco valor |
La señal más clara son reglas de negocio complejas y cambiantes visibles también en el cliente: configuradores, procesos de varios pasos, pricing, permisos, transiciones de estado, workflows o modelos distintos del mismo concepto según el área del producto.
DDD también aporta valor cuando varios equipos trabajan en un producto grande y las mismas palabras significan cosas distintas en distintas áreas. Bounded Contexts existen precisamente para mantener varios modelos coherentes. [3]
DDD Crew recomienda comprender primero el dominio, identificar subdominios estratégicamente importantes, definir después las responsabilidades de los contextos y solo entonces codificar el modelo. [10]
Cuándo DDD se convierte en sobrearquitectura
Si el frontend se limita principalmente a obtener datos, mostrarlos, editar formularios y enviar comandos CRUD sencillos sin reglas de negocio relevantes en el cliente, el conjunto completo de patrones tácticos DDD rara vez resuelve un problema real.
Microsoft indica expresamente que para un contexto CRUD simple un modelo de datos anémico puede ser suficiente y que patrones DDD más complejos no siempre compensan. Un modelo rico resulta más útil cuando existen muchas reglas de negocio cambiantes. [9]
El mismo criterio debería usarse en frontend: la sofisticación arquitectónica debe corresponder a la complejidad del dominio, no a la ambición técnica del equipo.
Ubiquitous Language también debe verse en el código de UI
Ubiquitous Language es un lenguaje común y riguroso construido por desarrolladores y expertos de dominio alrededor del modelo. Fowler subraya que debe evolucionar a medida que el equipo entiende mejor el dominio. [4]
En frontend, los nombres de casos de uso, acciones, tipos, módulos, pantallas y estados deberían usar vocabulario de negocio cuando expresan comportamiento de dominio.
Si negocio habla de `approveApplication` pero el código usa `setFlag2` o `handleData`, el código deja de funcionar como representación clara del lenguaje del dominio.
Organiza un frontend grande por dominios, no solo por tipos de archivo
Fowler señala que una estructura superior `view/model/data` puede bastar en sistemas pequeños, pero al crecer suele ser mejor dividir el nivel superior en módulos orientados por dominio que contengan internamente sus propias capas. [5]
La documentación actual de Nx sigue un enfoque similar: las carpetas pueden convertirse en límites de ownership por dominio y dentro de cada dominio las bibliotecas pueden clasificarse como `feature`, `ui`, `data-access` y `util`. [12]
Una estructura práctica puede ser `libs/orders/...`, `libs/billing/...`, `libs/identity/...` en lugar de un árbol global `components/`, `services/`, `models/`.
Separa comportamiento de presentación y comportamiento de dominio
Fowler describe la separación entre presentación, lógica de dominio y acceso a datos como una forma eficaz de modularización. También señala que la UI suele ser más difícil de probar, por lo que mantener lógica de dominio fuera de la presentación mejora la testabilidad. [5][6]
Un componente frontend debería centrarse en renderizar, manejar eventos de UI y delegar operaciones. Reglas como si un pedido puede cancelarse, cómo calcular un descuento o qué transición de estado es válida deberían vivir fuera de JSX, plantillas o clases de componente.
Esto no prohíbe la lógica de presentación. Validación de vista, visibilidad, foco local o animación no son lo mismo que reglas de negocio.
Capas prácticas dentro de un contexto frontend
| Capa | Responsabilidad | Ejemplos |
|---|---|---|
| presentation / ui | Renderizado y comportamiento de interfaz | components, routes, view state |
| application | Coordinación de casos de uso | commands, use cases, orchestration |
| domain | Reglas, conceptos e invariantes de dominio | entities, value objects, policies |
| infrastructure / data-access | Integraciones técnicas | HTTP, storage, SDKs, mappers |
DDD no impone una estructura de carpetas concreta. Separar `presentation`, `application`, `domain` e `infrastructure` es una interpretación práctica de la separación de responsabilidades, no un requisito formal de Evans. [1][5]
La capa domain puede contener reglas, conceptos y comportamiento independientes del framework; application coordina casos de uso; infrastructure adapta HTTP, storage y SDK externos; presentation contiene componentes, routing y estado puramente visual.
El modelo de API y el modelo de dominio frontend no tienen que ser iguales
Un Bounded Context puede tener su propio modelo de un concepto y distintos contextos pueden mapear entre representaciones diferentes. Fowler usa `Customer` y `Product` como ejemplos habituales de términos con significados distintos según el contexto. [3]
Por ello, un DTO de backend no tiene que llegar directamente a todos los componentes. Un mapper o adapter puede convertir el contrato de transporte al modelo que utiliza un contexto frontend.
Esta frontera aporta más valor cuando una API sirve a varios clientes, es legacy, combina varios dominios o evoluciona de forma independiente. Para un endpoint CRUD simple, una capa adicional puede sobrar.
Entities, Value Objects y Aggregates: úsalos selectivamente
DDD distingue conceptos como Entities, Value Objects, Services y Aggregates. Fowler los destaca como parte del vocabulario de Evans y Microsoft describe los agregados como un patrón táctico para mantener la coherencia del modelo. [2][8]
El frontend no debería copiar el modelo del backend uno a uno solo para tener las mismas clases. Un Value Object puede ser útil para `Money`, `DateRange` o `Email` si protege invariantes reales. Un Aggregate tiene sentido si el cliente necesita garantizar reglas de coherencia del modelo.
Si un tipo es solo datos para mostrar una tabla, un tipo TypeScript simple puede ser mejor que Entity, Factory, Repository y Service.
El estado de UI no es lo mismo que el estado de dominio
React trata la organización del estado como un problema de diseño y recomienda evitar estado redundante o duplicado. Es una buena base técnica, pero reducers, stores o signals no crean por sí mismos un modelo de dominio. [14]
Estado como `isModalOpen`, pestaña activa o posición de scroll pertenece a presentación. El ciclo de vida de un pedido, transiciones permitidas o reglas de un configurador pueden pertenecer al dominio.
Separar ambas categorías evita stores globales que mezclan datos del servidor, comportamiento de negocio y detalles de interfaz.
Los límites deben aplicarse, no solo documentarse
Nx permite definir tags de proyecto y restricciones declarativas de dependencias, por ejemplo para bloquear imports entre scopes o tipos de biblioteca. `@nx/enforce-module-boundaries` puede verificar imports durante lint. [11]
Nx también admite varias dimensiones de tags, lo que permite modelar por separado `scope`, tipo de biblioteca, estabilidad o separación client/server. [13]
Así, decisiones de DDD pueden convertirse en reglas de CI: `billing` no importa internos de `identity`, `ui` no depende de `data-access` y `domain` permanece independiente del framework.
Frontend y backend pueden compartir un dominio sin compartir un modelo idéntico
Un Bounded Context es una frontera de modelo y lenguaje, por lo que no debería dibujarse automáticamente sobre la frontera de red entre navegador y servidor. Guías prácticas de DDD frontend destacan que los contextos pueden atravesar capas técnicas y ser implementados conjuntamente por frontend y backend. [15][16]
La representación en cliente puede ser distinta de la representación en servidor debido a necesidades diferentes de interacción, estado local y presentación. Lo importante es conservar la semántica y mapear explícitamente, no copiar clases.
Tampoco debe crearse un micro-frontend únicamente porque exista un Bounded Context. La frontera de dominio y la frontera de despliegue son decisiones distintas.
La testabilidad es una de las razones más fuertes para aislar la lógica de dominio
Fowler cita la testabilidad como beneficio de separar presentación y dominio. La lógica fuera de la UI puede probarse sin renderizar componentes ni depender de detalles de interfaz. [5][6]
En frontend esto permite pruebas rápidas de reglas, Value Objects, casos de uso y transiciones de estado como TypeScript o JavaScript normal. Las pruebas de componentes siguen siendo necesarias, pero no tienen que ser el único lugar donde validar reglas de negocio.
Una señal de alarma es que cada cambio de regla exija montar un componente completo y simular router, store, HTTP y APIs del navegador.
Cómo introducir DDD en un frontend existente sin un gran rewrite
DDD Crew propone un proceso iterativo: comprender el dominio, identificar subdominios importantes, definir responsabilidades de Bounded Contexts y después codificar el modelo. [10]
En un frontend existente es más seguro elegir un área problemática, nombrar su lenguaje, establecer su límite, separar presentación de reglas, definir la API pública del módulo y migrar después otras funciones.
No es necesario migrar toda la aplicación. DDD puede aplicarse donde la complejidad de negocio compensa el coste de modelado y dejar las áreas simples como módulos de funcionalidad más ligeros.
- Empieza por un problema de negocio, no por las carpetas.
- Identifica términos, reglas y conceptos ambiguos.
- Define un Bounded Context y su contrato público.
- Separa reglas de dominio de componentes y DTO de transporte.
- Añade solo patrones tácticos que resuelvan complejidad concreta.
- Convierte los límites en reglas de lint o CI.
- Mide dependencias, ciclos, regresiones y coste de cambios transversales.
- No migres CRUD simples solo por simetría arquitectónica.

