В этом документе подробно описан foodtruacker — проект, реализующий доменно-ориентированное проектирование (DDD), CQRS и поиск событий. Он использует ASP.NET Core и фокусируется на улучшении удобства обслуживания в сложных бизнес-доменах. В иллюстративных целях в проекте используется упрощенный вымышленный бизнес-кейс. Это подробное объяснение охватывает мотивы, особенности, детали реализации и соответствующие технологии.
foodtruacker — реализация DDD, CQRS и источников событий
В этом событийно-ориентированном проекте используются принципы, структуры и архитектуры — все они сосредоточены на идее повышения удобства сопровождения при работе с системами, отражающими сложные бизнес-области. Веб-API приложения построен на платформе Microsoft ASP.NET Core и реализует доменно-ориентированное проектирование, а также шаблоны CQRS и поиска событий. Вымышленный бизнес-кейс закладывает основу этого проекта и является результатом семинара по штурму событий.
Обратите внимание: вымышленный бизнес-домен, представленный в этом проекте, сильно упрощен, и его следует рассматривать только как поставщик подходящих вариантов использования.
Мотивация
Поскольку не всегда лучше использовать CRUD-операции и POCO-объекты в проектах с довольно сложными бизнес-доменами, я решил создать этот проект как практическую реализацию моих исследований и интереса к предметно-ориентированному проектированию (DDD). подход к разработке программного обеспечения.
Поскольку вымышленный бизнес-кейс, представленный в этом проекте, в значительной степени ориентирован на события, я решил также реализовать шаблоны CQRS и Event Sourcing. Оба привлекли мое внимание во время исследования для этого проекта и хорошо сочетаются с DDD.
Функции
Обзор
Этот проект состоит из одного исполняемого приложения веб-API и нескольких функциональных компонентов, каждый из которых предоставляется через библиотеки классов. Код организован по пространствам имен. В приложениях ASP.NET Core, созданных в Visual Studio, пространства имен по умолчанию автоматически создаются из структуры папок проектов. На диаграмме ниже представлен обзор структуры папок (и пространства имен) этого проекта:
Введение
Штурм событий
Гибкий формат семинара для совместного изучения сложных областей бизнеса, изобретенный Альберто Брандолини. Это чрезвычайно легкая методология для быстрого улучшения, прогнозирования, изучения и проектирования бизнес-потоков и процессов в вашей организации.
В семинаре группа людей с разным опытом использует цветные стикеры для совместной планировки соответствующих бизнес-процессов. На семинаре EventStorming обязательно должны присутствовать нужные люди и иметь достаточную площадь для размещения стикеров. В число требуемых людей обычно входят те, кто знает, какие вопросы задавать (обычно разработчики), и те, кто знает ответы (эксперты в предметной области, владельцы продукта).
Цель этого семинара — дать участникам возможность учиться друг у друга, выявлять и опровергать заблуждения и, например, в этом проекте GitHub, заложить основу для разработки программного решения на основе событий, отражающего соответствующую сферу бизнеса.
Доменно-ориентированное проектирование (DDD)
Подход к разработке программного обеспечения, в котором основное внимание уделяется программированию модели предметной области, обеспечивающей глубокое понимание процессов и правил соответствующей бизнес-доменности. Термин «Дизайн, управляемый предметной областью» был придуман Эриком Эвансом в его одноименной книге.
DDD стремится облегчить создание сложных приложений и фокусируется на трех основных принципах:
В книге Эрика Эванса определены несколько общих терминов для предметно-ориентированного проектирования:
Модель предметной области
Система абстракций, описывающая процессы и политики бизнес-домена и используемая для выполнения необходимых задач, связанных с этим доменом.
Вездесущий язык
Слова и утверждения для определенных элементов бизнес-сферы. Чтобы снова избежать заблуждений, все члены команды должны принять определенные термины, обычно те, которые используют эксперты в предметной области.
Ограниченный контекст
Концептуальная граница, в пределах которой определена и применима конкретная модель предметной области. Обычно это представляет собой подсистему или область работы. В основном это лингвистическое разграничение, при котором каждый ограниченный контекст имеет свой собственный вездесущий язык.
Например: Управление клиентами, где пользователь называется «клиент».
Книга Эрика Эванса дополнительно разграничивает определенные части модели предметной области. Вот некоторые из них:
Сущность
Объект, который определяется своей идентичностью, а не атрибутами.
Например: Человек всегда останется тем же человеком, независимо от выбора куртки, цвета волос или языка, на котором он говорит в определенный момент.
Объект значения
Объект, который определяется исключительно значением его атрибутов. Объекты-значения неизменяемы и не имеют уникального идентификатора. Объекты-значения могут быть заменены другими объектами-значениями с теми же атрибутами.
Например: если сосредоточиться на человеке, сломанную пару солнцезащитных очков можно легко заменить новой, одинаково выглядящей парой солнцезащитных очков.
Совокупный
Кластер из одной или нескольких сущностей и необязательных объектов-значений, объединенных в единую транзакционную единицу. Одна сущность образует основу Агрегата и поэтому объявляется корнем Агрегата. Все свойства взаимодействующих с ним сущностей и объектов-значений могут быть доступны только через эту единую базовую сущность. Агрегат всегда должен находиться в согласованном состоянии. В объектно-ориентированном программировании это обычно делается с помощью частных установщиков и защищенных геттеров.
Например: в контексте продажи автомобилей один автомобиль (Организация) определяется его идентификационным номером. У этого автомобиля могут быть четыре колеса (Объекты-значения), которые через определенное время может потребоваться заменить.
Событие домена
Объект, созданный в результате деятельности в рамках модели предметной области. Он используется для хранения и передачи информации, связанной с этой деятельностью. События предметной области обычно создаются для тех действий, которые эксперты предметной области считают актуальными.
Шестиугольная архитектура (порты и адаптеры)
Шаблон архитектуры, используемый при проектировании программного обеспечения, предложенный Алистером Кокберном в 2005 году. Этот шаблон направлен на достижение высокой степени удобства сопровождения и описывает приложение на трех уровнях. Каждый уровень взаимодействует со соседними уровнями с помощью интерфейсов (портов) и реализаций (адаптеров):
Ключевое правило этого шаблона архитектуры заключается в том, что зависимости могут указывать только внутрь. Ничто во внутреннем круге не может ничего знать о чем-то во внешнем круге. Любые зависимости, указывающие наружу, например, вызов базы данных с прикладного уровня, должны быть созданы посредством инверсии управления (IoC) или внедрения зависимостей (DI).
CQRS с использованием MediatR (предварительно созданной среды обмена сообщениями)
CQRS означает разделение ответственности за команды и запросы и впервые был описан Грегом Янгом в 2010 году. Он основан на принципе разделения командных запросов (CQS) и позволяет разделить операции чтения и записи. CQS сообщает:
Преимущество CQRS по сравнению с CQS заключается в том, что эти команды и запросы рассматриваются как модели, а не как методы. Эти модели могут быть отправлены как объекты в одной точке, чтобы затем обрабатываться соответствующими обработчиками в другой точке системы, каждый из которых возвращает свои модели ответа для четкого разделения каждого действия.
Шаблон посредника позволяет реализовать слабосвязанные команды/запросы и обработчики, используя объект-посредник. Объекты больше не общаются друг с другом напрямую, а общаются через посредника.
Платформа MediatR — это реализация шаблона посредника с открытым исходным кодом, созданная Джимми Богардом. Он будет использоваться в этом проекте для связи между уровнем платформы и уровнем приложения. Он также будет использоваться для проецирования данных из базы данных команд в базу данных запросов.
Поиск событий
Шаблон архитектурного проектирования для хранения всех изменений в состоянии приложения, а не только текущего состояния данных в домене. Этот шаблон был представлен Грегом Янгом и с тех пор получил множество применений.
Шаблон призван фиксировать каждое изменение состояния приложения в виде объекта события. Эти объекты событий затем сохраняются в порядке появления только в виде добавления. Это не только позволяет воссоздать текущее состояние объекта на основе последовательности событий, которые произошли до сих пор, но в конечном итоге позволяет вернуться во времени и воссоздать состояние объекта для любого заданного времени.
Банковский счет может быть хорошим примером принципа поиска событий. Каждый раз, когда деньги снимаются или вносятся, вместо простого обновления текущего баланса записывается сумма сдачи. Затем текущий баланс рассчитывается путем анализа последовательности событий с соответствующей информацией о том, сколько денег было снято или внесено каждый раз.
Источник событий хорошо сочетается с проектированием на основе домена, поскольку он отлично подходит для хранения событий домена, инициируемых моделью домена при каждом запросе на изменение.
Служба поиска событий также значительно выигрывает от CQRS. Вместо того, чтобы делать запрос к базе данных источников событий, которая должна была бы пройти через все записанные события, связанные с запрашиваемым объектом, чтобы воссоздать текущее состояние, этот запрос можно выполнить к выделенной базе данных запросов. Эта база данных запросов обновляется собственными обработчиками событий, прослушивающими те же события, отправляемые сразу после добавления в базу данных источников событий. Эти процессы обновления называются прогнозами.
Такое разделение баз данных также закладывает основу для огромного потенциала масштабируемости и оптимизации производительности. Можно создать и синхронизировать несколько экземпляров базы данных запросов, просто заставив их обработчики событий прослушивать события, отправляемые из клиента базы данных источников событий сразу после того, как произошло соответствующее изменение состояния приложения. Выбор типа базы данных, а также степени денормализации данных, оптимизированной для каждого запроса, могут значительно повысить производительность.
Это постоянное обновление модели чтения может происходить синхронно или асинхронно. Последнее достигается за счет конечной согласованности, поскольку модель чтения не синхронизируется с моделью записи на небольшой промежуток времени (обычно миллисекунды).
Общее ядро
Общая библиотека для уровня предметной области, которая содержит общие базовые классы, специфичные для предметно-ориентированного проектирования, объекты предметной области, объекты значений и т. д., которые совместно используются в ограниченных контекстах.
Начиная
Чтобы запустить этот проект как есть, выполните следующие действия:
Предварительные условия
Настраивать
Запустите https://localhost:5001/swagger/index.html в своем браузере, чтобы просмотреть документацию Swagger по вашему API.
Используйте Swagger, Postman или любое другое приложение для отправки POST-запроса на https://localhost:5001/api/Administration/Register для регистрации вашей первоначальной учетной записи администратора. Отправьте следующий объект:
Посмотрите консольное приложение или любой другой вывод, который был перенастроен для журналов приложения. После любой успешной регистрации пользователя в журналах должна быть ссылка для проверки электронной почты, предоставленная EmailService. Скопируйте и вставьте этот URL-адрес в свой браузер и нажмите Enter, чтобы завершить регистрацию. Не стесняйтесь изменять или использовать эту неправильную реализацию службы электронной почты ;-)
Все готово. Далее войдите в систему.
Запустите http://localhost:2113/ в своем браузере, чтобы просмотреть графический интерфейс EventStoreDB. Откройте вкладку «Браузер потоков», чтобы просмотреть все сохраненные события.
Тесты можно выполнить, запустив:
Технологии
В этом проекте используются следующие пакеты технологий/NuGet:
Ресурсы / Рекомендуемая литература
Альберто Брандолини:
https://www.eventstorming.com
Вон Вернон:
https://dddcommunity.org/wp-content/uploads/files/pdfarticles/Vernon2011_1.pdf
https://dddcommunity.org/wp-content/uploads/files/pdfarticles/Vernon2011_2.pdf
https://dddcommunity.org/wp-content/uploads/files/pdfarticles/Vernon2011_3.pdf
Алистер Кокберн:
https://web.archive.org/web/20180822100852/http://alistage.cockburn.us/Hexagonal+architecture
Роберт К. Мартин (дядя Боб):
https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
Сезар де ла Торре, Билл Вагнер, Майк Русос:
https://docs.microsoft.com/en-us/dotnet/architecture/microservices/
Грег Янг
https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf
https://cqrs.wordpress.com/documents/building-event-storage/
https://msdn.microsoft.com/en-us/library/jj591559.aspx
Мартин Фаулер:
https://www.martinfowler.com/bliki/CQRS.html
Джимми Богард:
https://github.com/jbogard/MediatR
https://www.youtube.com/watch?v=SUiWfhAhgQw
Доменно-ориентированный дизайн:
https://dddcommunity.org
https://thedomaindrivendesign.io
https://dotnetcodr.com/2013/09/12/a-model-net-web-service-based-on-domain-driven-design-part-1-introduction/
https://dotnetcodr.com/2015/10/22/domain-driven-design-with-web-api-extensions-part-1-notifications/
Шестиугольная архитектура:
https://fideloper.com/hexagonal-architecture
https://herbertograca.com/2017/09/14/ports-adapters-architecture/
Кредиты
http://www.andreavallotti.tech/en/2018/01/event-source-and-cqrs-in-c/
https://www.Exceptionnotfound.net/real-world-cqrs-es-with-asp-net-and-redis-part-1-overview/
https://buildplease.com/pages/fpc-1/
https://dotnetcoretutorials.com/2019/04/30/the-mediator-pattern-in-net-core-part-1-whats-a-mediator/
https://itnext.io/why-and-how-i-implemented-cqrs-and-mediator-patterns-in-a-microservice-b07034592b6d