Что такое доменные сервисы
Доменные сервисы — это компоненты бизнес-логики, которые выражают важные правила предметной области, но не принадлежат одной конкретной сущности или значению. Чаще всего термин используют в подходе Domain-Driven Design, или DDD. Такой сервис помогает описать бизнес-операцию понятным языком и не перегружать модели обязанностями, которые им не свойственны.
Например, расчет стоимости доставки, проверка допустимости перевода средств между счетами или подбор оптимального тарифа могут затрагивать несколько объектов. Если поместить такую логику в одну сущность, модель станет искусственной и сложной для сопровождения. В этом случае правило оформляют как доменный сервис.
Место доменных сервисов в архитектуре
В DDD код разделяют на слои, каждый из которых решает свою задачу. Доменный слой содержит бизнес-смысл: сущности, объекты-значения, агрегаты, события и доменные сервисы. Его цель — отражать правила компании, а не детали базы данных, интерфейса или сетевого взаимодействия.
Доменный сервис работает с понятиями предметной области. Его методы обычно называются так, как бизнес-операции: «рассчитать комиссию», «определить кредитный лимит», «разрешить бронирование». Внутри находятся правила, от которых зависит корректность процесса. Благодаря этому код можно обсуждать с экспертами бизнеса и проверять независимо от технической среды.
Важно не путать доменный сервис с сервисом в общем смысле. Не каждый класс с суффиксом Service относится к доменному слою. Если компонент получает данные по сети, сохраняет записи, отправляет письмо или вызывает внешний платежный шлюз, он чаще относится к инфраструктуре либо прикладному слою.
Когда нужен доменный сервис
Доменные сервисы применяют, когда операция имеет явное бизнес-значение, но ее нельзя естественно закрепить за одной сущностью. Простой ориентир: если метод вынужденно принимает несколько равноправных объектов и ни один из них не выглядит владельцем действия, стоит рассмотреть отдельный сервис.
Представим систему аренды. Сущность «Автомобиль» отвечает за свое состояние: доступность, пробег, ограничения эксплуатации. Сущность «Клиент» хранит статус и данные водителя. Но решение о возможности выдачи автомобиля может учитывать оба объекта, правила страхования, возраст клиента, класс машины и условия региона. Правило «разрешить аренду» логично вынести в доменный сервис.
Еще один пример — валютная конвертация. Деньги и курс могут быть объектами-значениями, а операция обмена использует несколько валют и правила округления. Если расчет не принадлежит одному конкретному денежному объекту, сервис делает модель точнее. При этом слишком раннее выделение сервисов не требуется: логику, которая очевидно относится к сущности, лучше оставлять внутри нее.
Чем доменный сервис отличается от других видов сервисов
Прикладной сервис координирует сценарий пользователя или внешней системы. Он загружает агрегаты из репозитория, вызывает их методы и доменные сервисы, сохраняет изменения, запускает уведомления. В нем допустима оркестрация, но ключевые бизнес-правила желательно не прятать. Например, прикладной сервис оформляет заказ, а доменный сервис рассчитывает условия скидки.
Инфраструктурный сервис решает технические задачи: доступ к базе данных, кешу, очереди, файловому хранилищу, почте, платежному провайдеру. Его детали не должны определять модель бизнеса. Доменный слой может зависеть от абстракции, например интерфейса получения актуального курса, но конкретная реализация с запросами во внешний источник остается в инфраструктуре.
Сущность отличается наличием идентичности и жизненного цикла. Объект-значение описывается своими данными и обычно неизменяем. Доменный сервис, как правило, не хранит собственного состояния между вызовами: он принимает объекты, применяет правило и возвращает результат либо изменяет агрегат через его публичное поведение. Это делает сервисы проще для тестирования.
Признаки хорошего доменного сервиса
Хороший доменный сервис имеет узкую и ясную ответственность. Его название отвечает на вопрос, какое правило бизнеса он реализует. Вместо расплывчатого «Менеджер заказов» лучше использовать «Политика расчета скидки» или «Сервис проверки доступности бронирования», если такие названия совпадают с языком команды и заказчика.
Сервис должен быть максимально независим от технических деталей. В идеале его можно проверить модульным тестом без базы данных, веб-сервера и очередей. Входные данные передаются через параметры, а зависимость от внешней информации выражается небольшим интерфейсом. Так правила остаются прозрачными, а изменения во внешних системах меньше влияют на доменную модель.
Не стоит превращать доменный сервис в универсальное хранилище логики. Класс, содержащий десятки несвязанных операций, становится аналогом «божественного объекта» и размывает границы модели. Если сервис растет, полезно разделить его по бизнес-политикам, пересмотреть агрегаты или выделить отдельные объекты-значения.
Практика проектирования и типичные ошибки
Проектирование начинается не с классов, а с бизнес-разговора. Нужно выяснить, какие решения принимает система, какие условия обязательны, кто отвечает за исключения и какие термины используют специалисты предметной области. После этого правило размещают там, где оно естественно читается: в сущности, объекте-значении, агрегате или доменном сервисе.
Распространенная ошибка — называть доменным сервисом любой контроллер или класс прикладной логики. Другая ошибка — переносить в него простые операции, которые принадлежат сущности. Например, проверка, может ли заказ перейти в следующий статус, обычно является обязанностью самого заказа, поскольку защищает его инварианты.
Также опасно позволять сервису напрямую изменять внутренние поля нескольких агрегатов. Лучше вызвать их выразительные методы и обеспечить согласованность через прикладной сценарий, доменное событие или продуманную транзакционную границу. Такой подход сохраняет инкапсуляцию и снижает риск нарушения правил при последующих изменениях.
Доменные сервисы полезны не количеством, а точностью. Если они отражают реальные бизнес-политики, модель становится понятнее, тесты — надежнее, а изменения требований затрагивают меньше технического кода.