Ответ
Use Case (или Interactor) — это ключевой компонент слоя бизнес-логики в «чистой архитектуре» (Clean Architecture). Он инкапсулирует и реализует один конкретный сценарий использования системы, например, «Регистрация пользователя», «Создание заказа» или «Расчет стоимости доставки».
Основная цель Use Case — отделить бизнес-правила от деталей инфраструктуры (базы данных, веб-фреймворки, внешние API).
Принципы реализации в Go:
- Зависимость от абстракций: Use Case зависит не от конкретных реализаций (например, репозитория для PostgreSQL), а от интерфейсов (портов). Это позволяет легко подменять реализации.
- Инкапсуляция логики: Вся логика, связанная со сценарием (валидация, вызов нужных репозиториев, координация доменных моделей), находится внутри Use Case.
- Простота тестирования: Благодаря зависимостям от интерфейсов, 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. - Тестируемость: Логику можно проверить юнит-тестами без поднятия БД или веб-сервера.
Ответ 18+ 🔞
Ну слушай, вот есть у нас эта ваша «чистая архитектура», а там, в самом её сердце, сидит себе такой важный чувак — Use Case, или, по-нашему, Сценарий Использования. Представь, что это такой отдельный, блядь, начальник цеха, который знает ровно одну операцию наизусть. «Зарегистрировать пользователя», «Создать заказ», «Посчитать, сколько с тебя содрать за доставку» — вот это всё его работа. И делает он её так, что ему похуй, что вокруг творится: база данных горит, фреймворк обновился или апишка внешняя сдохла. Его дело — правила бизнеса выполнить, а не с инфраструктурой, сука, целоваться.
Зачем он, этот Use Case, вообще нужен? А затем, чтобы бизнес-логика не превратилась в такое месиво, где в одном файле и SQL-запросы, и проверка пароля, и отправка письма «С днём рождения, пидор!». Он эту логику инкапсулирует, как в банку, и говорит: «Всё, что нужно для этого сценария — лежит тут. Не лезть!».
Как его, падлу, в Go делать правильно? Тут главное — не накосячить с зависимостями.
- Хуй на конкретику, даёшь абстракции! Use Case не должен знать, что у тебя база — PostgreSQL, MongoDB или, прости господи, Excel-файл на флешке. Он знает только интерфейс (порт), типа
UserRepository. А кто его реализует — его, блядь, проблемы. Это как нанять грузчика: тебе важно, чтобы он коробки таскал, а он там хоть на спине, хоть на жопе — тебе похуй. - Вся логика — внутри. Валидация, проверки, вызовы к репозиториям, манипуляции с доменными моделями — всё это кипит в одном котле под названием
Execute. - Тестируется — заебись. Раз он зависит от интерфейсов, то ты можешь накормить его любыми муляжами (mock-объектами) и проверить логику в полной изоляции. Без поднятия целого сервера, блядь. Удобно же!
Смотри, как это выглядит в коде. Пример: «Зарегистрировать пользователя»
package usecase
import (
"errors"
"strings"
)
// User - наша доменная сущность, простая как три копейки.
type User struct {
Name string
Email string
}
// UserRepository - это ПОРТ (интерфейс). Контракт, который мы диктуем внешнему миру.
// Кто хочет с нами работать — пусть реализует. Нам похуй как.
type UserRepository interface {
Save(user User) error
FindByEmail(email string) (*User, error)
}
// RegisterUserUseCase - вот он, наш главный по регистрации.
type RegisterUserUseCase struct {
repo UserRepository // Храним зависимость от интерфейса. Не от конкретной реализации, блядь!
}
// NewRegisterUserUseCase - конструктор. Даём ему репозиторий, он делает своё дело.
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)
}
И что мы, блядь, выигрываем?
- Архитектура чище некуда. Слои не перемешаны, как салат оливье после пятого тоста. Каждый знает своё место.
- Гибкость — овердохуища. Захотел сменить базу данных? Просто напиши новую реализацию для
UserRepository, которая будет с новой БД общаться. Use Case даже не чихнёт. - Тестируемость на высоте. Писал же уже — подсовываешь заглушки и проверяешь логику. Никаких лишних движений.
Вот и весь сказ, ёпта. Use Case — это такой работяга, который делает одно дело, но делает его хорошо и независимо от всего окружающего пиздеца.