Какова роль контроллера в Чистой архитектуре (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))
}