Cuando un backend crece, el problema rara vez es falta de endpoints. El problema suele ser que la lógica de negocio termina dispersa entre controladores, servicios, ORMs y ifs sueltos. Ahí es donde Domain-Driven Design (DDD) brilla: te obliga a poner el negocio en el centro y a organizar el código para que sea más fácil evolucionarlo.
En este post te explico qué es DDD, qué piezas lo componen, y cómo aterrizarlo en un proyecto .NET de forma práctica.
Qué es el Domain-Driven Design
DDD no es una arquitectura ni un framework. Es un enfoque de diseño para modelar software complejo basándote en el dominio (las reglas del negocio) y un lenguaje común con el equipo (el Ubiquitous Language).
La idea principal de Domain-Driven Design es: si el dominio (negocio) es importante, el código debe reflejarlo con claridad, y la lógica clave debe estar protegida dentro del modelo de dominio
El problema típico (sin DDD)
Muchas APIs suelen arrancar de la siguiente manera:
- Controller recibe request
- Valida un poco
- Llama a EF Core
- Calcula un poco en el controller o service
- Guarda
- Devuelve OK
También lo podemos representar de la siguiente forma:

Al principio funciona. Pero cuando llegan reglas como:
- Un pedido no puede pagarse si no tiene ítems
- Un pedido no se puede cancelar si ya se envió
- El precio final depende de descuentos, impuestos y moneda
- Si se crea un pedido, hay que reservar stock y notificar…
La consecuencia de esto es que la lógica acaba repartida entre controllers, servicios, repositorios y queries. El resultado es: duplicación, bugs y miedo a tocar cosas, incertidumbre y al final refactorización haciendo gastar más horas innecesarias.
DDD intenta evitar esto con una estructura clara: dominio fuerte + capas que lo respetan.
Conceptos de Domain-Driven Design
1. Ubiquitous Language (Lenguaje ubicuo)
Negocio y devs usan el mismo vocabulario. Si negocio dice Pedido, Línea, Total, Estado, tu código debe decir eso. No TblOrderHeader o OrderEntityModelDto2.
2. Entidades
Una entidad tiene identidad y evoluciona. Un Order es el mismo pedido hoy y mañana, aunque cambie estado o total.
3. Value Objects
Objetos sin identidad, se comparan por valor y suelen ser inmutables. Ej.: Money, Email, Address. Ayudan a encapsular reglas pequeñas y a no repetir validaciones.
4. Agregados y Aggregate Root
Un agregado es un paquete coherente de objetos del dominio que se modifican juntos. Tiene una raíz (Aggregate Root) que es la única puerta de entrada para cambios importantes.
Ejemplo: Order (raíz) y sus OrderItems. Nadie debería modificar OrderItems directamente desde fuera: se hace a través de métodos del Order para que las reglas se cumplan.
5. Repositorios
Una interfaz para cargar/guardar agregados sin acoplarte a EF Core. El dominio no sabe si hay SQL, Mongo, Redis o lo que sea.
6. Domain Events (eventos de dominio)
Son hechos del negocio: OrderPlaced, OrderCancelled. No son logs. Sirven para desacoplar acciones secundarias: notificaciones, integraciones, colas, etc.
Cómo aplicarlo a una API en .NET (ejemplo realista)
Imaginemos una API de pedidos con reglas:
- Un pedido debe tener al menos un ítem.
- Cantidad > 0.
- Precio >= 0.
- El total se calcula desde las líneas (no se recibe total desde la API).
- Un pedido recién creado empieza en estado Draft.
- Sólo se puede confirmar un pedido si está en Draft.
Estructura sugerida
En este caso, se ha elegido una estructura simple con arquitectura limpia tal como se representa en la imagen:
Esta estructura es ideal para aplicar Domain-Driven Design (y muy alineada con Clean Architecture), aparte de compatible con Clean Architecture. La clave es que el dominio manda y las dependencias van hacia adentro:
- API: depende de Application
- Application: depende de Domain
- Infrastructure: depende de Application y Domain (porque implementa repositorios, EF, etc.)
- Domain: no depende de nadie
Así consigues que tus reglas de negocio no queden secuestradas por EF Core, controllers o frameworks.
Qué va en cada proyecto
Voy a explicar que va en cada capa. Empezamos por la capa del dominio:
Capa Domain
Aquí viven las reglas del negocio. No DTOs, no controllers, no DbContext.
- Entidades, Value Objects, Agregados
- Excepciones de dominio
- (Opcional) eventos de dominio
Piensa: si mañana cambio de base de datos y quito Web API… ¿esto sigue teniendo sentido? Si la respuesta es sí, va aquí.
Capa Application
Aquí viven los casos de uso: Crear pedido, Confirmar pedido, Cancelar pedido, etc.
- Commands/Handlers
- Interfaces (repositorios, servicios externos)
- Validaciones de entrada (no las invariantes del dominio)
La Application orquesta; el Domain decide.
Capa Infrastructure
Aquí metes lo técnico:
- EF Core (DbContext, mappings)
- Implementaciones de repositorios
- Integración con colas, email, servicios externos
Capa Api
Entrada/salida HTTP:
- Controllers, endpoints
- DTOs (requests/responses)
- Autenticación, filtros, swagger
- Inyección de dependencias
Ejemplo Domain-Driven Design en esa solución: API de Pedidos
Reglas que queremos proteger:
- Un pedido no se confirma si no tiene ítems
- Cantidad > 0, precio >= 0
- El total se calcula desde las líneas, no viene confiado desde el cliente
1. Domain: Value Object + Agregado
src/Domain/Commonts/ DomainException.cs
src/Domain/ValueObjects/Money.cs
src/Domain/Orders/Order.cs
Fijaos, las reglas están dentro de Order. Eso evita el dominio anémico (entidades tontas + lógica en services).
2. Application: Caso de uso de Crear y confirmar pedido
src/Application/Orders/CreateOrder/CreateOrderCommand.cs
src/Application/Orders/IOrderRepository.cs
src/Application/Orders/CreateOrder/CreateOrderHandler.cs
Application no recalcula totals ni valida invariantes profundas porque esto ya lo hace el Domain.
3. Infrastructure: EF Core + repositorio
src/Infrastructure/Persistence/AppDbContext.cs
src/Infrastructure/Repositories/OrderRepository.cs
4. Api: endpoint + DI
src/Api/Controllers/OrdersController.cs
src/Api/Program.cs
Resultado: qué mejoras notas de verdad
- Tus controllers quedan limpios (HTTP no contiene negocio).
- Las reglas viven en el agregado (Order) y se respetan siempre.
- Puedes testear Domain sin levantar DB ni Web API.
- EF Core y detalles técnicos se quedan en Infrastructure, donde pertenecen.
Si te interesa estar al día en desarrollo web, arquitectura y buenas prácticas en programación, síguenos en nuestras redes sociales y Canal de YouTube.
