Enterprise Patterns 在 Golang:使用 Repository Pattern 解耦你的代码

发布: (2025年12月6日 GMT+8 09:21)
4 min read
原文: Dev.to

Source: Dev.to

引言:耦合问题

当我们阅读 Martin Fowler 的 Patterns of Enterprise Application Architecture 时,常常会想到旧的 Java 单体系统。然而,在微服务时代,这些模式比以往任何时候都更为重要。

在 Go 应用中,一个常见的错误是把 SQL 查询(SELECT * FROM …)直接写在 HTTP 控制器或业务逻辑里。这会导致代码难以测试,也难以迁移。

今天我们将实现 Repository Pattern(仓储模式)。该模式在领域层(你的业务逻辑)和数据映射层(数据库)之间创建一个抽象层,使得应用能够在不破坏业务核心的前提下演进。

示意图

场景

设想一个企业员工管理系统。我们需要创建员工并保存它。我们并不关心数据是保存在 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. 创建具体实现(基础设施)
    repo := infrastructure.NewInMemoryRepo()

    // 2. 将仓储注入服务层(领域)
    empService := service.NewEmployeeService(repo)

    // 3. 执行业务逻辑
    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

相关文章

阅读更多 »