Какова роль контроллера в Чистой архитектуре (Clean Architecture) и как он реализуется в Go?

Ответ

Контроллер в Чистой архитектуре — это компонент во внешнем слое 'Interface Adapters', который служит точкой входа для внешних систем (например, веб-фреймворка).

Его основная задача — координировать поток данных, но он не должен содержать бизнес-логики. Его обязанности включают:

  1. Прием и разбор входящих запросов (например, HTTP-запросов).
  2. Валидация и преобразование данных из формата запроса (DTO - Data Transfer Object) в доменные модели, понятные для бизнес-логики.
  3. Вызов соответствующего Use Case (сценария использования) из слоя бизнес-логики, передавая ему подготовленные данные.
  4. Преобразование результата, полученного от Use Case, обратно в формат ответа (например, JSON), который будет отправлен клиенту.

Это обеспечивает соблюдение Правила Зависимостей (Dependency Rule): все зависимости направлены внутрь, к бизнес-логике. Use Cases ничего не знают о контроллерах, HTTP или конкретных фреймворках.

Пример на Go:

// UserController находится в слое 'Interface Adapters'
type UserController struct {
    createUser usecase.CreateUserUseCase // Зависимость от интерфейса Use Case
}

// CreateUser - метод контроллера, обрабатывающий HTTP-запрос
func (c *UserController) CreateUser(w http.ResponseWriter, r *http.Request) {
    // 1. Разбор и валидация DTO
    var req CreateUserRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        http.Error(w, "invalid request body", http.StatusBadRequest)
        return
    }

    // 2. Преобразование DTO в доменную модель и вызов Use Case
    user, err := c.createUser.Execute(r.Context(), req.ToDomain())
    if err != nil {
        // Обработка ошибок, пришедших от бизнес-логики
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    // 3. Преобразование результата в ответ
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(UserResponseFromDomain(user))
}

Ответ 18+ 🔞

О, смотри-ка, про контроллеры в чистой архитектуре. Ну, блядь, это ж классика, как Герасим с Муму, только без утопления, надеюсь.

Так вот, представь себе, что контроллер — это такой швейцар у парадного входа в твой домен, в твою священную бизнес-логику. Его задача — не думать, а координировать, сука. Он как тот самый немой Герасим, только вместо «Му-му» он говорит «JSON-у». Его работа — взять этот внешний пиздец (HTTP-запрос, например), разобрать, что к чему, и аккуратно, не накосячив, передать внутрь, в святая святых — в слой юзкейсов.

А что он делает, этот швейцар-контроллер? Да по пунктам, чтоб не запутаться, как в том анекдоте про ядра-изумруды.

  1. Приём и разбор. Приходит к нему запрос, а там, блядь, непонятно что. JSON, XML, голый питон — хуй его знает. Он это всё принимает, как мудак послушный, и начинает разбирать. «Так, сука, а где тут у нас логин? А пароль? А почта?»
  2. Валидация и преобразование. Вот тут он должен проверить, не принёс ли ему клиент какую-то хуйню вместо данных. Дата рождения — 32 июня? Имя — пустая строка? Нахуй такого клиента, блядь! А если всё ок, то он превращает эти сырые данные из запроса (это и есть DTO, Data Transfer Object, если по-умному) в нормальные, доменные модели, которые поймёт бизнес-логика. Нельзя же, сука, юзкейсу скармливать сырой JSON, он же подавится!
  3. Вызов юзкейса. Это самый главный момент, ёпта! Контроллер, подготовив данные, звонит внутрь: «Эй, Use Case, у меня тут один чувак хочет зарегистрироваться, держи его данные, сделай что надо!». И передаёт управление. Сам он при этом в бизнес-правила не лезет. Ни-ху-я. Он просто посредник, курьер, блядь.
  4. Преобразование ответа. Юзкейс поработал, вернул результат — доменный объект «Пользователь». А клиенту-то нахуй не нужен твой доменный объект со всеми его внутренними полями! Клиенту нужен красивый JSON. Вот контроллер и превращает результат обратно — из домена в DTO для ответа. И отправляет это всё на выход, как тот самый Герасим, который выносит мусор.

И вся эта, блядь, конструкция работает по Правилу Зависимостей. Зависимости идут внутрь, к ядру. Юзкейсы вообще не знают, что их вызывает какой-то HTTP-контроллер. Им похуй. Они знают только свои интерфейсы. Это гениально и просто, как «Муму», если бы её не утопили.

Вот, смотри, как это выглядит на Go, чтоб совсем понятно было:

// UserController — это наш швейцар, живёт в слое 'Interface Adapters'
type UserController struct {
    createUser usecase.CreateUserUseCase // Он знает только интерфейс юзкейса, а не его реализацию!
}

// CreateUser — метод, который ловит HTTP-запрос
func (c *UserController) CreateUser(w http.ResponseWriter, r *http.Request) {
    // 1. Разбор и валидация DTO
    var req CreateUserRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        http.Error(w, "invalid request body", http.StatusBadRequest) // Клиент принёс хуйню — получай 400!
        return
    }

    // 2. Преобразуем DTO в домен и зовём юзкейс
    user, err := c.createUser.Execute(r.Context(), req.ToDomain())
    if err != nil {
        // Ой, бля, юзкейс вернул ошибку (логин занят, например)
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    // 3. Всё ок, превращаем домен обратно в JSON и отправляем
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(UserResponseFromDomain(user))
}

Вот и вся магия, Колян. Контроллер — не царь и не бог, а слуга. Его дело — принять, преобразовать, передать и отправить ответ. А вся мозговая деятельность, вся, блядь, бизнес-логика — это священная корова, которая сидит глубоко внутри и ни о каких HTTP-запросах не думает. Чистота, блядь, архитектурная! В рот меня чих-пых!