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

Published: (December 5, 2025 at 08:21 PM EST)
3 min read
Source: Dev.to

Source: Dev.to

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.

Diagram

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.

Implementación en Go

Entidad (Dominio)

// domain/employee.go
package domain

type Employee struct {
    ID     string
    Name   string
    Role   string
    Salary float64
}

Interfaz del repositorio

// 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)
}

Service Layer (Lógica de negocio)

// 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")
    }

    // 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)
}

Implementación concreta (Infraestructura)

// 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")
}

Uso en main.go

// 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!")
    }
}

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.

Back to Blog

Related posts

Read more »