Способы отображения: существует ли связь между DDD и ООП

В ходе обсуждений докладов на Analyst Days возник вопрос о связи Domain-driven design (DDD) с объектно-ориентированным подходом (ООП): оказывается, для большинства она вовсе не так очевидна, как мне представлялось. Подробнее погружаясь в это обсуждение, я понял, что для современной разработки их общность действительно не очевидна, а практики DDD можно применять, не связывая с ООП. Я думаю, что подробное рассмотрение этого вопроса будет полезно для получения комплексного представления и понимания DDD, что сделает его применение эффективнее.

Для начала — про саму аббревиатуру ООП. Дело в том, что на русском она скрывает три возможных расшифровки: объектно-ориентированный подход, объектно-ориентированное программирование и объектно-ориентированное проектирование. Первая из них является объединяющей для двух других.

Исторически первым возникло объектно-ориентированное программирование, Object-Oriented Programming. Его распространение связано с появлением и последующим развитием языка С++ (1980). Позже это развитие в языке Java (1995), хотя теоретики говорят о более ранних истоках — Algol, Pascal и т. д. Дальнейший прогресс разворачивается вместе с объектными языками.

Теория включает в себя принципы абстракции данных, инкапсуляции внутри объекта, наследования, полиморфизма, SOLID, способов создания различных агрегатов и шаблоны для абстракций. Всё это хорошо известно разработчикам на объектных языках, которыми являются практически все распространенные сейчас языки, правда, в разной мере. Вернее, самые современные языки являются мультипарадимальными, включающими объектную, функциональную и реляционную парадигмы. Начало этому положил C# 3.0 в 2008, но как правильно работать в смешанных парадигмах, теоретики не разобрались до сих пор.

В 1990-х, то есть примерно на 10+ лет позже, появилось объектно-ориентированное проектирование, по-английски OOAD — Object Oriented Analysis and Design. Его разрабатывала большая группа методологов, одним из результатов этой разработки стал UML. Он включал создание модели предметной области в виде структуры объектов и их поведения. В целом это был перенос методов объектно-ориентированного программирования на работу с предметной областью: Object-oriented analysis and design (OOAD) is a technical approach for analyzing and designing an application, system, or business by applying object-oriented programming.

И этот подход был основным в начале 2000-х, когда Эрик Эванс разрабатывал Domain Driven Design. Поэтому модель предметной области у Эванса — это не бизнес-процессы, а бизнес-объекты: клиенты, договоры, платежи и т. д. При этом выделяют не только структуру данных, но и поведение этих объектов в виде методов. А также workflow этих объектов в ходе исполнения операций, описываемых через граф состояний. Предполагается, что бизнес-процессы будут поддержаны за счёт движения объектов по workflow, поэтому им особого внимания не уделяется. Может быть, конкретные способы придумает сам бизнес.

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

Отмечу, что в моей практике применение DDD устроено именно таким образом. Я описывал это в статье «Domain Driven Design: модели вместо требований», а здесь привожу комплексную схему оттуда: бизнес-процесс, отражённый с помощью Activity diagram, реализуется workflow документов, для которых наверху представлена диаграмма классов, а ниже — диаграмма состояний, в которой переходы документов соответствуют выполнению бизнес-функций. При этом в реализации мы специально размечали классы, соответствующие сущностям в модели, и методы, которые реализуют граф переходов. И это могло быть основой интерфейсов, реализуемых в парадигме Naked objects там, где это было уместно.

9f0aba3e9df9c60de15a3fc3f968c4bb.jpg

Модель предметной области надо согласовать с бизнесом, и её же должны понимать разработчики. Для этого надо строить единый язык, понимаемый всеми участниками проекта, а не просто делать словарь предметной области.

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

Ограниченные контексты появляются у Эванса в продвинутых методах, только в конце — 14 глава 4 части. Однако, по дальнейшему опыту он говорил, что ошибся, отнеся их так далеко, потому что это оказалось одним из самых важных концептов наряду с единым языком как таковым. И когда Вон Вернон писал свою книгу «Реализация методов предметно-ориентированного проектирования», то перенёс их в начало. Это всё — история.

В 2010-х всё то же самое проходят на другой парадигме, не объектной, а акторной, событийной модели. Появился концепт реализации систем как набора взаимодействующих через messaging акторов и технические средства для этого. Теоретики пишут, что это было придумано в Smalltalk много раньше, но Smalltalk не взлетел по технологическим проблемам того времени. А на рубеже 2010 появляется событийная архитектура, микросервисы, CQRS, RabbitMQ и Kafka, а затем — реактивный манифест как концепция такого подхода.

А дальше, как и с объектным подходом, концепт программирования переносят на моделирование предметной области. В 2017 появляется Event Storming как средство получить модель предметной области в терминах событийной модели. И это обеспечивает возможность прозрачного отражения модели в код, но уже не для объектной парадигмы, а для событийной модели. Именно этот вариант сейчас часто используется, и потому связь DDD с ООП видна слабо.

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

Таймлайн развития представлен на рисунке.

5a3561d027c617a79aa4f7ebb185e665.jpg

Отдельно стоит остановиться на концепте прозрачного отражения модели в код, трассируемости элементов модели, какими бы они ни были, до кода. Это — принципиальный момент DDD, и даже отражается в названии: Domain Driven Design. Если мы ограничиваемся моделированием предметной области и не отражаем нашу модель в реализацию прозрачно, то мы возвращаемся к классическому подходу из двух моделей: модель предметной области — дизайн софта с однократным переходом от одной к другой. И теряем основное преимущество DDD, которое проявляется на этапе развития системы: если модель и код связаны регулярным образом, то мы можем оценить сложность и стоимость доработок по сложности и стоимости изменения модели.

Способ отражения может быть разным. Как я писал выше, у Эванса большинство примеров — с использованием OOП и конкретно шаблона Business Object. Но он сам подчёркивает, что это для простоты изложения, а реальные способы могут быть более сложными. Действительно, даже в классических приложениях и объектной модели предметной области может быть удобно не использовать Business Objects, а делать для бизнес-объекта анемичный транспортный объект и контроллеры на сервере приложений и на клиенте, выделять отдельно фабрики и работу с коллекциями и т. д. И такие примеры у Эванса тоже встречаются.

А применение шаблона Business Object для реальных объектов может приводить к антипаттерну Big Object, потому что у реальных документов может быть очень много аспектов, связанных с разными этапами workflow. Например, если мы возьмём заказ интернет-магазина, то там и резервирование товара на складе, и фиксация адреса доставки, и взаимодействие с курьерскими компаниями и транспортными службами по организации доставки, и печатные формы документов. И для бизнеса это всё связано с заказом. Некоторые аспекты можно отделять понятным бизнесу образом, например доставку, другие можно отделить технически, сделав инфраструктурный механизм для печатных форм и применяя его одинаково для всех документов.

Но очень важно, чтобы способ отражения был единообразным, чтобы элементы модели прослеживались в коде. Если у вас в модели есть договоры, заказы и разные типы накладных, то и в коде должны быть они, а не «товарный документ» с каким-то другим делением. Если есть покупатели и поставщики, то опять-таки в коде тоже они, а не физические и юридические лица. Иначе ни о каком едином языке говорить невозможно.

Понятно, что может быть ситуация, когда разработчики во время дизайна хотят поменять набор объектов или ввести дополнительные абстракции. Но в этом случае нужно изменить модель (и ещё согласовать с заказчиком), чтобы было соответствие. А иначе получаются ситуации, что на запрос «добавьте атрибут к объекту» или «сделайте это перечисление ссылкой на справочник» следует ответ «мы это вынесли в базовый класс, и это затронет другие объекты, нам полсистемы перетестировать»…

То же самое, если мы работаем с моделью на основе событий и акторов: отражение может быть разным, но важно единообразие. Есть другие типы моделей, например, учёт или составление расписаний в терминах объектов описывается очень плохо, но это не значит, что DDD нельзя применять. В своё время я делал доклад о применении DDD для учётных моделей «Необъектные модели предметной области», а в докладе «DDD: проблемы и решения в отражении модели предметной области в код» подробно рассматривал вопросы единообразного отражения через технологические рельсы. Этим докладам уже более 10 лет, и позднее я развивал тему в других выступлениях.

В заключение хочу отметить, что в DDD, как и любая живая методология, развивается, в ней появляются новые практики. Event Storming — один из примеров. А ещё из DDD можно брать не полный набор практик, а применять лишь частично, и это тоже имеет смысл. Так что выражение «мы применяем DDD» при этом вполне может означать «мы применяем некоторые практики из DDD», и это нормально. Тем более, что нормативного определения для методологии нет, в отличие, например, от Scrum, для которого отдельное сообщество ведёт Scrum Guide и явно формулирует, что если его соблюдаете, у вас Scrum, а если нет, то лишь «процесс на основе Scrum». И на мой взгляд, такое множество диалектов — естественно, потому что все проекты разные и развиваются в разных условиях. В том числе это касается и принципа прозрачного отражения в код: его применение даёт преимущества и имеет цену, и в каждом проекте стоит это дело взвешивать и принимать решение, как поступать.

© Habrahabr.ru