По традиции Disclaimer:
Часто мы сталкиваемся с бездумным применением или стремлением в какой-то фреймворк без должного понимания рисков и проблем связанных с их реализацией, а ведь архитектор - это человек, у которого горизонт планирования часто измеряется годами.
И данная статья - попытка критически взглянуть на Data-driven подход, на коррупцию в нем и проблемы, которые он может породить на длинной дистанции.
Data-driven подход к архитектуре не «плох сам по себе», но у него есть системные ограничения, которые важно понимать. Основная проблема: архитектура начинает подстраиваться под данные, а не под бизнес-модель, домены и потоки изменений. Это приводит к ряду негативных эффектов.
На этом можно и закончить, но давайте разберем поподробнее.
Архитектура наследует структуру данных, а не структуру бизнеса
Чему нас учат всякие DDD, BIAMы и ETOMы с Тогафами? Мы автоматизируем бизнес и насколько близко к бизнес правилам мы можем привести наши паттерны в виде границ доменов, продуктов, доменных инвариантов, потоков событий - тем лучше. Подход на основе данных по определению чертит границы ландшафта по принципу "где лежат данные".
Если такое вот случилось, то итогом является очевидная коррупция архитектуры, строительные блоки начинают с каждой следующей итерацией проектирования отдалятся от оптимальной, а потом и от реальной структуры бизнеса. Далее появляются дополнительные слои, теряется общий язык и начинает существенно снижаться ТТМ.
Как понять, что этот анти-патерн реализовался и мы идем не туда? Описание сервисом становится похоже на "таблички" (UserService, ProductService, AccountService), с размытыми или полностью отсутствующими границами.
Сильная связанность через общие данные.
И сразу скажу, что это далеко не всегда плохо, но сильно не однозначно и рискованно. Если бизнес у вас строится вокруг одних сущностный, то общие наборы не есть антипаттерн, как пример - транспортная компания, в которой главное груз. И выравнивание через атрибутную и событийные модели положительно скажется на бизнес-функциях и качестве их реализации. Но вспомнив самый дискуссионный элемент DDD - agrigate root можно легко представить в какой момент у вас может реализоваться другой аннртипатерн - big model и к чему это в итоге приведет.
Но есть и риски:
- Сервисы начинают конкурировать за доступ к одной модели данных, здесь речь, прежде всего о приоритетах в развитии модели.
- Контракты между системами растут наполняясь лишнми атрибутами.
- Расширяются транзакционные границы
- Появляется хрупкость при любом изменении схемы.
Вместо слабой связанности доменов получаем общую мутабельную модель, привязывающую всех.
Блокировка эволюции (schema-first -> architecture-stuck)
Одно из самых коварных последствий data-driven архитектуры проявляется в момент, когда бизнес требует изменений. В идеальном мире доменная модель эволюционирует вместе с бизнесом, а технические артефакты следуют за ней. Однако когда в центре архитектуры находится схема данных, любое её изменение превращается в точку каскадного воздействия на весь ландшафт.
Представьте типичную ситуацию: бизнес решил, что "Клиент" теперь может быть не только физическим лицом, но и группой лиц с общим бюджетом. В доменно-ориентированной архитектуре это изменение локализовано в соответствующем bounded context, и команда может реализовать его относительно автономно. В data-driven архитектуре изменение таблицы Customer немедленно затрагивает десятки сервисов, которые завязаны на её структуру. Каждый из них требует ревизии, тестирования, и что самое неприятное - синхронного релиза.
Со временем команды начинают избегать изменений схемы, предпочитая "обходные" решения: добавление nullable-полей "на всякий случай", создание параллельных структур, введение JSON-полей для "гибкости". Схема превращается в археологический слой, где каждое поле несет в себе историю компромиссов и страха перед миграциями. Архитектура теряет способность к эволюции не из-за технических ограничений, а из-за накопленной сложности координации изменений.
Избыточная нормализация и "реляционное мышление".
Data-driven подход почти неизбежно приводит к тому, что архитекторы и разработчики начинают мыслить категориями реляционной модели данных. Это не случайно - если данные находятся в центре всего, то оптимизация их хранения становится приоритетом. Третья нормальная форма, минимизация дублирования, референциальная целостность - всё это прекрасные инструменты проектирования баз данных, но они не являются инструментами проектирования бизнес-систем.
Когда доменный инвариант "Заказ не может быть отгружен без оплаты" превращается в CHECK constraint или триггер в базе данных, происходит фундаментальный сдвиг ответственности. Бизнес-правило теперь живет не в коде домена, где его можно версионировать, тестировать и обсуждать с бизнес-экспертами, а в недрах СУБД, где оно становится техническим артефактом, понятным только DBA.
Избыточная нормализация создает ещё одну проблему - она оптимизирует модель под операции записи и под минимизацию дискового пространства, но часто делает это в ущерб операциям чтения и в ущерб концептуальной целостности. Простой вопрос "покажи мне полную информацию о клиенте" превращается в JOIN через пять-семь таблиц, каждая из которых была создана по соображениям нормализации, а не по соображениям бизнес-смысла. Разработчики тратят время на понимание физической структуры данных вместо понимания бизнес-домена.
Неправильные границы микросервисов
Микросервисная архитектура обещала нам независимость развертывания, автономию команд и возможность масштабирования отдельных частей системы. Однако data-driven подход систематически разрушает эти обещания, потому что он предлагает неверный критерий декомпозиции.
Когда границы сервисов проводятся по принципу "один сервис - одна таблица" или "один сервис - одна сущность", мы получаем не микросервисы, а распределенный монолит с сетевыми вызовами вместо локальных. UserService, ProductService, OrderService - эти названия звучат знакомо, и они являются красным флагом. За ними обычно скрываются тонкие CRUD-обертки над таблицами базы данных, которые не содержат никакой бизнес-логики и не реализуют никаких доменных инвариантов.
Настоящий bounded context определяется не тем, какие данные он хранит, а тем, какие бизнес-возможности он предоставляет и какой язык он использует. Контекст "Управление складом" может работать с теми же физическими товарами, что и контекст "Продажи", но он видит их по-разному, использует другую терминологию и решает другие бизнес-задачи. Data-driven подход не способен уловить эту разницу, потому что он смотрит на данные, а не на поведение и не на смысл.
Результатом становится архитектура, в которой простая бизнес-операция "оформить заказ" требует синхронной координации десяти сервисов, каждый из которых отвечает за свой кусочек данных. Вместо обещанной автономии мы получаем распределенную транзакцию, а вместо устойчивости - хрупкую цепочку, где отказ любого звена блокирует всю операцию.
Слабая поддержка автономии поставки
DevOps-культура и практики непрерывной доставки строятся на фундаментальном принципе: команды должны иметь возможность развертывать свои изменения независимо друг от друга. Это требует слабой связанности между компонентами системы и четких контрактов взаимодействия. Data-driven архитектура подрывает оба этих требования.
Когда несколько сервисов зависят от общей схемы данных, изменение этой схемы автоматически требует координированного релиза. Появляется необходимость в "релизных поездах", где изменения накапливаются и выкатываются пакетами. Частота релизов падает, размер изменений в каждом релизе растет, а вместе с ним растет и риск. Команды теряют автономию и начинают зависеть от расписания других команд.
Контракты между сервисами в data-driven архитектуре имеют тенденцию к разрастанию. Если API спроектировано вокруг структуры данных, а не вокруг бизнес-операций, то каждое новое требование приводит к добавлению новых полей. Со временем контракт превращается в "универсальный" DTO, который содержит все возможные атрибуты сущности, большинство из которых nullable и неактуальны для конкретного потребителя. Версионирование такого API становится кошмаром, а обратная совместимость - постоянным источником технического долга.
Схема данных становится Source of Truth вместо доменной модели
Возможно, самая глубокая проблема data-driven подхода заключается в том, что он смещает фокус внимания всей организации. Когда схема данных становится источником истины, команды начинают обсуждать таблицы и поля вместо бизнес-процессов и правил. ERD-диаграмма занимает центральное место на архитектурных встречах, а вопрос "какие данные нам нужны" вытесняет вопрос "какую проблему мы решаем".
Это смещение фокуса имеет культурные последствия. Новые разработчики онбордятся через изучение схемы базы данных, а не через погружение в бизнес-домен. Архитектурные решения аргументируются удобством работы с данными, а не соответствием бизнес-модели. Технические метрики вроде "количество таблиц" или "время выполнения запроса" начинают доминировать над бизнес-метриками вроде "время вывода фичи на рынок" или "стоимость изменения бизнес-правила".
В долгосрочной перспективе это приводит к разрыву между IT и бизнесом. Бизнес-эксперты перестают понимать, как устроена система, потому что её описание ведется на языке данных, а не на языке бизнеса. Разработчики теряют контекст и начинают принимать решения, которые оптимальны с точки зрения данных, но неоптимальны с точки зрения бизнеса. Ubiquitous Language из DDD остается недостижимой мечтой, потому что у данных нет языка - у них есть только структура.
И все же он хорош.
Не хотел бы, чтобы моя статья состояла чуть менее чем полностью только из критики.
После столь обширной критики важно восстановить баланс и признать, что data-driven подход не является универсальным злом. Как и любой архитектурный инструмент, он имеет свою область применения, где его преимущества перевешивают недостатки.
Прежде всего, data-driven подход естественен для систем, где данные действительно являются главным продуктом. Аналитические платформы, хранилища данных, системы отчетности - все они существуют ради данных и для данных. Проектировать DWH по принципам DDD было бы не просто избыточно, а контрпродуктивно. Здесь схема данных и есть доменная модель, и оптимизация вокруг неё полностью оправдана.
Data-driven подход также хорошо работает в ситуациях с высокой неопределенностью относительно бизнес-модели. Когда стартап ещё не понимает, какой именно продукт он строит, попытки выделить bounded contexts и агрегаты может оказаться преждевременной оптимизацией. Простая модель данных с CRUD-операциями позволяет быстро экспериментировать и получать обратную связь от рынка. Рефакторинг в сторону более зрелой архитектуры можно провести позже, когда бизнес-модель стабилизируется.
Ещё одна область, где data-driven подход оправдан - это интеграционные слои и ETL-процессы. Когда задача состоит в том, чтобы переместить данные из точки А в точку Б с минимальными трансформациями, фокус на структуре данных является естественным и правильным. Пытаться навязать доменную модель процессу, который по своей природе является техническим, означает добавлять ненужную сложность.
Наконец, data-driven подход может быть разумным выбором для небольших систем с ограниченным сроком жизни. Если вы строите внутренний инструмент для команды из десяти человек, который будет использоваться год-два, инвестиции в сложную доменную архитектуру могут не окупиться. Простота имеет свою ценность, и иногда "таблички с CRUD" - это именно то, что нужно.
Ключевой вывод состоит не в том, что data-driven подход плох, а в том, что его применение должно быть осознанным выбором, а не случайным следствием. Архитектор должен понимать, какие компромиссы он принимает, и быть готовым к последствиям этих компромиссов. Data-driven подход - это инструмент в арсенале, а не единственный способ строить системы.