Зачем нужна папка repository?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Архитектурная роль папки 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
- Интерфейсы определяются на уровне домена, а реализуются в репозитории
- Методы возвращают доменные модели, а не DTO или структуры БД
- Транзакционная логика может управляться на уровне репозитория или отдельного слоя
- Каждая сущность обычно имеет свой репозиторий, но возможны агрегатные репозитории для сложных сценариев
Отличия от других подходов
В отличие от Active Record паттерна, где модель сама управляет персистентностью, Repository полностью отделяет доменную модель от инфраструктуры. Также Repository отличается от Data Mapper более высокоуровневой абстракцией, ориентированной на бизнес-концепции, а не на таблицы БД.
В Go-экосистеме подход с папкой repository стал стандартом де-факто для средних и крупных проектов, хотя для микросервисов или простых приложений иногда используют более плоские структуры. Ключевое — сохранение слабого связывания между бизнес-логикой и деталями хранения данных, что делает код более поддерживаемым и адаптивным к изменениям.