вторник, 4 февраля 2014 г.

Ключевые концепции DDD

… и рецензия книги Эрика Эванса “Domain-Driven Design: Tackling Complexity in the Heart of Software”

Хороший дизайн приложения является главной основой любого приложения. Чрезмерная сложность дизайна приводит к увеличению стоимости изменений и некоторой «хаотичности» этого процесса. Чрезмерная простота – оставляет бреши в полученных моделях, в результате чего приложение может не выполнять тех функций, ради которых оно разрабатывается.

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

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

Паттерны проектирования – это попытка формализации некоторых типовых проблем и их решений, с которыми сталкивается разработчик приложений. Тоже самое можно сказать и о DDD. Давайте предположим, что мы не знаем ни одного принципа или паттерна из области предметно-ориентированного проектирования (что было справедливо для меня еще совсем недавно) и давайте вспомним некоторые типовые проблемы разработки ПО и наши с вами подходы к их решению.

ПРИМЕЧАНИЕ
Во многих проектах существует папка Domain, в которой расположены объекты-данные, папка DAL с набором репозиториев и папка Services, с набором сервисов. Если вы считаете, что это и есть DDD, то у меня плохие новости, все это, хоть и имеет некоторое отношение к DDD, не гарантирует наличие реальной доменной модели в вашем приложении.

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

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

Если вам знакома такая картина, то поздравляю, вы уже используете две ключевые концепции DDD: Model-Driven Design (Проектирование по модели) и Ubiquitous Language (Единый язык).

ЦИТАТА
Когда пользователи или эксперты предметной области используют общий словарь, который не находит отражения в дизайне системы, то это является тревожным знаком. Вдвойне опасно, когда разработчики и эксперты предметной области используют понятия, не отраженные в дизайне системы. Или, возможно, стоит рассматривать такую ситуацию, как возможность для улучшений. ЕДИНЫЙ ЯЗЫК состоит из словаря, который наполняет речь, документацию, диаграммы и даже код. Если некое понятие отсутствует в дизайне системы, то это хорошая возможность, чтобы улучшить модель и дизайн системы и добавить его туда.

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

Если и это отвечает вашим подходам к разработке, то вы знаете о другом фундаментальном принципе DDD: Supple Design (Гибкий дизайн), который опирается на трех китов – Intention-Revealing Interfaces (Информативный интерфейс), Assertions (Утверждения a.k.a Контракты) и Side-Effect-Free Functions (Функции без побочных эффектов).

ЦИТАТА
Давайте классам и методам имена, описывающие их цель и результат, не заостряя внимание на то, как они выполняют свои обещания. Это освободит разработчиков от необходимости понимать внутренности реализации. Эти имена должны соответствовать ЕДИНОМУ ЯЗЫКУ, что позволит членам команды быстро понять их значение.

Дальше, несмотря на качество своего дизайна вы периодически начинаете сталкиваться с такой дилеммой: а как быть с общими понятиями предметной области, модели которых несколько различаются в разных частях системы или в разных подсистемах? Что делать, если в нескольких местах у вас есть одно и тоже понятие, например, Employee, которое требует несколько разных подходов в разных контекстах? Наверняка вы начинаете взвешивать все «за и против» и думать над тем, стоит ли выделять общий базовый класс, использовать один и тот же класс в разных контекстах путем агрегации, или быть может лучше не связывать эти понятия вообще никак и позволить развиваться им независимо, даже рискуя получить некоторое дублирование.

В этом плане вам может очень помочь такое понятие из словаря DDD, как Bounded Context (Ограниченный контекст), с набором вспомогательных паттернов, которые к этому времени вы уже наверняка изобрели и сами.

В зависимости от задачи вы можете остановиться на общем «ядре», которое затем может использоваться по-разному в клиентском и серверном коде (Core Domain – Смысловое ядро). Если же эти модели находятся под контролем разных команд, то вы можете сделать разные модели и построить между ними простой слой трансформации (Translation Layer – Слой преобразования). Если же разные модели располагаются в разных системах, причем одна из систем была старой или написанной левой ногой, то вы можете построить между ними жесткий «фасадный» слой, придя, таким образом, еще к одному паттерну – Anticorruption Layer (Предохранительный слой).

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

Вы также наверняка задумывались о роли модулей в дизайне и старились делать их такими, чтобы в них располагались понятия, близкие друг к другу не столько с технической, но и с «доменной» точки зрения. Если все это так, то вы выполняли “DDD рефакторинг” и проектировали модули «предметно-ориентированным образом».

ЦИТАТА
Объяснения понятий связности и связанности (coupling и cohesion) обычно подаются с технической точки зрения, оценивая решения с точки зрения распределения ассоциаций и взаимодействий. Но не только код разделяется на МОДУЛИ, но и понятия предметной разделяются аналогичным образом. Существует предел на количество понятий, о которых может думать человек в один момент времени (отсюда слабая связанность, low coupling). Неправильно разделенные или смешанные в кучу несвязанные понятия очень сложно понять (отсюда сильная связанность, high cohesion).

Работая со слоем доступа к данным вы наверняка задумывались о том, какие именно объекты стоит вычитывать и что должно быть «корнем» такой группы объектов (Aggregate Root). В результате вы начали разделять все объекты на две группы, ведь некоторые объекты содержат некий уникальный идентификатор (Entities), а другие не являются самостоятельными и рассматриваются лишь в качестве составной части других объектов (Value Objects). Вы переосмыслили роль фабрик, поскольку в некоторых случаях значительно проще спрятать процесс создания группы объектов за некоторым «фасадным методом создания».

Вполне возможно, вы неоднократно пересматривали свое отношение к разбиению приложения на слои, меняя свое отношение к сервисам, слою доступа к данным, слою бизнес-логики и слою приложения (application layer).

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

И в этом плане нет лучшего помощника, чем хорошая книга, желательно от автора DDD – Эрика Эванса.

О книге Эванса “Domain-Driven Design”

clip_image002

Материал любой книги или статьи по определению избыточен. Автор начинает лить воду, чтобы выстроить подходящий контекст, который будет полезен большинству читателей. При этом, если вам этот контекст по какой-то причине не нужен, то все это будет для вас лишь информационным шумом. В результате такой избыточности мы начинаем выбирать из книги/статьи самое главное, игнорируя ненужные нам детали. Таким образом, КПД любой книги/статьи снижается, и если он опускается ниже определенного уровня (например, 50%), то мы забиваем на чтение и переходим на что-то другое.

Если измерять эффективность книги таким образом, то складывается впечатление, что в идеальном случае КПД от прочтения может быть максимум 80-90%, а учитывая то, что любую хорошую книгу легко можно перечитывать раз в несколько лет, то этот показатель для одного чтения должен быть еще ниже. Однако есть ряд книг, эффективность которых значительно выше этого уровня, на самом деле, она может легко превышать 100%. Каким образом? Да очень просто.

Наверняка у каждого разработчика есть книга, которая не просто понравилась, вы не просто прочли ее от корки до корки и запомнили каждое предложение. Наверняка у вас есть книги, которые изменили ваше отношение к разработке ПО целиком. Хорошая книга – это не просто набор интересных фактов, хорошая книга стимулирует вас думать над некоторой проблемой, расширяя ваш кругозор, заставляя вас размышлять над поднятыми в ней проблемами далеко за пределами рассматриваемого контекста.

Для меня главной такой книгой была книга Грэди Буча «Объектно-ориентированный анализ и проектирование», прочитанная мною в первые полгода работы программистом. Именно первая ее часть сформировала мое отношение к разработке ПО, к дизайну и борьбе со сложностью. Для кого-то эту же роль сыграла книга «банды четырех», еще для кого-то – книга Мейера, и, вполне возможно, есть немало людей, на которых неизгладимое впечатление оставила книга Эрика Эванса “Domain-Driven Design: Tackling Complexity in the Heart of Software”.

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

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

Ключевые особенности книги

Автор прагматик и не предлагает серебряных пуль; он четко говорит о плюсах и минусов тех или иных подходов к проектированию и дает понять, когда их лучше применять, а когда нет. У Эрика очень прагматичное отношение к дизайну, архитектуре и таким вещам, как тесты и процессы. Там нет категорических заявлений, типа TDD – форева, процессы – только XP, в приложении не менее трех слоев, а в каждом слое – два десятка моих паттернов!

Эрик Эванс делает акцент на фундаментальных принципах дизайна, а не на конкретных enterprise-паттернах. В книге рассматриваются Repository, Services, Entities, но гораздо больший акцент делается на декларативности дизайна, его качестве, архитектурных подходах, едином языке и других принципах.

В книге делается упор на принципах проектирования, значительно более фундаментальных и более точных, чем принципы SOLID и на паттернах, более высокоуровневых, чем паттерны проектирования банды четырех. При этом изложение обильно напичкано примерами, хотя, нужно признать, далеко не всегда простыми и понятными.

Об актуальности книги

Недавно я написал статью с критикой весьма популярной книги Боба Мартина «Принципы, паттерны и методики гибкой разработки на языке C#» и один из читателей откомментировал, что при чтении нужно учитывать возраст книги, дескать первое издание книги Мартина было выпущено в далеком 2002-м, что делает многие заявления несколько наивными и неактуальными.

С одной стороны иногда и правда нужно делать «поправку на возраст», но в этом случае стоит задуматься над тем, а стоит ли она ваших усилий и так ли она хороша, как о ней пишут? Почему не нужно делать никаких поправок на книгу Мейера «Объектно-ориентированное конструирование программных систем», выпущенную почти 20 лет назад и не нужно делать никаких поправок для книги Эванса, выпущенную в «далеком» 2003-м?

Книга Эрика Эванса абсолютно актуальна сегодня, как и десять лет назад и ничего в ней не говорит о столь почтенном возрасте.

Вердикт: Must Have!

Дополнительные ссылки

9 комментариев:

  1. Поздравляю и добро пожаловать в клуб! :) Там где заканчивается Blue Book, начинается Red Book. Милости прошу на Implementing Domain Driven Design, Vaughn Vernon.

    ОтветитьУдалить
    Ответы
    1. Спасибо. Уже куплена и лежит на полке и ждет своего часа, который должен наступить через несколько дней:)

      Удалить
  2. > связности и связанности (coupling и cohesion)
    в скобках перепутано местами

    ОтветитьУдалить
  3. Я где-то встречал перевод "cohesion" как "зацепление".

    ОтветитьУдалить
  4. Я когда-то плотно изучал этот вопрос во время редактирования книг. Там нет адекватного варианта перевода этих понятий, поскольку они не будут интуитивно понятными большинству разработчиков.

    Именно пожтому в разговоре я использую внешнюю связность (coupling) или внутреннюю связность ли цельность (cohesion).

    Альтернатив не вижу:((

    ОтветитьУдалить
    Ответы
    1. Как вариант, можно использовать термин "сплоченность".

      Удалить
    2. Сплоченность - любопытный вариант, но у меня она как-то ассоциируется с людьми, а не с кодом.

      Удалить
  5. Вы раньше ссылались на эванса в своих постах, не думал что вы ее еще не читали. Я кстати один из таких людей - книга эванса моя любимая.

    ОтветитьУдалить
    Ответы
    1. Vlad, я ссылался на нее лишь в последних постах, а до этого я ее действительно не читал, хотя многие (включая Yevhen Bobrov) настоятельно рекомендовали это сделать.

      Удалить