Introducción: ¿Por qué su proyecto Go pide a gritos DDD?
\n\n¿Alguna vez ha escrito un servicio en Go que comenzó con un par de endpoints y tres tablas en la base de datos, y seis meses después se convirtió en un monstruo con archivos handler.go de kilómetros de largo y números mágicos esparcidos por todo el código? ¿Le suena familiar? Yo también pasé por eso.
El problema del desarrollo clásico en Go es que pensamos demasiado pronto en bases de datos y JSON, olvidándonos de lo principal: la lógica de negocio. Domain-Driven Design (DDD) viene al rescate justo cuando queremos poner orden en el caos. Pero, ¿cómo aterrizar DDD en Go si el lenguaje no nos mima con clases y herencia? Vamos a descubrirlo.
\n\nEn este artículo, le mostraré cómo aplicar Domain-Driven Design en proyectos Go sin fanatismo excesivo, con ejemplos de código funcionales y una estructura clara. Aprenderá a separar el grano de la paja y a hacer un código que sea comprensible no solo para la máquina, sino también para el analista de negocio.
\n\n¿Qué es DDD y por qué lo necesita un desarrollador Go?
\n\nDomain-Driven Design no trata sobre tecnología. Trata sobre el lenguaje. Se trata de que el código hable el idioma del negocio. Si su dominio es una tienda en línea, en el código debería haber Order, Product, Cart, y no DataTransferObject y EntityManager.
Go, con su simplicidad y ausencia de magia, es ideal para DDD. Aquí no hay clases proxy ni reflexión que oculten la esencia. En Go, usted está obligado a ser honesto: si tiene un agregado, es una estructura explícita; si tiene un repositorio, es una interfaz.
\n\nLos bloques de construcción principales de DDD que veremos son:
\n- \n
- Entity (Entidad) — objeto con un identificador único (por ejemplo,
User). \n - Value Object (Objeto de valor) — objeto inmutable que describe características (por ejemplo,
Address). \n - Aggregate (Agregado) — clúster de entidades que garantiza la consistencia (por ejemplo,
OrderconOrderItem). \n - Repository (Repositorio) — colección de objetos que oculta los detalles de almacenamiento. \n
- Domain Service (Servicio de dominio) — operación que no encaja en una sola entidad. \n
Ahora, traduzcamos esto a Go.
\n\nEstructura del proyecto: cómo no perderse en las carpetas
\n\nLa buena noticia: Go no impone ninguna estructura. La mala noticia: sin estructura, se ahogará rápidamente. Para DDD en Go, recomiendo la siguiente organización del código:
\n\nyour-project/\n├── cmd/\n│ └── server/\n│ └── main.go\n├── internal/\n│ ├── domain/ # Núcleo puro: entidades, reglas\n│ │ ├── order.go\n│ │ ├── product.go\n│ │ └── errors.go\n│ ├── application/ # Casos de uso / servicios de la aplicación\n│ │ ├── order_service.go\n│ │ └── product_service.go\n│ └── infrastructure/ # Bases de datos, API externas, manejadores HTTP\n│ ├── postgres/\n│ │ └── order_repository.go\n│ └── http/\n│ └── handler.go\n└── go.mod\n\nTenga en cuenta: domain no importa infrastructure. Esta es la regla de oro de DDD: el dominio no sabe nada sobre bases de datos ni HTTP. Vive en un vacío de estructuras e interfaces puras.
Escribiendo el dominio: Order y Value Objects
\n\nEmpecemos por el núcleo. Supongamos que estamos construyendo un sistema de gestión de pedidos. Así es como podría verse la entidad Order:
// internal/domain/order.go\npackage domain\n\nimport (\n "errors"\n "time"\n)\n\ntype OrderStatus string\n\nconst (\n OrderStatusPending OrderStatus = "pending"\n OrderStatusConfirmed OrderStatus = "confirmed"\n OrderStatusCancelled OrderStatus = "cancelled"\n)\n\ntype OrderID string\n\ntype Order struct {\n ID OrderID\n CustomerID string\n Items []OrderItem\n Status OrderStatus\n CreatedAt time.Time\n Upd