← Назад к вопросам

Зачем нужна папка repository?

2.0 Middle🔥 231 комментариев
#Другое#Микросервисы и архитектура

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Архитектурная роль папки Repository в Go-проектах

Папка repository в Go-проектах выполняет ключевую роль слоя доступа к данным в рамках архитектурных паттернов типа Clean Architecture, Hexagonal Architecture или многослойной архитектуры. Это не просто каталог для хранения кода, а важный структурный элемент, обеспечивающий разделение ответственностей и поддержку принципов SOLID.

Основные функции Repository

1. Абстракция над источниками данных Repository инкапсулирует всю логику работы с различными источниками данных, предоставляя единый интерфейс для бизнес-логики:

// Интерфейс репозитория в слое домена
type UserRepository interface {
    FindByID(ctx context.Context, id uuid.UUID) (*User, error)
    Save(ctx context.Context, user *User) error
    Delete(ctx context.Context, id uuid.UUID) error
}

// Реализация для PostgreSQL
type postgresUserRepository struct {
    db *sql.DB
}

func (r *postgresUserRepository) FindByID(ctx context.Context, id uuid.UUID) (*User, error) {
    // SQL-запросы и маппинг
    row := r.db.QueryRowContext(ctx, "SELECT id, name, email FROM users WHERE id = $1", id)
    // ... маппинг в доменную модель
}

2. Сокрытие деталей реализации Бизнес-логика не должна знать, где именно хранятся данные — в PostgreSQL, MongoDB, Redis или внешнем API:

// Бизнес-сервис работает только с интерфейсом
type UserService struct {
    repo repository.UserRepository
}

func (s *UserService) GetUserProfile(ctx context.Context, userID uuid.UUID) (*UserProfile, error) {
    user, err := s.repo.FindByID(ctx, userID)
    if err != nil {
        return nil, fmt.Errorf("failed to get user: %w", err)
    }
    // ... бизнес-логика
}

3. Централизация операций с данными Все CRUD-операции, специфичные запросы и трансформации данных сосредоточены в одном месте:

// Репозиторий может содержать сложные запросы
func (r *postgresUserRepository) FindActiveUsersWithOrders(ctx context.Context, since time.Time) ([]*User, error) {
    query := `
        SELECT u.id, u.name, u.email 
        FROM users u
        JOIN orders o ON u.id = o.user_id
        WHERE u.is_active = true 
          AND o.created_at > $1
        GROUP BY u.id`
    // ... выполнение и маппинг
}

Преимущества использования папки Repository

  • Тестируемость: Мокирование репозиториев для unit-тестов
  • Гибкость: Легкая замена источника данных без изменения бизнес-логики
  • Поддержка: Единое место для оптимизации запросов и кэширования
  • Согласованность: Стандартизация доступа к данным во всем приложении

Структура папки в типичном проекте

internal/
├── domain/
│   └── user.go          # Доменные модели и интерфейсы репозиториев
├── repository/
│   ├── user_repository.go           # Интерфейсы
│   ├── postgres/
│   │   ├── user_repository.go      # PostgreSQL реализация
│   │   └── migrations/              # Миграции
│   └── inmemory/
│       └── user_repository.go       # In-memory реализация для тестов
└── service/
    └── user_service.go   # Бизнес-логика

Важные принципы при работе с Repository в Go

  1. Интерфейсы определяются на уровне домена, а реализуются в репозитории
  2. Методы возвращают доменные модели, а не DTO или структуры БД
  3. Транзакционная логика может управляться на уровне репозитория или отдельного слоя
  4. Каждая сущность обычно имеет свой репозиторий, но возможны агрегатные репозитории для сложных сценариев

Отличия от других подходов

В отличие от Active Record паттерна, где модель сама управляет персистентностью, Repository полностью отделяет доменную модель от инфраструктуры. Также Repository отличается от Data Mapper более высокоуровневой абстракцией, ориентированной на бизнес-концепции, а не на таблицы БД.

В Go-экосистеме подход с папкой repository стал стандартом де-факто для средних и крупных проектов, хотя для микросервисов или простых приложений иногда используют более плоские структуры. Ключевое — сохранение слабого связывания между бизнес-логикой и деталями хранения данных, что делает код более поддерживаемым и адаптивным к изменениям.