Поддомены: где сосредоточить силы
Как разделить предметную область на поддомены — основные, вспомогательные и типовые, — найти их по возможностям бизнеса, экспертам и языку, решить, куда тратить лучших людей и где хватит простого CRUD, выделить ядро и описать его на одной странице. Полный разбор онлайн-школы и решения «делать или купить».
Зачем это нужно
Заголовок раздела «Зачем это нужно»В онлайн-школе шесть разработчиков, и один из них по-настоящему сильный. Кому он должен посвятить ближайший год? Можно поручить ему систему уведомлений с повторными отправками и красивыми шаблонами. Можно — интеграцию с платёжной системой. А можно — правила записи на потоки, листа ожидания и возвратов, из-за которых школа теряет студентов и деньги. Если ничего не решать, решение примет сам разработчик — и, скорее всего, выберет технически интересное.
Эванс описывает эту закономерность в главе о выделении ядра: сильных разработчиков тянет к технической инфраструктуре и к задачам, которые можно понять без глубокого знания предметной области, а самая важная для бизнеса часть достаётся тем, кто остался. В итоге школа получает великолепную систему рассылок и запутанный Набор.
DDD предлагает ответ: разделить предметную область на поддоменыподдоменEN subdomainЧасть предметной области со своими задачами, экспертами и словами: Набор, Оплата, Обучение. Поддомены описывают пространство задачи — их обнаруживают, изучая бизнес. и честно признать, что они неравноценны. Одни отличают школу от конкурентов — туда лучших людей и полный арсенал DDD. Другие нужны, но просты — там хватит простого CRUD. Третьи все решают одинаково — их покупают. В этом уроке мы разберём онлайн-школу по этой схеме и напишем описание её ядра на одну страницу.
Предметная область делится на поддомены
Заголовок раздела «Предметная область делится на поддомены»Поддомен — часть предметной области со своими задачами, своими экспертами и часто своими словами. У онлайн-школы их шесть (как вы помните из урока DDD.1.1): Каталог, Набор, Оплата, Обучение, Сертификация, Уведомления. В Наборе говорят о потоках, местах и листе ожидания; в Оплате — о счетах, платежах и чеках; в Обучении — о занятиях, заданиях и прогрессе. Эксперт по Набору — менеджер набора, по Оплате — бухгалтер, по Обучению — методист.
Важная тонкость: поддомены описывают пространство задачи — как устроен бизнес. Их не придумывают, а обнаруживают: у школы есть набор студентов, даже если программы ещё нет. Как устроить код — какие модели, модули, сервисы, — это уже пространство решения, и его границы, ограниченные контексты, проектируют в уроке DDD.2.1. В нашей школе для простоты каждому поддомену будет соответствовать один контекст, но в жизни так бывает не всегда.
Зачем вообще делить? В процессе DDD Crew (DDD Starter Modelling Process) этому посвящён шаг «Decompose», и причины там названы три:
- меньше когнитивная нагрузка — о части можно рассуждать, не держа в голове всю школу;
- автономия команд — разные команды работают над разными частями, не мешая друг другу;
- слабая связанность — внутри поддомена много связей, между поддоменами мало, и это потом переносится в архитектуру и в устройство команд.
Следующий шаг того же процесса — «Strategize»: решить, какие поддомены основные. Ему посвящена остальная часть урока.
Три типа поддоменов
Заголовок раздела «Три типа поддоменов»Основной поддоменосновной поддомен (core)EN core subdomain, core domainТо, чем компания отличается от конкурентов и что даёт ей преимущество. Обычно сложен и часто меняется. Делают сами, лучшими людьми и с полным арсеналом DDD. (core subdomain; у Эванса — core domain) — то, чем компания отличается от конкурентов и за что ей платят. Обычно он сложен, потому что простое легко скопировать, и часто меняется, потому что компания постоянно ищет, как стать лучше. Его делают сами, лучшими людьми, с полным арсеналом DDD.
Вспомогательный поддоменвспомогательный поддомен (supporting)EN supporting subdomainНужен бизнесу, но преимущества не даёт, и логика в нём простая. Делают просто (CRUD, готовые конструкторы) или отдают на аутсорс — под тестами основной команды. (supporting) — нужен бизнесу, но преимущества не даёт, и логика в нём простая. Готового решения на рынке может не быть, потому что задача специфична для компании, но и изобретать там нечего. Его делают просто — CRUD, готовые конструкторы — или отдают на сторону.
Типовой поддоментиповой поддомен (generic)EN generic subdomainЗадача, которую все компании решают одинаково и для которой есть готовые решения: платежи, рассылки, вход в систему. Может быть сложной, но её покупают или берут готовой, а не изобретают. (generic) — задача, которую все компании решают одинаково: приём платежей, рассылки, вход в систему. Она бывает очень сложной (попробуйте сами сделать платёжный шлюз), но уже решена — её покупают или берут готовой.
Сравнение по мотивам первой главы книги Влада Хононова «Изучаем DDD»:
| Основной (core) | Вспомогательный (supporting) | Типовой (generic) | |
|---|---|---|---|
| Даёт преимущество над конкурентами | да | нет | нет |
| Сложность логики | высокая | низкая | высокая |
| Как часто меняется | часто | редко | редко |
| Как реализовать | сами, лучшими силами | сами просто или на стороне | купить, взять готовое |
| Задача | интересная, решения нет | очевидная | уже решена всеми |
Две из этих характеристик — дифференциация (отличает ли от конкурентов) и сложность модели — служат осями канвы Core Domain Chart от DDD Crew: каждый поддомен ставят точкой на плоскость. В виде таблицы она выглядит так:
| Сложность модели низкая | Сложность модели высокая | |
|---|---|---|
| Дифференциация высокая | core, но насторожитесь: простое легко повторить конкурентам | core |
| Дифференциация низкая | supporting | generic |
Авторы канвы подчёркивают: главная сила техники — разговоры, которые она вызывает. Сложность лучше оценивают инженеры, а дифференциацию — люди продукта и бизнеса; разногласия между ними и есть самое ценное. Оценки субъективны: стратегия — это ставка на будущее, а не измерение.
Как найти поддомены
Заголовок раздела «Как найти поддомены»Готового алгоритма нет, но есть надёжные подсказки. Хорошо работают сразу несколько:
- Возможности бизнеса. Перечислите глаголами, что делает компания: «показывать курсы», «набирать студентов на потоки», «принимать оплату», «учить», «подтверждать квалификацию», «сообщать студентам». Каждая возможность — кандидат в поддомен.
- Эксперты и отделы. У кого спросить о правилах? Методист, менеджер набора, бухгалтер, поддержка. Разные эксперты часто означают разные поддомены, хотя оргструктура и не всегда совпадает с доменом.
- Язык. Где слова меняют смысл, там граница. В Наборе — «студент», в Уведомлениях — «получатель», в Оплате — «плательщик».
- События. На стене событий из Event Storming (урок DDD.2.3) события естественно собираются в группы: всё о записи на поток, всё о деньгах, всё об учёбе. Процесс DDD Crew так и советует — нарезать «стену» на поддомены.
- Связность. Если две части постоянно меняются вместе и не могут жить друг без друга, возможно, это один поддомен. Если общаются редко и через пару фактов — разные.
Для каждого найденного поддомена задайте вопросы из README канвы Core Domain Charts:
- Насколько трудно новому игроку на рынке или существующему конкуренту повторить или превзойти эту возможность?
- Какое преимущество она даёт сейчас — в выручке, бренде, вовлечённости? А какое могла бы дать?
- Какой вред будет бренду, если здесь начнутся серьёзные или частые сбои?
- Насколько трудно спроектировать модель этой части и поддерживать её в коде?
- Есть ли сложные процессы, расчёты и решения, которые сейчас происходят вне программы — в головах и таблицах?
- Сколько времени нужно новичку, чтобы начать здесь работать эффективно?
Разбор онлайн-школы
Заголовок раздела «Разбор онлайн-школы»Теперь классифицируем шесть частей школы. Это классификация нашего учебного примера, на ней строятся все следующие уроки.
| Поддомен | Что делает | Тип | Почему |
|---|---|---|---|
| Набор | потоки, записи, лист ожидания, отмены и возвраты | core | школа учит потоками с ограниченным числом мест. От того, насколько хорошо заполняются потоки, честно работает лист ожидания и понятны правила отмены, прямо зависят выручка и отношение студентов. Правила сложные (время, статусы, очереди) и меняются вместе с маркетингом |
| Обучение | занятия, задания, прогресс | core | ради этого студенты и платят: методика, задания, проверка, прогресс — то, чем школа отличается от бесплатных видео. Эксперты — методисты, правила богатые и постоянно улучшаются |
| Каталог | описания курсов и программ, цены, витрина | supporting | без каталога не продать, но никто не выбирает школу за устройство каталога. Логика простая: создать, показать, изменить, скрыть |
| Сертификация | выдача сертификатов | supporting | нужна и важна для студентов, но правило «выполнил условия — получи документ» простое, а сам документ — шаблон |
| Оплата | счета и платежи через внешнюю платёжную систему | generic | критична, но все принимают платежи одинаково. Сложности — безопасность, банки, чеки, требования закона — давно решены платёжными сервисами |
| Уведомления | письма и пуши | generic | рассылки нужны всем; доставку, отписки и шаблоны дают готовые сервисы |
flowchart TB subgraph CORE["Основные (core): делаем сами, лучшими силами"] N["Набор<br/>потоки, записи,<br/>лист ожидания"] L["Обучение<br/>занятия, задания,<br/>прогресс"] end subgraph SUP["Вспомогательные (supporting): делаем просто"] K["Каталог"] C["Сертификация"] end subgraph GEN["Типовые (generic): покупаем, пишем адаптер"] P["Оплата"] U["Уведомления"] end K --> N N <-->|"счёт и ПлатёжПолучен"| P N -->|"ЗаписьПодтверждена"| L L --> C N -.-> U C -.-> U
Обратите внимание: в основных поддоменах почти все правила, которые мы записали в глоссарий в уроке DDD.1.2. Это не совпадение: правила, ради которых стоит спорить с экспертами, и живут там, где бизнес отличается от других.
Классификация — ставка, и со временем она меняется. Если школа решит конкурировать проверяемыми сертификатами, которые ценят работодатели (с проверкой навыков и защитой от подделки), Сертификация станет основным поддоменом, и вложения в неё придётся пересмотреть. И наоборот: когда-то уникальная возможность со временем становится товаром, и её выгоднее купить. Канва ограниченного контекста от DDD Crew поэтому предлагает записывать не только тип поддомена, но и стадию эволюции по Уордли: новое неисследованное, сделанное на заказ, продукт, массовый товар.
Куда тратить лучших людей и где допустим простой CRUD
Заголовок раздела «Куда тратить лучших людей и где допустим простой CRUD»Тип поддомена — это решение о том, как вкладывать силы:
| Основной | Вспомогательный | Типовой | |
|---|---|---|---|
| Люди | самые сильные, рядом с экспертами | любые, можно младших или подрядчика | тот, кто хорошо интегрирует чужие системы |
| Устройство кода | богатая модель: агрегаты, объекты-значения, события (модуль DDD.3) | CRUD, простые сценарии, готовые конструкторы | тонкий адаптер и предохранительный слой (урок DDD.2.2) |
| Тесты | на каждое правило, на языке экспертов | на основные сценарии | на адаптер и перевод чужих данных |
| Глоссарий и разговоры с экспертами | постоянно | по мере надобности | только о том, что нужно школе от чужой системы |
Для вспомогательных поддоменов хорошо подходят простые шаблоны из книги Мартина Фаулера «Шаблоны корпоративных приложений»: сценарий транзакции (transaction script) — одна функция на одну операцию — и активная запись (active record) — объект, который сам себя сохраняет в таблицу. Вот весь «доменный код» Каталога, и он нормален:
from dataclasses import dataclass
@dataclassclass CourseCard: """Карточка курса в Каталоге: только данные для витрины, правил нет.""" course_id: str title: str description: str price_kopecks: int is_published: bool = False
catalog = {}
def publish_course(card): """Опубликовать курс на витрине — обычная операция «создать или изменить».""" card.is_published = True catalog[card.course_id] = card
publish_course(CourseCard("python-analytics", "Python для аналитиков", "От нуля до pandas", 3_000_000))print([card.title for card in catalog.values() if card.is_published])['Python для аналитиков']Тратить на Каталог агрегаты и доменные события значит отнимать время у Набора. А попытка сделать Набор таким же «простым CRUD» возвращает нас к кому грязи из урока DDD.1.1.
Вспомогательную часть можно отдать подрядчику, но с одной оговоркой из материалов SAP: автотесты на то, что именно нужно бизнесу, полезно писать основной команде. Тогда реализацию можно проверить, а позже — переписать, не боясь сломать поведение.
Выделение ядра
Заголовок раздела «Выделение ядра»В «синей книге» Эванса стратегической части посвящена глава о дистилляциивыделение ядра (дистилляция)EN distillationРаздел книги Эванса о том, как отделить основной поддомен от всего остального, чтобы он был виден и получал лучшие силы: вынести типовое, написать описание ядра, выделить ядро в коде и документах. — выделении ядра. Как при перегонке отделяют ценную фракцию от остального, так и здесь основной поддомен отделяют от всего, что ему мешает, чтобы он был виден и получал лучшие силы. Главные приёмы:
- Ядро (core domain). Сделать основной поддомен маленьким и заметным: явно сказать, что входит в ядро, и поставить на него сильнейших людей.
- Типовые поддомены (generic subdomains). Вынести всё типовое в отдельные модули, чтобы оно не загромождало ядро; по возможности взять готовое.
- Описание ядра (domain vision statement). Короткий документ о ядре и его ценности — о нём ниже.
- Выделенное ядро (highlighted core). Сделать ядро видимым в документах и коде: короткий документ об основных элементах ядра и их связях или явные пометки в модели.
Дальше в книге идут более тонкие приёмы — связные механизмы, обособленное ядро, абстрактное ядро. Они нужны в больших системах; если захотите углубиться, начните со справочника Эванса (Domain-Driven Design Reference).
Описание основного поддомена на одну страницу
Заголовок раздела «Описание основного поддомена на одну страницу»Описание основного поддоменаописание основного поддомена (domain vision statement)EN domain vision statementКороткий документ — около страницы — о том, что составляет ядро системы, какую ценность оно даёт бизнесу и что в ядро не входит. Шаблон Эванса из раздела о выделении ядра. (domain vision statement) — документ примерно на страницу: что составляет ядро, какую ценность оно даёт бизнесу, какие правила в нём главные и что в ядро не входит. Его пишут рано, показывают и разработчикам, и руководству, и пересматривают, когда понимание растёт. Вот такое описание для Набора:
# Ядро: Набор онлайн-школы
**Зачем.** Школа учит потоками с ограниченным числом мест: на поток приходитсяфиксированное число наставников. Выручка и репутация школы зависят от того, насколькополно и честно заполняются потоки. Набор отвечает за то, чтобы каждое место было занятотем, кто действительно учится, а студент понимал, что с его записью и деньгами.
**Что входит в ядро.**- Запись студента на поток: вместимость, запрет двойной записи, закрытие записи при старте.- Жизненный цикл записи: ожидает оплаты → подтверждена; отмена в любом статусе.- Неоплаченная запись отменяется через 48 часов; место освобождается.- Лист ожидания: освободившееся место — первому в очереди, у него 24 часа.- Возврат при отмене: за 7 суток и более до старта — полный, позже — без возврата.
**Чем мы отличаемся.** Поток не стартует полупустым из-за «мёртвых» записей:неоплаченные места быстро возвращаются в оборот, лист ожидания работает автоматическии прозрачно для студента.
**Что НЕ входит в ядро.** Приём платежей (покупаем: платёжный сервис), отправка писем(покупаем: сервис рассылок), описания курсов и витрина (Каталог), выдача сертификатов.
**Как поймём, что ядро работает.** Доля мест, занятых к старту; среднее время, за котороеосвободившееся место находит нового студента; число обращений в поддержку о записях.
Числа 48 часов, 24 часа и 7 суток — условия учебного примера.Такая страница отвечает на вопрос «что здесь главное» быстрее любой архитектурной схемы. Она пригодится и при найме, и на ревью («это изменение про ядро?»), и как контекст для ИИ-агента.
Решения «делать или купить»
Заголовок раздела «Решения «делать или купить»»Тип поддомена подсказывает решение «делать или купить»«делать или купить»EN build vs buyРешение для каждой части системы: писать самим, купить готовый сервис, взять открытое решение или отдать на аутсорс. В DDD его подсказывает тип поддомена.. Вариантов на деле четыре: сделать самим, купить готовый сервис, взять открытое решение, отдать на сторону. Процесс DDD Crew так и формулирует цель шага «Strategize»: понимать, какие поддомены основные, чтобы обоснованно решать «делать, покупать или отдавать».
| Поддомен | Решение | Что пишем сами |
|---|---|---|
| Набор | делаем сами | богатую модель: поток, запись, лист ожидания, правила и события (модуль DDD.3) |
| Обучение | делаем сами | модель заданий, проверки и прогресса — вместе с методистами |
| Каталог | делаем просто или берём готовую систему управления контентом | формы, витрину, публикацию курсов |
| Сертификация | делаем просто | правило «кому выдать» и шаблон; документ генерирует готовая библиотека |
| Оплата | покупаем платёжный сервис | адаптер к нему и предохранительный слой, который переводит чужие статусы и поля на язык школы (урок DDD.2.2); Счёт и Платёж — только в нужном школе объёме |
| Уведомления | покупаем сервис рассылок или берём открытую библиотеку | шаблоны писем и подписку на события школы |
У покупки есть цена: зависимость от поставщика. Если модель чужой платёжной системы расползётся по коду школы, сменить поставщика будет так же трудно, как переписать половину системы. Поэтому чужое подключают через тонкий слой перевода — тогда смена поставщика затрагивает только его. Как этот слой устроен, разберём в уроке DDD.2.2.
Отдельный риск — сделать типовое самим «потому что интересно». Собственная система рассылок с очередями и повторами — прекрасная инженерная задача, которая не приведёт в школу ни одного студента.
Практика
Заголовок раздела «Практика»Канва поддоменов в коде
Заголовок раздела «Канва поддоменов в коде»Перенесём разговор о поддоменах в маленькую программу. Каждому поддомену команда вместе с экспертами ставит оценки от 1 до 5: дифференциация, сложность модели, критичность при сбое и есть ли готовое решение на рынке. Классификация — по осям Core Domain Chart:
# Оценки 1–5 — итог разговора команды с экспертами, а не измерение.# Поддомен: (дифференциация, сложность модели, критичность при сбое, есть ли готовое решение на рынке)SUBDOMAINS = { "Каталог": (2, 1, 3, True), "Набор": (4, 4, 5, False), "Оплата": (1, 5, 5, True), "Обучение": (5, 4, 4, False), "Сертификация": (2, 2, 2, True), "Уведомления": (1, 3, 3, True),}
STRATEGY = { "core": "делаем сами, лучшими людьми; богатая модель и тесты на каждое правило", "generic": "покупаем или берём готовое; свой код — тонкий адаптер", "supporting": "делаем просто (CRUD, конструктор) или отдаём на сторону",}
def classify(differentiation, complexity, has_market_solution): """Тип поддомена по осям канвы Core Domain Chart: дифференциация и сложность модели.""" if differentiation >= 4: return "core" # этим школа отличается от конкурентов if complexity >= 3 and has_market_solution: return "generic" # сложно, но все решают одинаково — есть готовое return "supporting" # нужно, но просто и без преимущества
print(f"{'Поддомен':13} {'Дифф.':>5} {'Слож.':>5} {'Крит.':>5} Тип Что делаем")for name, (diff, compl, crit, market) in sorted(SUBDOMAINS.items(), key=lambda kv: -kv[1][0]): kind = classify(diff, compl, market) print(f"{name:13} {diff:>5} {compl:>5} {crit:>5} {kind:11} {STRATEGY[kind]}")
critical_but_generic = [n for n, (d, c, k, m) in SUBDOMAINS.items() if k == 5 and classify(d, c, m) != "core"]print("\nКритичны (5), но не core:", critical_but_generic)Поддомен Дифф. Слож. Крит. Тип Что делаемОбучение 5 4 4 core делаем сами, лучшими людьми; богатая модель и тесты на каждое правилоНабор 4 4 5 core делаем сами, лучшими людьми; богатая модель и тесты на каждое правилоКаталог 2 1 3 supporting делаем просто (CRUD, конструктор) или отдаём на сторонуСертификация 2 2 2 supporting делаем просто (CRUD, конструктор) или отдаём на сторонуОплата 1 5 5 generic покупаем или берём готовое; свой код — тонкий адаптерУведомления 1 3 3 generic покупаем или берём готовое; свой код — тонкий адаптер
Критичны (5), но не core: ['Оплата']Обратите внимание на последнюю строку: критичность в классификации не участвует вовсе. Оплата получила высший балл
по критичности и всё равно осталась типовой — её нужно сделать надёжно, но покупкой и аккуратным адаптером.
Сортировка key=lambda kv: -kv[1][0] упорядочивает поддомены по убыванию дифференциации: основные — сверху.
Попробуйте поменять оценки и посмотрите, как меняется решение. Поставьте Сертификации дифференциацию 4 — так будет,
если школа решит конкурировать проверяемыми сертификатами, — и она переедет в core. Поставьте Уведомлениям
has_market_solution=False — и они станут вспомогательными: сложно, но придётся делать самим. Пороги 4 и 3 условны;
в жизни важнее не числа, а спор о них.
Упражнения
Заголовок раздела «Упражнения»1. Описание второго ядра (20 минут). По образцу описания Набора напишите domain vision statement для Обучения: зачем оно школе, что входит в ядро, чем школа отличается, что не входит, как понять, что ядро работает. Не больше страницы. Если не знаете, как в школе устроены задания и прогресс, — запишите это как открытые вопросы к методисту: это тоже результат.
2. Чужой домен (15 минут). Сервис доставки еды: каталог ресторанов и блюд, приём заказов, оплата, подбор и маршрутизация курьеров, отслеживание заказа на карте, отзывы, уведомления, бухгалтерия с ресторанами. Классифицируйте каждую часть и решите «делать или купить». Потом сверьтесь с разбором ниже.
Возможный разбор
Основные: подбор и маршрутизация курьеров (от них зависят скорость доставки и стоимость — главное отличие сервиса), возможно — приём заказов, если в нём хитрые правила объединения заказов и окон доставки. Вспомогательные: каталог ресторанов и блюд, отзывы, отслеживание на карте (сама карта — покупная, логика показа простая), расчёты с ресторанами, если договоры нестандартные. Типовые: оплата, уведомления, сами карты и геокодирование, бухгалтерский учёт. Спорные места — нормально: например, отзывы станут основным поддоменом, если сервис конкурирует честными рейтингами. Главное — уметь объяснить, почему вы так решили.
Проверь себя
Заголовок раздела «Проверь себя»- Предметная область делится на поддомены — части со своими задачами, экспертами и словами. Поддомены обнаруживают, изучая бизнес; границы моделей в коде, ограниченные контексты, проектируют отдельно.
- Три типа: основной (core) — отличие от конкурентов, сложен и изменчив, делаем сами лучшими силами; вспомогательный (supporting) — нужен, но прост, делаем просто или отдаём; типовой (generic) — решён всеми, покупаем. Критичность — не то же самое, что «основной».
- Поддомены находят по возможностям бизнеса, экспертам, языку, событиям и связности и классифицируют по двум осям Core Domain Chart — дифференциации и сложности. Самое ценное — разговор о спорных оценках.
- В онлайн-школе Набор и Обучение — core, Каталог и Сертификация — supporting, Оплата и Уведомления — generic. Отсюда решения «делать или купить»: ядро делаем сами, вспомогательное — просто, типовое покупаем и подключаем через слой перевода.
- Выделение ядра по Эвансу делает основной поддомен маленьким и видимым; описание ядра на одну страницу отвечает на вопрос «что здесь главное» для команды, руководства и ИИ-агента.
Дальше: урок DDD.2.1 — ограниченные контексты. Одно слово «курс» значит разное для маркетинга, учебной части и бухгалтерии; разберёмся, где проходят границы моделей и как разбить онлайн-школу на контексты.