Что такое контроллер в чистой архитектуре?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое контроллер в чистой архитектуре?
В чистой архитектуре (Clean Architecture), концепции, популяризированной Робертом Мартином (дядя Боб), контроллер — это компонент, который принадлежит к слою интерфейсов (или доставки — delivery). Его главная роль — быть точкой входа для внешних запросов (например, HTTP-запросов от клиента) и преобразовывать эти запросы в понятные для внутренних слоёв структуры данных, не содержащие зависимостей от фреймворков или внешних деталей. Контроллер — это не обязательно классический MVC1контроллер, а скорее общий термин для адаптера между внешним миром и бизнес-логикой.
Ключевые характеристики контроллера в чистой архитектуре:
- Расположение во внешнем слое: Контроллер находится во внешнем круге (слое интерфейсов) и служит мостом между внешними системами (веб, CLI, GUI) и внутренним слоем use cases (сценариев использования).
- Отсутствие бизнес-логики: Контроллер не должен содержать бизнес-правил. Вместо этого он делегирует выполнение use case'ам (интеракторам).
- Преобразование данных: Он преобразует входные данные (например, JSON из HTTP-запроса) в простые объекты (DTO — Data Transfer Objects) или запросы (Request models), которые передаются в use case.
- Изоляция от фреймворков: Контроллер должен быть написан так, чтобы минимизировать зависимости от конкретных фреймворков (например, Gin, Echo для Go), что упрощает тестирование и замену.
- Возврат ответов: После выполнения 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:
- Размещение бизнес-логики в контроллере: Это нарушает принципы чистой архитектуры и усложняет поддержку.
- Использование структур фреймворка в use case: Например, передача
*gin.Contextв use case. - Отсутствие DTO: Прямая работа с запросами фреймворка приводит к связыванию слоёв.
Таким образом, в чистой архитектуре контроллер — это тонкий адаптер, который координирует поток данных между внешним миром и ядром приложения, обеспечивая соблюдение границ архитектуры и поддерживая высокую поддерживаемость кода. В Go это часто реализуется через структуры с инъекцией зависимостей и использованием интерфейсов для use case'ов.