Что такое Use Case (сценарий использования) в контексте чистой архитектуры в Go?

Ответ

Use Case (или Interactor) — это ключевой компонент слоя бизнес-логики в «чистой архитектуре» (Clean Architecture). Он инкапсулирует и реализует один конкретный сценарий использования системы, например, «Регистрация пользователя», «Создание заказа» или «Расчет стоимости доставки».

Основная цель Use Case — отделить бизнес-правила от деталей инфраструктуры (базы данных, веб-фреймворки, внешние API).

Принципы реализации в Go:

  1. Зависимость от абстракций: Use Case зависит не от конкретных реализаций (например, репозитория для PostgreSQL), а от интерфейсов (портов). Это позволяет легко подменять реализации.
  2. Инкапсуляция логики: Вся логика, связанная со сценарием (валидация, вызов нужных репозиториев, координация доменных моделей), находится внутри Use Case.
  3. Простота тестирования: Благодаря зависимостям от интерфейсов, Use Case легко тестировать в изоляции, передавая в него mock-объекты (заглушки).

Пример: Use Case для регистрации пользователя

package usecase

import (
    "errors"
    "strings"
)

// User - простая доменная модель
type User struct {
    Name  string
    Email string
}

// UserRepository - это порт (интерфейс), который должен быть реализован
// слоем инфраструктуры (например, postgres_repo.go).   ype UserRepository interface {
    Save(user User) error
    FindByEmail(email string) (*User, error)
}

// RegisterUserUseCase - структура, реализующая сценарий.   ype RegisterUserUseCase struct {
    repo UserRepository // Зависимость от интерфейса, а не от реализации
}

// NewRegisterUserUseCase - конструктор для создания Use Case.
func NewRegisterUserUseCase(repo UserRepository) *RegisterUserUseCase {
    return &RegisterUserUseCase{repo: repo}
}

// Execute - основной метод, выполняющий бизнес-логику.
func (uc *RegisterUserUseCase) Execute(name, email string) error {
    // 1. Валидация входных данных
    if !strings.Contains(email, "@") {
        return errors.New("некорректный email")
    }

    // 2. Проверка бизнес-правила (пользователь не должен существовать)
    if _, err := uc.repo.FindByEmail(email); err == nil {
        return errors.New("пользователь с таким email уже существует")
    }

    // 3. Выполнение основной операции
    user := User{Name: name, Email: email}
    return uc.repo.Save(user)
}

Преимущества такого подхода:

  • Чистая архитектура: Четкое разделение ответственности между слоями.
  • Гибкость: Можно легко заменить базу данных (PostgreSQL на MongoDB), просто написав новый адаптер для интерфейса UserRepository.
  • Тестируемость: Логику можно проверить юнит-тестами без поднятия БД или веб-сервера.