Controller – это объект, который отвечает за обработку системных событий и при этом не относится к интерфейсу пользователя. Он определяет методы для выполнения системных операций.
Зачем он нужен
Без контроллера логика сценария расползается по обработчикам кнопок и HTTP-эндпоинтам. Поменять способ доставки события – например, добавить к веб-интерфейсу консольный – значит переписать сценарий заново.
Контроллер принимает событие и решает, кому передать работу. Снаружи приходит что угодно – незаполненная форма, чужой идентификатор, строка вместо числа; задача контроллера – либо вернуть ошибку наружу, либо привести данные к виду, с которым домену удобно работать.
«Сырые данные» – это буквально то, что пришло по проводу. Тело запроса POST /checkout из
веб-формы выглядит примерно так:
{
"orderId": " 1042 ",
"quantity": "2 шт",
"promo": "",
"comment": "<script>alert(1)</script>"
}
Здесь нет ни одного поля, которому можно верить:
orderId– строка с пробелами вместо числа;quantity– вообще не число;promo– пустая строка: это «промокода нет» или «промокод пустой»?comment– чужая разметка внутри текста.
Из консоли то же событие придёт строками аргументов, из очереди – сообщением с полями в другом порядке.
Разобрать всё это – работа контроллера: он превращает запрос в Checkout(orderId: 1042, quantity: 2) или отвечает ошибкой, так и не дойдя до домена. Заказ и платёжный сервис
получают уже готовые значения и ничего не знают ни про строки, ни про HTTP.
Сам контроллер ничего существенного не делает: как только в нём появляется бизнес-логика, он превращается в «божественный объект».
// Контроллер: принимает системное событие и раздаёт работу
public class CheckoutController
{
private readonly IOrderRepository _orders;
private readonly IPaymentService _payments;
public CheckoutController(IOrderRepository orders, IPaymentService payments)
{
_orders = orders;
_payments = payments;
}
public void Checkout(int orderId)
{
var order = _orders.GetById(orderId);
_payments.Pay(order.Total);
order.MarkPaid();
_orders.Save(order);
}
}
// Контроллер: принимает системное событие и раздаёт работу
class CheckoutController {
public:
CheckoutController(IOrderRepository& orders, IPaymentService& payments)
: orders_(orders), payments_(payments) {}
void Checkout(int order_id) {
Order order = orders_.GetById(order_id);
payments_.Pay(order.Total());
order.MarkPaid();
orders_.Save(order);
}
private:
IOrderRepository& orders_;
IPaymentService& payments_;
};
// Контроллер: принимает системное событие и раздаёт работу
type CheckoutController struct {
orders OrderRepository
payments PaymentService
}
func NewCheckoutController(orders OrderRepository, payments PaymentService) *CheckoutController {
return &CheckoutController{orders: orders, payments: payments}
}
func (c *CheckoutController) Checkout(orderID int) error {
order, err := c.orders.GetByID(orderID)
if err != nil {
return err
}
if err := c.payments.Pay(order.Total()); err != nil {
return err
}
order.MarkPaid()
return c.orders.Save(order)
}
# Контроллер: принимает системное событие и раздаёт работу
class CheckoutController:
def __init__(self, orders: OrderRepository, payments: PaymentService) -> None:
self._orders = orders
self._payments = payments
def checkout(self, order_id: int) -> None:
order = self._orders.get_by_id(order_id)
self._payments.pay(order.total)
order.mark_paid()
self._orders.save(order)
// Контроллер: принимает системное событие и раздаёт работу
class CheckoutController {
constructor(
private readonly orders: OrderRepository,
private readonly payments: PaymentService,
) {}
checkout(orderId: number): void {
const order = this.orders.getById(orderId);
this.payments.pay(order.total);
order.markPaid();
this.orders.save(order);
}
}
// Контроллер: принимает системное событие и раздаёт работу
public class CheckoutController {
private final OrderRepository orders;
private final PaymentService payments;
public CheckoutController(OrderRepository orders, PaymentService payments) {
this.orders = orders;
this.payments = payments;
}
public void checkout(int orderId) {
Order order = orders.getById(orderId);
payments.pay(order.getTotal());
order.markPaid();
orders.save(order);
}
}
// Контроллер: принимает системное событие и раздаёт работу
class CheckoutController(
private val orders: OrderRepository,
private val payments: PaymentService,
) {
fun checkout(orderId: Int) {
val order = orders.getById(orderId)
payments.pay(order.total)
order.markPaid()
orders.save(order)
}
}
Обратите внимание: сумму считает заказ, оплату проводит платёжный сервис, а контроллер лишь выстраивает порядок шагов.