← Назад к вопросам

Что такое контроллер в чистой архитектуре?

2.2 Middle🔥 251 комментариев
#Микросервисы и архитектура

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое контроллер в чистой архитектуре?

В чистой архитектуре (Clean Architecture), концепции, популяризированной Робертом Мартином (дядя Боб), контроллер — это компонент, который принадлежит к слою интерфейсов (или доставки — delivery). Его главная роль — быть точкой входа для внешних запросов (например, HTTP-запросов от клиента) и преобразовывать эти запросы в понятные для внутренних слоёв структуры данных, не содержащие зависимостей от фреймворков или внешних деталей. Контроллер — это не обязательно классический MVC1контроллер, а скорее общий термин для адаптера между внешним миром и бизнес-логикой.

Ключевые характеристики контроллера в чистой архитектуре:

  1. Расположение во внешнем слое: Контроллер находится во внешнем круге (слое интерфейсов) и служит мостом между внешними системами (веб, CLI, GUI) и внутренним слоем use cases (сценариев использования).
  2. Отсутствие бизнес-логики: Контроллер не должен содержать бизнес-правил. Вместо этого он делегирует выполнение use case'ам (интеракторам).
  3. Преобразование данных: Он преобразует входные данные (например, JSON из HTTP-запроса) в простые объекты (DTO — Data Transfer Objects) или запросы (Request models), которые передаются в use case.
  4. Изоляция от фреймворков: Контроллер должен быть написан так, чтобы минимизировать зависимости от конкретных фреймворков (например, Gin, Echo для Go), что упрощает тестирование и замену.
  5. Возврат ответов: После выполнения use case контроллер принимает выходные данные (например, объект ответа) и преобразует их в формат, подходящий для внешнего интерфейса (например, JSON-ответ HTTP).

Пример контроллера на Go в чистой архитектуре:

Рассмотрим простой пример для веб-приложения с использованием HTTP. Здесь контроллер обрабатывает создание пользователя.

// user_controller.go — контроллер в слое интерфейсов
package controller

import (
    "net/http"
    "github.com/gin-gonic/gin"
    "github.com/yourproject/usecase"
    "github.com/yourproject/dto"
)

// UserController — структура контроллера, зависящая от use case
type UserController struct {
    createUserUseCase usecase.CreateUserUseCase
}

// NewUserController — конструктор для инъекции зависимостей
func NewUserController(createUserUseCase usecase.CreateUserUseCase) *UserController {
    return &UserController{
        createUserUseCase: createUserUseCase,
    }
}

// CreateUser — метод-обработчик HTTP POST запроса
func (ctrl *UserController) CreateUser(c *gin.Context) {
    var request dto.CreateUserRequest
    
    // 1. Получение и валидация входных данных от клиента
    if err := c.ShouldBindJSON(&request); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
        return
    }
    
    // 2. Вызов use case (бизнес-логика) с преобразованными данными
    userID, err := ctrl.createUserUseCase.Execute(request.ToEntity())
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    
    // 3. Формирование ответа клиенту
    response := dto.CreateUserResponse{
        ID:       userID,
        Message:  "User created successfully",
    }
    c.JSON(http.StatusCreated, response)
}
// dto/create_user_request.go — DTO для запроса
package dto

import "github.com/yourproject/entity"

type CreateUserRequest struct {
    Name  string `json:"name" binding:"required"`
    Email string `json:"email" binding:"required,email"`
}

// ToEntity — преобразование DTO в сущность доменного слоя
func (req *CreateUserRequest) ToEntity() *entity.User {
    return &entity.User{
        Name:  req.Name,
        Email: req.Email,
    }
}

Почему контроллеры так важны в чистой архитектуре:

  • Соблюдение принципа единой ответственности: Контроллер отвечает только за обработку ввода-вывода, делегируя логику use case'ам.
  • Тестируемость: Поскольку контроллеры изолированы от фреймворков, их легко тестировать с помощью моков для use case.
  • Гибкость: Можно изменить внешний интерфейс (например, перейти с HTTP на gRPC), не затрагивая бизнес-логику.
  • Следование правилу зависимостей: Зависимости направлены внутрь, к ядру. Контроллер зависит от use case, но не наоборот.

Типичные ошибки при реализации контроллеров в Go:

  1. Размещение бизнес-логики в контроллере: Это нарушает принципы чистой архитектуры и усложняет поддержку.
  2. Использование структур фреймворка в use case: Например, передача *gin.Context в use case.
  3. Отсутствие DTO: Прямая работа с запросами фреймворка приводит к связыванию слоёв.

Таким образом, в чистой архитектуре контроллер — это тонкий адаптер, который координирует поток данных между внешним миром и ядром приложения, обеспечивая соблюдение границ архитектуры и поддерживая высокую поддерживаемость кода. В Go это часто реализуется через структуры с инъекцией зависимостей и использованием интерфейсов для use case'ов.