После короткой книги Vladik Khononov я решил, что надо углубиться в тему DDD и прочитать книгу Eric Evans, раскрывающую тему более подробно. Совсем не пожалел и в очередной раз убедился, что некое краткое изложение — это хорошо, но более подробная и полная книга позволяет действительно проникнуться идеями.
Книга состоит из 4х больших частей.
В первой автор рассуждает о важности общей модели предметной области — общей и для программистов и для представителей бизнеса. Сама мысль изложена и в What is domain driven design, но тут она подается подробнее, с примерами из жизни. Интересная мысль, что целостность модели и ее общий язык — это не только то о чем мы договорились в начале. Если в процессе работы мы нашли более удачные слова, точнее описывающие нашу область и лучше понятные бизнесу, то необходимо отразить это в том числе в коде. Необходим рефакторинг кода, для того чтобы используемый в нем язык продолжал совпадать с языком модели, чтобы не происходило постепенного расхождения.
Во второй — автор переходит от общих рассуждений о моделецентричности при разработке к конкретным предложениями как проектировать ПО так, чтобы в итоге оно отражало модель предметной области. Для этого сначала вводятся понятия entity, value object, service описывающие различные составные части системы. Затем — aggregate, группирующий их в тесно связанные с точки зрения бизнеса блоки, factory — для создания сущностей и агрегатов, repository — для внешнего хранения сущностей и восстановления их из внешнего хранилища в память. В конце главы приводится пример проектирования системы управления перевозкой товаров на основе предлагаемых принципов из этих кирпичиков.
С одной стороны здесь не описывается ничего революционного, но с другой методично и с примерами показывается как может быть упрощена или наоборот усложнена система, если глубже вникнуть в бизнес требования и отразить их при проектировании. Как влияет на дизайн правильное понимание, что в бизнес области является entity, а что просто value, т.е. определяется только своими свойствами и не требует отслеживания уникальности. Как упрощает систему замена двунаправленных связей однонаправленным, где это возможно. Как грамотное использование репозиториев может решать проблемы с конкретными изменениями (опять таки если бизнес требования позволяют это)
В третьей части автор обсуждает рефакторинг, как необходимый компонент поддержания соответствия кода модели, по мере ее изменения и усовершенствования. Начинает он с примера из жизни, когда вникнув глубже в доменную область, удалось создать более удачную её модель, что с одной стороны позволило упростить дальнейшую разработку, устранив недопонимания с бизнес-заказчиками, но с другой потребовало существенного переписывания системы под новую модель. После этого, ещё порассуждав о том, что модель постоянно улучшается и меняется — переходит к рассуждениям о гибком и в то же время понятном дизайне приложений. Сначала обсуждается, как лучше отражать бизнес-правила и ограничения в коде. Альтернативой вынесения сложной бизнес логики в уровень приложения предлагается создание классов-спецификаций, инкапсулирующих внутри себя проверку неких бизнес-правил, а возможно во взаимодействии с репозиториями и выборку объектов, удовлетворяющих спецификации.
Другими инструментами гибкого дизайна автор называет:
— интерфейсы, с понятными названиям как самих классов, так и методов, чтобы из самих их названий было понятно, что они делаютчистые функции
— операции над иммутабельными объектами
— операции над value objects, которые бы возвращали новые value objects
В четвертой части автор переходит к вопросам организации работы большой компании со сложным продуктом, над которым работает несколько команд. На примерах он показывает, какие проблемы могут возникнуть, когда команды работают в рамках своих моделей, но их границы четко не очерчены и одна команда может вносить изменения, нарушающие работу кода, поддерживаемого другой. Чтобы не допускать таких проблем предполагается концепция bounded contexts — четко очерченных границ моделей, с понятными местами их пересечения. Если это сделано — то, как минимум проблема обозначена и обозначены пересечения, по которым работа должна быть в какой-то степени совместной и надо принять решение как будет построена эта работа.
Автор рассматривает следующие способы взаимодействия команд, по степени уменьшения степени интеграции: continuous integration, shared kernel, upstream/downstream, anticorruption level, open host service, separate ways. Суть ясна из названий, разве что стоит пояснить что под anticorruption автор понимает набор фасадов/адаптеров, которые защищают нашу модель от «повреждения» неподходящей нам чужой моделью. Важная мысль при этом, что выбор модели взаимодействия — это чаще всего не техническое, а организационное решение и зависит прежде всего от размера команд, их взаимоотношений, их организационной подчинённости, общности их целей.
Но возвращаясь чуть назад, как разделить единую и ставшую слишком сложной и запутанной модель на отдельные bounded contexts, чтобы с одной стороны над ними могли работать независимые команды, а с другой — чтобы усилия наиболее сильных программистов (команд) были направлены на действительно важные вещи, а какие-то менее критичные могли бы быть сделаны силами джунов или вовсе отданы на аутсорс?
Для этого необходимо провести model distillation — анализ модели и выделение, что в ней является нашим core domain — тем, что мы на самом деле делаем, что составляет цель нашего продукта, чем он отличается от конкурентов. А что является generic / supporting domains — частями продукта, которые необходимы, но при этом могут быть такими же как у всех вокруг, решают задачи поддержки, не являются основными. Часто такими частями являются биллинг, поддержка, общие технические модели работы с сетью / датами и т.п.
Для упрощения основной модели полезно выделить generic subdomains в отдельные модули / команды, а возможно и отдать на аутсорс для ускорения разработки. Другим подходом может быть наоборот явное выделение в отдельный модуль / сервис в первую очередь core domain (автор называет этот подход segregated core), а уже потом распиливание остатка.
В конце книги автора рассуждает о том, как поддерживать целостный дизайн больших сложных систем. Одним из решений для этого он видит слоеную архитектуру, которая бы с одной стороны ограничивала дизайн-решения, не позволяя создать беспорядочный клубок связей, с другой стороны делала бы систему более понятной (в том числе за счёт дополнительной информации о принадлежности модулей разным слоям). Но при этом автор предупреждает об опасностях излишнего увлечения глобальными структурами / генеральными планами — когда они не дают достаточной гибкости программистам, они становятся не помощниками, а наоборот — силы начинают тратиться на поиски обходных путей и впихивание невпихуемого. Также плоха идея выделения специальной команды лучших программистов чисто для создания общей архитектуры — если они не участвуют в решении повседневных задач, то вероятность создания переусложненной и только мешающей работе системы повышается. И вообще
Team divisions that assume some developers are not smart enough to design are likely to fail because they underestimate the difficulty of application development. If those people are not smart enough to design, they shouldn’t be assigned to develop software. If they are smart enough, then the attempts to coddle them will only put up barriers between them and the tools they need