DEV Community

Enterprise Patterns en Golang: Desacoplando tu código con el Patrón Repositorio

¡Entendido! Aquí tienes el contenido listo para copiar y pegar. He elegido el lenguaje Golang (Go) porque es el estándar actual para desarrollo backend empresarial de alto rendimiento y se ajusta perfectamente a los patrones de arquitectura limpia.

El patrón elegido es el Repository Pattern (Patrón Repositorio), uno de los más fundamentales del libro de Martin Fowler para desacoplar la lógica de negocio de la base de datos.

Parte 1: El Artículo (Para Dev.To / Medium / HashNode)
Título: 🏛️ Enterprise Patterns en Golang: Desacoplando tu código con el Patrón Repositorio

Subtítulo: Cómo aplicar las enseñanzas de Martin Fowler en el desarrollo moderno de microservicios con Go.

Tags: #Golang #SoftwareArchitecture #DesignPatterns #Backend #CleanCode

Introducción: El problema del acoplamiento
Cuando leemos "Patterns of Enterprise Application Architecture" de Martin Fowler, a veces pensamos en sistemas monolíticos antiguos de Java. Sin embargo, estos patrones son más relevantes que nunca en la era de los microservicios.

Un error común en aplicaciones Go es mezclar las consultas SQL (SELECT * FROM...) directamente dentro de los controladores HTTP o la lógica de negocio. Esto hace que el código sea imposible de testear y difícil de migrar.

Hoy implementaremos el Repository Pattern. Este patrón crea una capa de abstracción entre el dominio (tu lógica) y el mapeo de datos (tu base de datos), permitiendo que tu aplicación evolucione sin romper el núcleo del negocio.

El Escenario
Imaginemos un sistema empresarial de gestión de empleados. Necesitamos crear un empleado y guardarlo. No nos importa si se guarda en Postgres, MySQL o en Memoria; nuestra lógica de negocio no debería saberlo.

La Implementación en Go
Primero, definimos nuestra Entidad (El Dominio).

// domain/employee.go
package domain

type Employee struct {
    ID        string
    Name      string
    Role      string
    Salary    float64
}
Enter fullscreen mode Exit fullscreen mode

Ahora, la clave del patrón: La Interfaz del Repositorio. Esto es el contrato que define qué necesitamos, sin decir cómo se hace.

// domain/repository.go
package domain

// EmployeeRepository define el contrato para acceder a los datos
type EmployeeRepository interface {
    Save(employee *Employee) error
    FindByID(id string) (*Employee, error)
}
Enter fullscreen mode Exit fullscreen mode

Implementamos la lógica de negocio (Service Layer). Fíjate cómo el servicio pide un EmployeeRepository en su constructor. Esto es Inyección de Dependencias.

// service/employee_service.go
package service

import (
    "errors"
    "enterprise-patterns/domain"
)

type EmployeeService struct {
    repo domain.EmployeeRepository
}

func NewEmployeeService(repo domain.EmployeeRepository) *EmployeeService {
    return &EmployeeService{repo: repo}
}

func (s *EmployeeService) RegisterEmployee(id, name, role string) error {
    if name == "" {
        return errors.New("el nombre no puede estar vacío")
    }

    // Lógica de negocio: Regla empresarial
    // "Solo los gerentes pueden tener bonos iniciales (simulado aquí)"
    emp := &domain.Employee{
        ID:   id,
        Name: name,
        Role: role,
    }

    return s.repo.Save(emp)
}
Enter fullscreen mode Exit fullscreen mode

Finalmente, la implementación concreta (La infraestructura). Aquí es donde iría SQL, pero usaremos un mapa en memoria para el demo.

// infrastructure/memory_repo.go
package infrastructure

import (
    "errors"
    "enterprise-patterns/domain"
)

type InMemoryEmployeeRepo struct {
    store map[string]*domain.Employee
}

func NewInMemoryRepo() *InMemoryEmployeeRepo {
    return &InMemoryEmployeeRepo{
        store: make(map[string]*domain.Employee),
    }
}

func (r *InMemoryEmployeeRepo) Save(employee *domain.Employee) error {
    if _, exists := r.store[employee.ID]; exists {
        return errors.New("el empleado ya existe")
    }
    r.store[employee.ID] = employee
    return nil
}

func (r *InMemoryEmployeeRepo) FindByID(id string) (*domain.Employee, error) {
    if emp, ok := r.store[id]; ok {
        return emp, nil
    }
    return nil, errors.New("empleado no encontrado")
}
Enter fullscreen mode Exit fullscreen mode

Uso en el main.go
Así es como unimos todo:

// main.go
package main

import (
    "fmt"
    "enterprise-patterns/infrastructure"
    "enterprise-patterns/service"
)

func main() {
    // 1. Crear la implementación concreta (Infraestructura)
    repo := infrastructure.NewInMemoryRepo()

    // 2. Inyectar el repositorio en el servicio (Dominio)
    empService := service.NewEmployeeService(repo)

    // 3. Ejecutar la lógica
    err := empService.RegisterEmployee("001", "Carlos Dev", "Senior Engineer")

    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Empleado registrado exitosamente usando Repository Pattern!")
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusión
Al usar el Repository Pattern, hemos logrado que nuestro EmployeeService sea 100% agnóstico de la base de datos. Si mañana queremos cambiar de memoria a MongoDB, solo creamos un nuevo archivo en infrastructure y no tocamos ni una línea de la lógica de negocio. Eso es arquitectura empresarial robusta.

Top comments (0)