Volver al inicio

DDD y Clean Architecture en Go: enfoque pragmático

El artículo explica cómo aplicar de manera pragmática DDD y Clean Architecture en Go, evitando una estructura excesiva. Se proporcionan ejemplos de código, recomendaciones de organización del proyecto y reglas clave para reducir la deuda técnica.

Go: DDD y Clean Architecture sin 200 archivos
Advertisement 728x90

DDD y Clean Architecture en Go: Un enfoque pragmático sin sobreingeniería

La deuda técnica derivada de problemas arquitectónicos consume hasta el 40% de los presupuestos de TI de las empresas. En proyectos Go, esto a menudo se manifiesta como estructuras excesivamente complejas: 200 archivos llenos de interfaces vacías y capas donde la lógica de negocio queda enterrada. La causa raíz es el mal uso de patrones DDD y Clean Architecture tomados prestados de Java o C#. Pero cuando se aplican de manera pragmática, estos patrones resuelven problemas de complejidad. Aquí te explico cómo evitar la sobreingeniería y construir un sistema mantenible.

¿Por qué los proyectos Go se convierten en un laberinto de archivos?

Los desarrolladores que llegan a Go desde otros lenguajes suelen arrastrar hábitos arquitectónicos que no encajan con el Go idiomático. Según la Go Developer Survey 2024, el principal problema es mantener estándares de código consistentes debido a niveles de experiencia variados y patrones no idiomáticos. Por ejemplo, crear estructuras de carpetas como domain, application e infrastructure imitando Java, sin captar los principios fundamentales.

La investigación de Carnegie Mellon lo confirma: los problemas arquitectónicos son la principal fuente de deuda técnica. En Go, se ve así:

Google AdInline article slot
  • Una carpeta dto con structs duplicados.
  • mappers entre capas para conversiones triviales.
  • Interfaces en domain que dependen de infrastructure (p. ej., database/sql).
  • Casos de uso que solo llaman a repositorios sin lógica de negocio.

El resultado: 200 archivos, pero ninguno que describa reglas de negocio reales. Clean Architecture y DDD se convierten no en la solución, sino en el problema mismo.

DDD: No se trata de carpetas, sino de lenguaje y límites

Domain-Driven Design no se trata de un conjunto de carpetas, sino de un enfoque para modelar el dominio del problema. Conceptos clave:

Lenguaje Ubicuo

Los desarrolladores y las partes interesadas del negocio deben usar los mismos términos en todas partes: en el código, la documentación y las discusiones. Si un gerente dice "confirmar pedido", el código debería tener un método Confirm(), no SetStatus(). Esto elimina confusiones y acelera la incorporación.

Google AdInline article slot

Contexto Acotado

Un sistema grande consta de múltiples contextos. Por ejemplo, "cliente" en ventas (un prospecto con embudo de ventas) y en soporte (tickets) son modelos diferentes. El Contexto Acotado define límites claros donde el modelo se mantiene consistente. En microservicios, un servicio típicamente se alinea con un contexto.

Entidad y Objeto de Valor

  • Entidad tiene un ID único y mantiene identidad (p. ej., Order). Contiene reglas de negocio.
  • Objeto de Valor es inmutable y definido por sus atributos (p. ej., Money). Dos objetos con campos idénticos son equivalentes.

Ejemplo de Objeto de Valor en Go:

type Money struct {
    Amount   int64
    Currency string
}

func (m Money) Add(other Money) (Money, error) {
    if m.Currency != other.Currency {
        return Money{}, errors.New("currency mismatch")
    }
    return Money{Amount: m.Amount + other.Amount, Currency: m.Currency}, nil
}

Agregado

Un grupo de objetos tratados como una unidad única. Tiene una raíz (Raíz de Agregado) a través de la cual ocurre todo acceso. Por ejemplo, Order (raíz) incluye OrderItem. Regla: los cambios se hacen solo a través de la raíz, asegurando consistencia.

Google AdInline article slot

Clean Architecture: La regla de dependencia como fundamento

La esencia de Clean Architecture es que la lógica de negocio no debería depender de detalles de implementación (bases de datos, frameworks). La única regla: las dependencias apuntan hacia adentro (Regla de Dependencia).

Cómo funciona en Go

  • domain: Contiene el modelo de negocio (Entidad, Objeto de Valor, interfaces de repositorio). Sin importaciones externas excepto stdlib.
  • application: Casos de uso que orquestan el dominio. Depende solo de domain.
  • infrastructure: Implementaciones de adaptadores (bases de datos, clientes HTTP). Depende de domain vía interfaces.
  • delivery: Puntos de entrada (HTTP, gRPC). Depende de application.

Si domain importa database/sql, la arquitectura está rota. Ejemplo correcto: interfaz de repositorio declarada en domain, implementación en infrastructure/postgres.

Beneficios de combinar DDD y Clean Architecture

| Aspecto | Efecto | Métrica de mejora |

|----------------------|--------------------------------------|-------------------|

| Testabilidad | Aislamiento del dominio de infrastructure | +40% cobertura |

| Flexibilidad | Cambiar adaptadores en horas | -90% tiempo |

| Comprensibilidad | Límites claros de componentes | -70% incorporación|

Estructura real de proyecto: Evitando 200 archivos

Estructura óptima para un servicio Go:

internal/
  domain/
    order.go        # Agregado Order
    order_repo.go   # Interfaz de repositorio
  application/
    create_order.go # Caso de uso
  infrastructure/
    postgres/
      order_repo.go # Implementación
  delivery/
    http/
      order_handler.go

Principios clave:

  • Interfaces de repositorio en domain, no en infrastructure.
  • Sin carpetas dto o mappers a menos que sean realmente necesarias: reutiliza las mismas structs a través de capas si no viola límites.
  • Lógica de negocio solo en domain. Por ejemplo, método Cancel() para Order verifica el estado:
func (o *Order) Cancel() error {
    if o.status == StatusShipped {
        return errors.New("cannot cancel shipped order")
    }
    o.status = StatusCancelled
    return nil
}

Puntos clave

  • Lenguaje Ubicuo es el fundamento de DDD. Si los términos del código no coinciden con el lenguaje del negocio, el modelo estará mal.
  • Regla de Dependencia es el núcleo de Clean Architecture. Las violaciones llevan a código espagueti.
  • Pragmatismo — no crees capas sin necesidad. En Go, a menudo puedes omitir dto si las structs se alinean.
  • Agregados definen límites de transacción. Cambia solo a través de la raíz para consistencia.
  • Pruebas — las pruebas de dominio se ejecutan sin infrastructure, acelerando el desarrollo.

— Editorial Team

Advertisement 728x90

Leer después