Enterprise Patterns in Golang: Repository 패턴으로 코드 디커플링

발행: (2025년 12월 6일 오전 10:21 GMT+9)
4 min read
원문: Dev.to

Source: Dev.to

소개: 결합도 문제

Martin Fowler의 Patterns of Enterprise Application Architecture를 읽을 때, 우리는 종종 오래된 Java 모놀리식 시스템을 떠올립니다. 그러나 이러한 패턴은 마이크로서비스 시대에 그 어느 때보다도 중요합니다.

Go 애플리케이션에서 흔히 저지르는 실수는 SQL 쿼리(SELECT * FROM …)를 HTTP 컨트롤러나 비즈니스 로직에 직접 섞어 넣는 것입니다. 이렇게 하면 코드를 테스트하기가 불가능하고, 마이그레이션도 어려워집니다.

오늘은 Repository Pattern을 구현해 보겠습니다. 이 패턴은 도메인(비즈니스 로직)과 데이터 매핑(데이터베이스) 사이에 추상화 레이어를 만들어, 핵심 비즈니스 로직을 깨뜨리지 않고 애플리케이션을 진화시킬 수 있게 합니다.

Diagram

시나리오

직원 관리 기업 시스템을 상상해 보세요. 우리는 직원을 생성하고 저장해야 합니다. 저장이 Postgres, MySQL 또는 메모리 중 어디에 이루어지는지는 중요하지 않으며, 비즈니스 로직은 이를 알 필요가 없습니다.

Go 구현

엔티티 (도메인)

// domain/employee.go
package domain

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

레포지토리 인터페이스

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

구체 구현 (인프라)

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

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

결론

Repository Pattern을 사용함으로써 EmployeeService가 데이터베이스에 전혀 의존하지 않게 되었습니다. 내일 메모리 저장소를 MongoDB로 바꾸고 싶다면 infrastructure에 새로운 파일을 하나 만들면 되고, 비즈니스 로직 코드는 전혀 건드릴 필요가 없습니다. 이것이 바로 견고한 엔터프라이즈 아키텍처입니다.

Back to Blog

관련 글

더 보기 »