Дерево, Лес или DAG? Как мы строили модель данных для цифрового офиса сотрудника

Предметное поле банковского приложения для сотрудников: как его визуализировать — как лес, дерево или граф? Я Валерий Разномазов, тимлид команды системных аналитиков проекта «Цифровой офис сотрудника», в рамках которого мы создаем приложение для сотрудников Россельхозбанка. В этой статье я рассказываю, как мы строили модель данных для приложения «Цифровой офис сотрудника» в РСХБ и почему отказались от классического «дерева» в пользу ориентированного ациклического графа (Directed Acyclic Graph, DAG).

Вы узнаете, почему сложность ролевой модели энтерпрайза и HR-tech не позволяет использовать привычную лесную структуру предметного поля, почему выгоднее, чтобы один объект принадлежал нескольким ролям и как математически формализовать жизненный цикл бизнес-объектов — создание, проверку и удаление.
Особое внимание в статье уделяем новым метрикам значимости бизнес-объектов: мы ввели значимость (сколько связей у объекта с другими объектами) и навигационную глубину (сколько кликов нужно до функционала). Это позволило не только ранжировать объекты по важности, но и связать математику с проектированием интерфейсов: чем объект значимее, тем ближе к поверхности он должен находиться.
Статья будет полезна архитекторам, бизнес- и системным аналитикам и всем, кто строит модели данных в крупных компаниях с развитой ролевой моделью.
Граф как таксономия
Пять лет назад наша команда поставила цель — создать современное приложение для сотрудников банка. Вот раньше, все крупные банки имели корпоративные порталы с личным кабинетов сотрудника. Мы же решили заменить такой портал полноценным приложением, в рамках которого можно не только читать корпоративные новости и отслеживать количество корпоративных псевдоплатежных единиц на счету, но и иметь полноценный органайзер, в котором в удобном порядке имеются функции коммуникации с командой, сервис заказа справок, отслеживания отпусков, распределения рабочих задач и прочее, прочее, прочее, без чего работнику в современном банке трудно.
Такая задача требовала принципиально новых подходов в аналитике и в работе с коллекциями артефактов. Для любого, кто работал в банках, очевидно: чем крупнее компания, тем острее потребность в построении, поддержании и формализации модели данных, понятной всем участникам. Именно поэтому объектно-ориентированный подход уверенно вытесняет функциональный. Функциональный подход требует описания каждого бизнес-процесса, но в крупной социотехнической компании новые процессы возникают каждый день — угнаться за ними невозможно.
Зачем вообще визуализировать предметное поле? Ответ: чтобы управлять сложностью. В энтерпрайзе сотни объектов, тысячи связей и десятки ролей — без общей схемы всё это остаётся в головах аналитиков. Визуализация делает неявное явным, обнаруживает пробелы и дублирования, служит мостом между бизнесом и IT, позволяет измерять значимость объектов и делает изменения управляемыми. Граф — это не просто модель данных, а инструмент коллективного понимания и архитектурного контроля.
Вся статья построена на одной методологии: перед нами граф, где узлы — это бизнес-сущности, а рёбра — связи, которые их порождают.
Теория графов не новость. Её изучают на любом техническом факультете, и инженеры часто обращаются к ней в мысленных экспериментах. Но современный финтех и HR-tech — это среда, где далеко не все имеют профильное образование. Поэтому для многих станет открытием, что классические подходы к описанию бизнес-процессов, например, методология ARIS Августа-Вильгельма Шеера или нотация BPMN по своей сути являются графовыми моделями.
Мы предлагаем взглянуть на граф иначе. Не как на способ описания «движения по маршруту» (как у Шеера), а как на таксономию — подобно тому, как Карл Линней классифицировал животный мир. Мы покажем, как таксономический подход к графу помог нам построить модель данных, где бизнес-объекты имеют чёткое место в иерархии, а роли пользователей становятся корнями этой классификации. Расскажем, почему классическое «дерево» и тем более «лес» не справляются с этой задачей, и как мы пришли к ориентированному ациклическому графу (DAG). А также о том, как этот подход породил метрики значимости бизнес-объектов, позволяющие проектировать интерфейсы, где самое важное всегда наиболее доступно для пользователя.
Граф предметного поля приложения для сотрудника банка: лес или дерево?
Когда мы начали проектировать модель данных, ответ казался очевидным: дерево. Пользователь в корне, а всё остальное — ветви. Но энтерпрайз — это всегда про роли. А ролей много. Значит, деревьев должно быть много? Лес?

Лес — это красивая метафора, но она рассыпается при первом же соприкосновении с реальностью. Ведь менеджер, сотрудник и администратор сервиса работают с одной и той же заявкой. Зачем плодить копии? Почему бы не сделать одну заявку, доступную из разных ролей одного пользователя? Так мы пришли к DAG — ориентированному ациклическому графу. В нём бизнес-объект существует в единственном экземпляре, а роли пользователя становятся корнями, из которых он достижим. Мы сознательно отказались от правила единственного родителя и сделали ставку на множественное наследование.

Предлагаем читателям посмотреть на предметное поле сквозь такие свойства DAG как топологическая сортировка, множественное наследование и конечность путей.
Топологическая сортировка
Одна из ключевых задач энтерпрайза — определить порядок жизненного цикла объектов: создание, валидация, удаление. Один объект порождает другой и данный факт необходимо учесть. В графовой модели данная задача решается через топологическое упорядочивание: ребро (A → B) означает, что A обрабатывается раньше B.
Возьмём «заявку» и «задачу». Рядовой пользователь создаёт заявку, уполномоченный сотрудник работает с задачей. В лесу они были бы изолированы — каждый в своём микросервисе. DAG вскрыл связь: заявка порождает задачу. Так родился единый конвейер с фабрикой задач, где REST-взаимодействия следуют порядку, заданному графом.
А теперь — удаление. В лесу связь между заявкой и задачей не зафиксирована. Удаляем заявку — задача остаётся «сиротой». Для бизнеса это потеря истории, ручная чистка базы и риск обработки по неактуальной заявке. В DAG зависимость зафиксирована ребром: удаление заявки влечёт каскадное удаление задачи или перевод её в «отменена». Система сама управляет целостностью, а не надеется на внимательность сотрудника.
Проверка строится вдоль рёбер: если заявка невалидна, задача не создаётся. Если заявка удалена, задача теряет валидность. Граф становится не только моделью данных, но и моделью бизнес-правил.
Но самое интересное случилось, когда мы нанесли на граф все объекты. Оказалось, что некоторые узлы не имеют корня — не принадлежат ни одной роли. Часть объектов относилась к HR-процессам и была неинтересна рядовому сотруднику. А рекрутеры? Мы просто забыли выделить их в отдельную роль. В лесу этот пробел остался бы незамеченным — каждый микросервис работал бы со своим фрагментом. DAG подсветил проблему: узел без корня — архитектурный долг. Мы ввели роль «рекрутер» и зафиксировали её как корень. Граф перестал быть просто схемой, он стал инструментом рефлексии над архитектурой.
Пять принципов бизнес-анализа, которые позволил сформулировать DAG:
Принцип единственности: объект существует в единственном экземпляре.
Принцип достижимости: каждый объект достижим из хотя бы одной роли.
Принцип порядка: создание объектов идёт от корней к листьям.
Принцип контекстной валидации: объект валиден только в контексте своих предков.
Принцип эволюционной полноты: граф показывает пробелы, требующие новых ролей.
Топологическая сортировка добавляет ответственности. Если архитектура построена по принципам DDD и каждый микросервис соответствует одной бизнес-сущности, то ребро (A → B) означает: объект A, стоящий в графе раньше, управляет созданием объекта B. На практике это либо REST-вызов, либо вызов фабрики на стороне A. Так граф задаёт не только порядок, но и распределение ответственности между сервисами.
А теперь — про нагрузку. Чем больше исходящих рёбер у A, тем чаще к нему обращаются. Ведь A — родитель: он «рождает» B, C, D. Каждое ребро — это вызов. Чем больше рёбер, тем больше вызовов. Так A становится «горячей точкой» системы. Если вы видите в графе объект с десятью исходящими рёбрами — знайте: именно он, скорее всего, будет тормозить в час пик. Кэширование, балансировка, отдельный сервис — вот что ему нужно.

Множественное наследование и рождение метрик значимости
В дереве у каждого узла один родитель. В нашей ролевой модели это означало бы: каждый объект принадлежит одной роли или одному бизнес-объекту. Но «задача» нужна и рядовому сотруднику, и уполномоченному сотруднику, и владельцу сервиса. Лесная модель заставила бы либо дублировать задачу в трёх деревьях, либо выбрать одного «владельца» — и лишить остальных контекста. Аналогичная ситуация с «задачей» Задача может порождаться не только «заявкой», но и таким объектом как «идея по улучшению».
Решение — множественное наследование: объект может иметь несколько родителей, оставаясь единственным экземпляром для всех. При этом изменение его состояния мгновенно видно всем родителям. И сам дочерний объект наследует свойства всех родителей, обеспечивая тем самым контекст.
А потом случилось неожиданное. Мы рисовали предметное поле в draw.io, и к некоторым объектам сходилось слишком много рёбер. Сначала это раздражало: линии сливались, приходилось их разводить. А потом пришла постмодернисткая ассоциация. Вспомним силовые линии Фарадея и электростатику. Чем гуще линии, тем больше потенциал поля. Так и здесь: чем больше рёбер сходится к объекту, тем он значимее в предметном поле. Мы ввели метрику ρ — количество бизнес-объектов и бизнес-субъектов, для которых достижим данный объект. И если пытаться оценивать количественно данную метрику, то такие объекты как «Заявка» или «Справка» окажутся на вершине: они доступны для многих ролей и достижимы из множества объектов. Более того, они же породят множество объектов следующего поколения, такие как «задача» или «уведомление о смене статуса». А вот такой объект как «Балльная мотивация» для кассиров является специфическим для одной лишь роли. Таким образом метрика ρ позволяет ранжировать объекты, приоритизировать изменения и находить несоответствия в реализации концепции интерфейса — то, что казалось незначительным, вдруг должно оказывается в центре внимания пользователя.
Таким образом граф перестал быть схемой. Он стал силовым полем архитектуры. Давайте взглянем на общее количество связей (рёбер) объекта в контексте топологической сортировки.
Пусть,
δ(v) = i(v) + j(v)
Где:
i(v) — количество исходящих рёбер (объект порождает или ссылается на другие).
j(v) — количество входящих рёбер (другие объекты ссылаются на данный).
Что такая формализация показывает? Если попытаться интерпретировать полученную формализацию, то можно прийти к выводу, насколько объект интегрирован в модель данных и влияет на другие бизнес-объекты.
Интерпретация тут в самом общем случае может быть следующей:
Высокая δ (> среднего по графу) — структурный центр, «узел» модели данных. Если мы создаем этот объект в рамках микросервиса, то на данный микросервис будет повышенная нагрузка.
Низкая δ (≤ среднего) — периферийный объект, локальный. Надо подумать, нужно ли его выделять в отдельный микросервис.
А если вспомнить топологическую сортировку, то:
Если i ≫j — объект является источником (порождает много других).
Если j ≫ i — объект является агрегатором (на него всё ссылается).
Оптимизировать инфраструктурные компоненты, отвечающие за данные бизнес-объекты нужно, стало быть, по-разному: источник — по логике порождения, агрегатор — по скорости доступа к данным.
Неожиданные выводы для UX/UI
Любой дизайнер скажет: чем важнее объект, тем меньше кликов до его функционала. Чем менее важен, тем глубже его можно спрятать. Это интуитивно.
Но давайте измерим эту интуицию. Введём навигационную глубину D(v) — длину кратчайшего пути от корня-роли до объекта.
И сформулируем принцип: значимость объекта обратно пропорциональна глубине доступа:
δ(v) ∝ 1 / D(v).
Что будет, если этот принцип нарушить? Важный функционал останется незамеченным. Знакомая история: релизишь важную для бизнеса фичу, а DAU не растёт. Потому что фича неочевидна, спрятана глубоко, и пользователь до неё просто не доходит. Математика здесь не абстракция. Это инструмент, который помогает не потерять важное в глубине навигации.
Или, строже, можно вывести принцип обратной пропорциональности или Закон рациональной эргономики интерфейса:
δ(v) * D(v) ≈ const
То есть:
Если δ(v) высокая → D(v) должна быть низкой (мало кликов).
Если δ(v) низкая → D(v) может быть высокой (много кликов).
Если топологическая сортировка задаёт порядок создания, то глубина доступа задаёт порядок показа в интерфейсе. Они связаны, но не идентичны:
Топологическая сортировка → от корней к листьям (технический порядок).
Навигационная глубина → от важного к второстепенному (пользовательский порядок).
В идеале эти два порядка должны совпадать или быть максимально близкими. Если объект создаётся раньше остальных, но в интерфейсе спрятан глубоко (D = 5) — это сигнал к пересмотру концепции UI/UX.
Конечность путей
Ацикличность DAG означает не только отсутствие циклов. Её важнейшее следствие — конечность всех путей: от любого корня до любого узла существует конечное число рёбер. Покажем это на примере сервисов «Заявки» и «Справки».
«Заявка» порождает «Справку» (нужна справка о доходах). «Справка» порождает «Задачу» (уполномоченный сотрудник проверяет справку). «Задача» порождает «Уведомление». Путь: Заявка → Задача →Справка → Уведомление. Длина = 3.
Конечность путей гарантирует, что этот путь закончится. Создание «Уведомления» не потребует новой «Заявки». Цепочка также не замкнётся. Если бы «Уведомление» ссылалось обратно на «Заявку» (например, «создать новую заявку на основе уведомления»), мы получили бы цикл — и создание заявки никогда бы не завершилось.
С конечностью путей мы получаем: гарантию завершимости (любая операция ≤ L шагов), предсказуемое время обработки (SLA = L × t), ограничение рекурсии в SQL и ORM (max_recursion_depth = L), полный аудит (цепочка восстановима до корня) и безопасное каскадное удаление (обход завершается за L шагов).
Формально существует константа L = max path_length (r, v), ограничивающая глубину любого пути. В нашем примере с заявкой и справкой L = 3. Любая операция завершается за число шагов ≤ L. Время процесса ≤ L × t. И конечность путей позволяет планировать SLA. Поскольку любой путь в DAG конечен, мы можем вычислить максимальную длину пути от роли до объекта (L_max) и, зная стоимость каждого шага, определить предельное время выполнения процесса. Это даёт бизнесу чёткие обещания: «задача появится не позднее чем через N секунд», «договор будет сформирован не позднее чем за M шагов». Для каждой роли можно рассчитать свой SLA. А отклонение от расчётного времени становится сигналом аномалии: система сама сообщает, что процесс идёт не по плану.
Лес или дерево?
Мы начали с вопроса «Лес или дерево?», а закончили методологией, которая переворачивает взгляд на моделирование данных в энтерпрайзе. Дерево не выдержало столкновения с ролевой моделью, а лес — с реальностью, где один объект нужен нескольким ролям. Ответом стал DAG — граф, в котором бизнес-объекты существуют в единственном экземпляре, а роли становятся корнями, из которых они достижимы.
Итак, граф решает три задачи жизненного цикла — создание, проверку и удаление. В нашем случае граф подсветил архитектурные пробелы, помог сформулировать новую метрику — метрику значимости и показал, как конечность путей позволяет планировать SLA.
Если вы дочитали до этих строк и задумались, как выглядит предметное поле вашего продукта, значит, статья достигла цели. Постройте граф, нанесите на него объекты и посмотрите, какие пробелы он подсветит. Уверен, вы увидите то, о чём не подозревали.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.