Говоря о концепции и применении DDD через недостатки приложения Spring (2)

задняя часть Архитектура

существуетПредыдущийВ этой статье мы познакомим вас с соответствующими концепциями и стратегиями проектирования доменно-ориентированной разработки с учетом недостатков веб-приложений Spring. В этой статье в основном объясняются несколько типов моделей предметной области и простые практические примеры DDD.

архитектурный стиль

Несколько архитектурных стилей упоминаются в книге «Реализация дизайна, управляемого доменом»: гексагональная архитектура, архитектура REST, CQRS, управление событиями и т. д. В реальном использовании архитектура лендинга не является одним из них, и весьма вероятно, что пользователи будут комбинировать вышеупомянутые архитектурные стили для достижения цели.

Многоуровневая архитектура

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

Четырехуровневая архитектура

Эрик Эванс предложил традиционную четырехуровневую архитектуру в книге «Domain-Driven Design — How to Cope with Software Core Complexity»:

  • Пользовательский интерфейс — это уровень пользовательского интерфейса (или уровень представления), отвечающий за отображение информации для пользователей и интерпретацию пользовательских команд. Упомянутый здесь пользователь может быть другой компьютерной системой, не обязательно лицом, использующим пользовательский интерфейс.
  • Приложение — это прикладной уровень, который определяет задачи, которые должны выполняться программным обеспечением, и направляет объекты, выражающие концепции предметной области, для решения проблем. Этот слой отвечает за работу, имеющую большое значение для бизнеса, а также является необходимым каналом взаимодействия с прикладным слоем других систем. Прикладной уровень должен быть максимально простым, не содержащим бизнес-правил или знаний, а только координировать задачи для объектов предметной области на следующем уровне, назначая работу и заставляя их взаимодействовать друг с другом. У него нет состояния, отражающего бизнес-ситуацию, но может быть другое состояние, показывающее пользователю или программе ход выполнения задачи.
  • Домен — это уровень домена (или уровень модели), отвечающий за выражение бизнес-концепций, информации о бизнес-состоянии и бизнес-правил. Хотя технические детали сохранения бизнес-состояния реализованы на уровне инфраструктуры, состояние, отражающее бизнес-ситуацию, контролируется и используется этим уровнем. Уровень предметной области является ядром программного обеспечения для бизнеса, и модель предметной области находится на этом уровне.
  • Уровень инфраструктуры — это базовый уровень реализации, обеспечивающий общие технические возможности для других уровней: доставка сообщений на прикладной уровень, предоставление механизмов сохраняемости для доменного уровня, отрисовка компонентов экрана для уровня пользовательского интерфейса и т. д. Уровень инфраструктуры также может поддерживать режим взаимодействия между четырьмя уровнями через архитектурную структуру.

Традиционная четырехуровневая архитектура представляет собой ограниченную слабоуровневую архитектуру, то есть любой верхний уровень уровня Infrastructure может получить доступ к этому слою (тип «L»), в то время как другие уровни следуют строгой многоуровневой архитектуре.

Пятиуровневая архитектура

Пятиуровневая архитектура основана на модели архитектуры DCI, упомянутой в «Архитектуре DCI: новая концепция объектно-ориентированного программирования». Архитектура DCI (данные, контекст и интерактивная трехуровневая архитектура):

  • Уровень данных описывает концепции предметной области системы и отношения между ними.Этот уровень фокусируется на создании объектов предметной области и управлении жизненным циклом и связях этих объектов, что позволяет программистам думать о системе с точки зрения объектов, поэтому как сделать «что такое система?» «Легче понять.
  • Контекстный слой: самый тонкий возможный слой. Контекст часто реализуется без сохранения состояния, просто найдите подходящую роль и позвольте ролям взаимодействовать, чтобы завершить бизнес-логику. Однако простота не означает, что это не важно.Явный контекстный уровень обеспечивает точку входа и основную линию для людей, чтобы понять бизнес-процесс программного обеспечения.
  • Интерактивный слой в основном отражается в моделировании роли, которая является реальным исполнителем сложной бизнес-логики в каждом контексте, отражая «что делает система». Что делает роль, так это моделирует поведение, она связывает объекты контекста и предметной области. Поскольку поведение системы сложное и изменчивое, эта роль разделяет систему на слой модели стабильной предметной области и слой изменяемого поведения системы, а роль фокусируется на моделировании поведения системы. Этот уровень часто связан с расширяемостью системы, что ближе к практике разработки программного обеспечения В объектно-ориентированном подходе речь идет больше о мышлении и проектировании с точки зрения классов.

В настоящее время DCI широко рассматривается как развитие и дополнение к DDD, используемому в объектно-ориентированном моделировании предметной области. Конкретное определение пятиуровневой архитектуры выглядит следующим образом:

  • Пользовательский интерфейс — это уровень пользовательского интерфейса, который в основном используется для обработки запросов Restful, отправляемых пользователями, анализа файлов конфигурации ввода пользователя и т. д., а также для передачи информации в интерфейс уровня приложения.
  • Уровень приложения — это прикладной уровень, отвечающий за управление несколькими процессами и планирование, управление несколькими потоками и планирование, планирование нескольких сопрограмм и поддержание модели состояния бизнес-экземпляров. Когда уровень планирования получает запрос от уровня пользовательского интерфейса, он поручает уровню контекста обрабатывать контекст, связанный с этим бизнесом.
  • Контекст — это уровень среды, и он принимает контекст как единицу для приведения объектов домена уровня домена к соответствующим ролям и позволяет ролям взаимодействовать для завершения бизнес-логики.
  • Уровень предметной области — это слой предметной области, определяющий модель предметной области, включая не только моделирование объектов предметной области и их взаимосвязей, но и явное моделирование ролей объектов.
  • Уровень инфраструктуры — это базовый уровень реализации, обеспечивающий общие технические возможности для других уровней: бизнес-платформы, среды программирования, механизма сохраняемости, механизма сообщений, инкапсуляции сторонних библиотек, общих алгоритмов и т. д.
Шестиугольная архитектура

Гексагональная архитектура, также известная как стиль портов и адаптеров, была впервые предложена Алистером Кокберном. Разработанный и продвигаемый в сообществе DDD, гексаморф должен подчеркнуть, что это плоская архитектура с равными весами для каждой границы.

Мы знаем, что классическая многоуровневая архитектура делится на три уровня (уровень представления, прикладной уровень, уровень доступа к данным), а для шестиугольной архитектуры ее можно разделить еще на три уровня:

  • Уровень предметной области: самая внутренняя, чистая базовая бизнес-логика, как правило, не содержащая каких-либо технических реализаций или ссылок.
  • Уровень портов: вне уровня домена он отвечает за получение всех запросов, связанных с вариантом использования, и эти запросы отвечают за координацию работы на уровне домена. Уровень порта действует как граница уровня домена внутри порта и играет роль внешнего объекта за пределами порта.
  • Уровень адаптеров: за пределами уровня порта он отвечает за получение входных данных в определенном формате и создание выходных данных. Например, для запроса пользователя HTTP адаптер преобразует его в вызов уровня домена и маршалирует ответ, возвращенный уровнем домена, вызывающему клиенту через HTTP. На уровне адаптера нет доменной логики, его единственной обязанностью является выполнение технических переводов между внешним миром и уровнем домена. Адаптеры могут быть связаны с протоколом порта и использовать порт, несколько адаптеров могут использовать один и тот же порт, а при переключении на новый пользовательский интерфейс новый интерфейс может использовать тот же порт одновременно со старым интерфейсом.

图片转自网络
Картинка перенесена из сети

Преимущество этого состоит в том, чтобы сделать границы бизнеса более четкими, чтобы получить лучшую масштабируемость. Кроме того, разделение сложности бизнеса и технической сложности является важной основой для DDD. Уровень ядра домена может сосредоточиться на бизнес-логике и не заботиться о технических зависимостях, и когда внешний интерфейс вызывается потребителями, вам не нужно заботиться о том, как бизнес реализован внутри.

REST-архитектура

Архитектура в стиле RESTful будет资源во-первых, каждый资源Ему соответствует URI, который может быть资源Это похоже на объект в DDD; RESTful использует сообщения с самоописываемыми функциями для обеспечения связи без сохранения состояния и повышения доступности системы; что касается资源Какие свойства могут быть обнародованы, для资源Работа RESTful реализована с использованием существующих методов протокола HTTP: GET, PUT, POST и DELETE.

При реализации DDD мы можем проектировать внешние сервисы как сервисы RESTful и использовать сервисы объекта/значения/домена как сервисы.资源Предоставление услуг по добавлению, удалению, изменению и проверке внешним сторонам. Однако не рекомендуется раскрывать объект напрямую. Во-первых, некоторые атрибуты конфиденциальности объекта не могут быть раскрыты внешнему миру. Во-вторых, некоторые сценарии получения ресурсов не могут быть удовлетворены одним объектом. Поэтому в процессе реальной практики мы добавили в модель предметной области роль DTO, которая может объединять ресурсы нескольких сущностей/объектов-значений и выставлять их внешнему миру.

CQRS

CQRS — это разделение чтения и записи, о котором все говорят.Обычно целью разделения чтения и записи является повышение производительности запросов и достижение разделения чтения/записи. Комбинируя DDD и CQRS, мы можем моделировать чтение и запись отдельно.Модель запроса обычно представляет собой денормализованную модель данных, которая не отражает поведение домена, а используется только для отображения данных; модель команд выполняет поведение домена, а в домене поведение После завершения выполнения найдите способ уведомить модель запроса.

Итак, как модель команды уведомляет модель запроса? Этот шаг может быть опущен, если модель запроса и модель домена делятся источником данных; если нет общих источников данных,消息模式(Шаблоны обмена сообщениями) уведомляют модель запроса о достижении конечной согласованности (Eventual Consistency).

Мартин указал в своем блоге: CQRS подходит для очень немногих сложных сфер бизнеса, а если не очень подходит, то увеличивает сложность; еще один подходящий сценарий — получение высокопроизводительных сервисов.

доменная модель

В приведенном выше разделе объясняются несколько архитектурных стилей доменно-ориентированного дизайна.Давайте рассмотрим деление моделей предметной области на простых примерах, которые изначально делятся на четыре категории:

  1. модель кровопотери
  2. Модель анемии
  3. модель гиперемии
  4. Модель отека крови

Давайте посмотрим, что представляют собой эти модели предметной области, и каковы их плюсы и минусы.

модель кровопотери

Проще говоря, модель кровопотери представляет собой чистый класс данных с только методами получения/установки свойств для объектов домена. Вся бизнес-логика полностью завершается бизнес-объектами (также известными как TransactionScript). Объекты домена в этой модели вызываются Мартином Фаулер "Анемичный объект домена". следующее:

  • Класс сущностей под названием Item

    public class Item implements Serializable {   
     private Long id = null;   
     private int version;   
     private String name;   
     private User seller;   
     // ...  
     //   getter/setter方法省略不写,避免篇幅太长   
    

}
```

  • Класс интерфейса DAO под названием ItemDao.

    public interface ItemDao {   
     public Item getItemById(Long id);   
     public Collection findAll();   
     public void updateItem(Item item);   
    

} ```

  • Класс реализации интерфейса DAO называется ItemDaoHibernateImpl.

    public class ItemDaoImpl implements ItemDao extends DaoSupport {   
     public Item getItemById(Long id) {   
         return (Item) getHibernateTemplate().load(Item.class, id);   
     }   
     public Collection findAll() {   
         return (List) getHibernateTemplate().find("from Item");   
     }   
     public void updateItem(Item item) {   
         getHibernateTemplate().update(item);   
     }   
    

} ```

  • Класс бизнес-логики под названием ItemManager (или ItemService)

public class ItemManager {
private ItemDao itemDao;
public void setItemDao(ItemDao itemDao) { this.itemDao = itemDao;}
public Bid loadItemById(Long id) {
itemDao.loadItemById(id);
}
public Collection listAllItems() {
return itemDao.findAll();
}
public Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount,
Bid currentMaxBid, Bid currentMinBid) throws BusinessException {
if (currentMaxBid != null && currentMaxBid.getAmount().compareTo(bidAmount) > 0) {
throw new BusinessException("Bid too low.");
}

  // ...  

} ```

Выше приведен пример кода для модели полной кровопотери. В данном примере LoadItemByid, FindAll и т.д. Бизнес-логика реализована в ItemManager, а у Items есть только метод getter/setter.

Модель анемии

Проще говоря, доменный объект содержит доменную логику, которая не зависит от постоянства, а те доменные логики, которые зависят от постоянства, разделены на уровень службы.

Сервис (бизнес-логика, инкапсуляция транзакций) --> DAO ---> объект домена

Это то, что Мартин Фаулер называет богатым доменным объектом:

  • Класс сущности с бизнес-логикой, то есть объект предметной области — это Item.
  • Интерфейс DAO ItemDao
  • Элемент реализации DAOdaohibernateImpl
  • Объект бизнес-логики ItemManager

Преимущества этой модели:

  1. Односторонняя зависимость каждого слоя, четкая структура, простота реализации и обслуживания.
  2. Дизайн прост и удобен в реализации, а базовая модель очень стабильна.

Недостатки:

  1. Постоянная логика домена, от которой более тесно зависит часть объекта домена, отделена от сервисного уровня, чего недостаточно ООП.
  2. Сервисный уровень слишком тяжелый

Конкретный код относительно прост и больше не будет показан.

модель гиперемии

Модель перегрузки аналогична второй модели, разница в том, как разделить бизнес-логику, то есть большая часть бизнес-логики должна быть размещена в объекте предметной области (включая логику персистентности), а слой Сервиса должен быть очень тонким слоем, который инкапсулирует только транзакции и небольшое количество логики и не имеет отношения к уровню DAO.

Сервис (инкапсуляция транзакции) ---> объект домена DAO

Эта модель объединяет объект предметной области и бизнес-объект второй модели в один. Так что ItemManager не нужен, в этой модели всего три класса, это:

  • Пункт: содержит физическую информацию, также содержит все бизнес-логику
  • ItemDao: постоянный класс интерфейса DAO
  • ItemDaoHibernateImpl: класс реализации интерфейса DAO.

В этой модели вся бизнес-логика находится в Item, и управление транзакциями также реализовано в Item. Преимущества этой модели:

  1. Более соответствует принципам ООП
  2. Слой службы очень тонкий и играет только роль фасада и не имеет отношения к DAO.

Недостатки этой модели:

  1. DAO и объект домена образуют двусторонние зависимости, сложная двусторонняя зависимость приводит к большому количеству потенциальных проблем.
  2. Как разделить логику сервисного слоя и логику доменного слоя очень неоднозначно, в реальных проектах из-за различий в уровне дизайна и разработчиков вся структура может быть хаотичной и беспорядочной.
  3. Учитывая особенности инкапсуляции транзакций уровня Сервиса, уровень Сервиса должен обеспечивать соответствующие методы инкапсуляции транзакций для логики всех объектов домена, в результате чего Сервис полностью переопределяет всю логику домена, что очень громоздко, и транзакционную инкапсуляцию Служба имеет смысл Это эквивалентно преобразованию доменной логики OO в Service TransactionScript процесса.

Модель отека крови

Основываясь на третьем недостатке модели гиперемии, некоторые студенты предложили отменить сервисный уровень, оставив только доменный объект и слои DAO и инкапсулировав транзакции в доменную логику доменного объекта.

объект домена (инкапсуляция транзакций, бизнес-логика) DAO

Похоже, ruby ​​on rails и есть эта модель, он даже слил и объект предметной области, и DAO.

Преимущества этой модели:

  1. Упрощенное наслоение
  2. Также в соответствии с ОО

Недостатки этой модели:

  1. Многие сервисные логики, которые не являются доменной логикой, также принудительно внедряются в доменные объекты, вызывая нестабильность модели доменных объектов.
  2. Объект домена предоставляет веб-слою слишком много информации, что может вызвать неожиданные побочные эффекты.

резюме

Среди этих четырех моделей модель кровопотери и модель отека крови не следует поддерживать. Модель анемии и модель гиперемии технически осуществимы. Что лучше, модель анемии или модель гиперемии? По этому вопросу велись затяжные дебаты, но в итоге мало что дали. В центре дискуссии между двумя сторонами находятся в основном два предложения, которые я выделил жирным шрифтом выше, то есть должна ли модель предметной области полагаться на уровень сохраняемости, потому что опора на уровень сохранения означает, что разработка модульных тестов более сложна. (без фреймворка нельзя протестировать, в исходном обсуждении здесь речь идет именно о Hibernate), доменному слою сложнее быть независимым, и в будущем его будет сложнее отделить от приложения. Преимущество в том, что бизнес-логику не нужно смешивать на разных уровнях, что делает единую ответственность лучше. Сторонники (модели перегрузки) считают, что пока слой персистентности абстрагирован, сложность тестирования может быть уменьшена.В то же время применение модели гиперемии приносит много удобства в разработке.Помимо опоры на Слой стойкости, он имеет больше Модель преимущества гиперемии по-прежнему стоит выбирать. В конце концов, никому не удалось никого убедить в том, что выбор модели анемии и модели гиперемии больше зависит от конкретных бизнес-сценариев, и невозможно сказать, какая из них лучше, чем какая. Разве такой шаблон проектирования не всегда неубедителен?

Я лично предпочитаю использовать модель гиперемии, потому что модель гиперемии больше похожа на хорошо спроектированную системную архитектуру.К счастью, в компьютерном мире существует множество IOC и DI-фреймворков.Единственный недостаток заключается в том, что слой персистентности можно обойти с помощью различных обходные пути.С развитием технологий некоторые дефекты будут постепенно устраняться. Моя идея такова: сначала абстрагируйте слой сохранения в интерфейс, а затем внедрите слой сохранения в модель предметной области через уровень службы, чтобы модель предметной области зависела только от интерфейса слоя сохранения. И этот интерфейс можно абстрагировать с помощью технологии существующего фреймворка.

Ссылаться на

  1. 【DDD】Практика бизнес-моделирования - Удалить сообщение
  2. Анемия, интерпретация модели гиперемии и некоторый опыт