Domain Driven Design

Что это

Цитата с отличного объяснения на Stackoverflow:

DDD is about trying to make your software a model of a real-world system or process.

The idea is that you should be able to write down what the system does in a way that the domain expert can read it and verify that it is correct.

Разработка модели данных является одной из самых сложных задач в веб-разработке. Все дело в высокой связанности кода. Модель данных со временем обрастает кодом: views, templates, management commands. Менять структуру данных — значит переписывать и заново отлаживать существенную часть проекта. Занятие сложное и дорогое.

Названия моделей и полей берем из предметной области

Предположим, мы пишем сайт для агентства недвижимости и создали такую модель данных:

class Ads(db.Model):
    price = db.Column(db.Integer, index=True)
    description = db.Column(db.Text)
    show = db.Column(db.Boolean, index=True)

Прошло время, модель данных обросла кодом. А затем потребовались нововведения:

  1. Архивные объявления теперь видны и пользователями и поисковым роботам, все ради сохранения ссылочной массы;
  2. Теперь модератор проверяет каждое объявление прежде, чем его увидит пользователь сайта.

Ситуация получается печальной… Теперь не ясно, за что же отвечает флаг show: он для архивных объявлений или для непроверенных? Пример, изменение поля show приведет к веерным правкам по всему коду проекта с последующим увлекательным дебагом.

Риелторы делят объявления на актуальные и устаревшие, но ничего не знают про видимые и невидимые.

Обобщить идею можно следующим образом.

В модели данных нельзя использовать названия и термины отсутствующие в предметной области

Еще один пример для наглядности. Тот же проект про недвижимость. У объявления появилось новое поле user:

class Ads(db.Model):
    user = Column(db.Integer, db.ForeignKey('user.id'))
    price = db.Column(db.Integer, index=True)
    description = db.Column(db.Text)

Стоит сразу задать себе вопрос “Какую роль играет этот user с точки зрения бизнеса?” Он автор объявления или собственник жилья? Или он риелтор? А может, это модератор, проверявший объявление? И что мы будем делать, когда каждая эта роль получит отдельное поля в модели данных? Как не запутаться во всех этих юзерах?

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

Не придумывать новые сущности

Например, можно выдумать некую AutoDeal для брокеров. А потом долго и упорно им объяснять что это такое, зачем нужно, какое отношение имеет в валютным парам, Bid и Ask.

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

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

Не придумывать новые связи

Рассмотрим пример из другой области. Пиццерия создает себе сайт, а в базу данных кладет меню такого вида:

catalog = [
  {
    'title': 'Маргарита',
    'description': 'соус,сыр Моцарелла',
    'choices': [
      {'title': "30 см (450гр)", 'price': 360},
      {'title': "40 см (750гр)", 'price': 460}
    ],
  },
  // ... а еще 'Грибная', 'С ветчиной', 'Ветчина с грибами', всего 30 наименований
  // все они с идентичными choices: размером и ценой
]

Сразу бросается в глаза: у всей пиццы идентичные choices. Давай их объединим, и менеджерам не придется вбивать одни и те же цифры 100500 раз, они будут рады. Достаточно завести несколько сущностей Choice в базе и связать их отношением ManyToMany с Pizza. Получим такую картинy: 30 записей Pizza связаны с 2 записями Choice через промежуточную таблицу для связывания PizzaChoice.

Теперь цену на всю пиццу можно менять разом, буквально несколькими кликами мыши! Красота!

Посмотрим как дальше будут развиваться события. Проходит неделя и на пиццу #25 поднимают цену на 30%. Она готовится по необычному рецепту, такую никто из конкурентов не предлагает. И пусть стоимость ингредиентов та же, гурманы готовы платить больше. В базе данных создаем две новые записи Choice, не беда.

Проходит еще неделя, продажи падают — конкурент в соседнем квартале демпингует, приходится подстраиваться. Выбираем 10 самых популярных рецептов пиццы, снижаем цену на 10%, ищем дополнительные источники прибыли и партнеров. В базе данных появилось две дополнительные записи Choice.

И вот прошел год. В базе данных появились порядка 20 записей Choice, еще больше записей PizzaChoice. Понять смысл их связи и группировки не представляется возможным. Хаос победил. И нет никакого встроенного механизма рефакторинга, придется все сносить и вбивать цены заново. Печаль. Может, дело в неправильной группировке?

Суть проблемы в том что никто не знает как правильно группировать рецепты пиццы, какой критерий взять за основу. Более того, в природе такого критерия просто не существует. И никакой связи ManyToMany тоже не наблюдается, по крайней мере с ценами. А вот размер пиццы действительно может быть обусловлен общепринятыми стандартами.

Мы старались упростить работу менеджерам. Хотели как лучше, а получилось как всегда. ©

Не упрощать связи

Если в реальном мире сущности связаны отношением ManyToMany, то не стоит их «упрощать» до OneToMany или OneToOne. Требования меняются всегда, помни об этом! Через полгода то, что «сейчас совершенно не нужно» может стать жизненно необходимым, а подобные изменения модели и логики даются дорогой ценой.

Что дает DDD

Стабильность модели данных

Ведь если мы используем устоявшуюся терминологию, то на нее можно положиться. Будет меньше рефакторинга и больше фич!

Простой код

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


Попробуйте бесплатные уроки по Python

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

Переходите на страницу учебных модулей «Девмана» и выбирайте тему.