Многоуровневая архитектура на основе принципа Domain-driven design (DDD)

Автор: | 06.08.2019

Предметно-ориентированное проектирование (DDD) — набор правил по созданию систем объектов, программных абстракций, которые называются моделями предметных областей.

Все встречали код, который содержит в себе в перемешку верстку, бизнес логику, взаимодействие с базами данных и бог знает что еще.

Такой код получил название Big Ball of Mud. Потому что он вызывал трудности в сопровождении, тестирование и большие проблемы при масштабировании проекта.

В результате было предложено решение по разделению и идентификации всех слоев приложения. Особое место при таком подходе отводится слою модели.

Модель захватывает и централизует все поведение. Этот уровень управляет всеми данными, логикой и бизнес правилами независимо от представления данных. Можно сказать, что слой модели — это сердце и душа каждого приложения MVC.

Уровень модели теперь определяется классами Entity (сущность), который соответствует одной строке записи в БД, и Repository (репозиторий) представляющего коллекцию сущностей.

Пример репозитория:

Обратите внимание, что инфраструктурный уровень (Infrastructure layer), отвечающий за подключение к БД, вынесен отдельно:

Можно пойти еще дальше и воспользоваться принципом инверсии зависимостей (Dependency Inversion Principle — DIP):

  • Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций.
  • Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

Уровень инфраструктуры можно назвать модулем низкого уровня. Он должен зависеть от уровня пользовательского интерфейса, уровня приложения и уровня домена, которые являются модулями высокого уровня. Мы как бы помещаем инфраструктуру поверх остальных трех уровней.

Это может быть реализовано с помощью гексагональной архитектуры (Hexagonal Architecture) представляемой такими шаблонами проектирования как Port и Adapter. В концепции DIP Port был бы модулем высокого уровня, а Adapter был бы низкоуровневым модулем.

Абстрактно говоря, если приложению необходимо отправить сообщение во внешний мир, оно должно использовать Port и Adapter для его отправки и преобразования. В этом смысле, гексагональная архитектура создает концепцию симметрии в приложении, что как бы меняет схему архитектуры. Потому что, больше нет модулей верхнего или нижнего уровней, а есть «внешний мир» outside и «внутренняя сторона» inside, что часто представляется в виде шестиугольника (hexagon).

Посмотрим, как в связи с этим мог бы измениться наш класс Repository. Он будет представлять собой Port, который используется для получения и передачи сообщений приложения из «внешнего мира».

Теперь необходим Adapter для этого порта (Port). Адаптер отвечает за конкретную реализацию, за определение способа извлечения записей с помощью определенной технологии:

Независимо от конкретной реализации, внутри модели необходим еще один слой, который координирует и упорядочивает поведение домена модели, это слой приложения (Application Layer).

Его представляет класс Service — известный как Application Service, его целью является организация поведения домена. Другими словами сервисы являются прямыми пользователями домена модели и не один другой объект не должен напрямую иметь доступ к внутренним слоям уровня модели:

Подытожив можно сказать, что модель в многоуровневой архитектуре представлена тремя классами — Entity, Repository и Service. Причем первые два являются внутренними и доступ к ним возможен только через третий.

Напоследок, приведу пример того как мог бы выглядеть один из контроллеров такого приложения: