Por qué DDD no es solo "otro patrón más"
\n\n¿Alguna vez ha reescrito un proyecto desde cero solo porque "la lógica de negocio se extendió por todos los controladores"? ¿O tal vez su UserManager creció hasta 2000 líneas y nadie entiende lo que realmente hace? ¿Le suena familiar?
En el mundo del desarrollo Swift, estamos acostumbrados a MVC, VIPER o MVVM. Pero todos estos patrones resuelven el mismo problema: cómo separar la UI de la lógica. Sin embargo, casi no dicen nada sobre cómo organizar la lógica de negocio en sí. Y aquí entra en escena Domain-Driven Design (DDD): un enfoque que pone en el centro no el framework, sino el dominio de su aplicación.
\n\nEn este artículo, analizaremos cómo aplicar Domain-Driven Design en Swift con ejemplos reales. Verá que DDD no es una teoría abstracta, sino una herramienta poderosa que hace que el código sea legible, comprobable y resistente a los cambios.
\n\nQué es DDD en palabras sencillas
\n\nImagine que está escribiendo una aplicación para una tienda en línea. MVC le dirá: "Crea un modelo Product, un controlador ProductViewController y las vistas". Pero DDD preguntará: "¿Qué es un 'producto' en su negocio? ¿Puede existir sin precio? ¿Qué sucede cuando se agrega al carrito?"
Domain-Driven Design es una forma de modelar software donde el código refleja con la máxima precisión el lenguaje y las reglas del negocio. En lugar de entidades "técnicas" (como DataManager), usted trabaja con objetos de dominio: Order, Customer, Invoice.
Los componentes básicos de DDD
\n\nPara no perderse en la terminología, recuerde tres conceptos principales:
\n\n- \n
- Entity (Entidad) — un objeto que tiene identidad. Dos objetos con los mismos campos son entidades diferentes (como dos personas diferentes con el mismo nombre). \n
- Value Object (Objeto de valor) — un objeto que se define solo por sus atributos. Si dos objetos tienen todos los campos iguales, son el mismo objeto (como una fecha o una cantidad de dinero). \n
- Aggregate (Agregado) — un grupo de objetos relacionados que se considera como una unidad completa. El agregado tiene una raíz (root entity) a través de la cual se accede a todos los objetos internos. \n
¿Suena seco? Vamos a Swift.
\n\nEscribiendo un modelo de dominio en Swift
\n\nSupongamos que estamos haciendo una aplicación para gestionar tareas (lista TODO). Empecemos con lo más simple: un Value Object para el estado de la tarea.
\n\n// Value Object: estado de la tarea\nstruct TaskStatus: Equatable {\n let rawValue: String\n \n static let todo = TaskStatus(rawValue: "todo")\n static let inProgress = TaskStatus(rawValue: "inProgress")\n static let done = TaskStatus(rawValue: "done")\n \n // Regla de negocio: solo se puede transicionar al siguiente estado\n func canTransition(to newStatus: TaskStatus) -> Bool {\n switch (self, newStatus) {\n case (.todo, .inProgress), (.inProgress, .done):\n return true\n default:\n return false\n }\n }\n}\n\nObserve: no usamos enum porque en un negocio real los estados pueden llegar del servidor o almacenarse en la base de datos. TaskStatus es un objeto inmutable que conoce sus propias reglas.
Ahora creemos una Entity — la tarea en sí:
\n\n// Entity: tarea\nfinal class Task {\n let id: UUID\n var title: String\n var description: String\n private(set) var status: TaskStatus\n let createdAt: Date\n \n init(id: UUID = UUID(), title: String, description: String, status: TaskStatus = .todo) {\n self.id = id\n self.title = title\n self.description = description\n self.status = status\n