惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Blog — PlanetScale
Blog — PlanetScale
小众软件
小众软件
F
Fortinet All Blogs
博客园 - 叶小钗
博客园_首页
D
DataBreaches.Net
Apple Machine Learning Research
Apple Machine Learning Research
U
Unit 42
爱范儿
爱范儿
aimingoo的专栏
aimingoo的专栏
博客园 - Franky
Martin Fowler
Martin Fowler
酷 壳 – CoolShell
酷 壳 – CoolShell
The Cloudflare Blog
A
About on SuperTechFans
Google DeepMind News
Google DeepMind News
Microsoft Security Blog
Microsoft Security Blog
IT之家
IT之家
M
MIT News - Artificial intelligence
有赞技术团队
有赞技术团队
博客园 - 【当耐特】
S
SegmentFault 最新的问题
Hugging Face - Blog
Hugging Face - Blog

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания
Неизменяемая архитектура. Практическая проверка кодом. Пр...
VOrlyanskiy · 2026-05-04 · via Все публикации подряд на Хабре

Неизменяемая архитектура. Практическая проверка кодом. Проверка работы с бизнес-процессом

Уровень сложностиСредний

Время на прочтение5 мин

Охват и читатели3.2K

Сокращения

Книга

Вся данная работа делается ради проверок идей изложенных в книге «Искусство неизменяемой архитектуры: теория и практика управления данными в распределенных системах». Далее по тексту если указано слово «книга» и не названо ее имя, подразумевается книга «Искусство неизменяемой архитектуры: теория и практика управления данными в распределенных системах».

Ссылки на предыдущие части

В прошлых частях цикла

  1. Неизменяемая архитектура. Практическая проверка кодом. Версия 1, начало / Хабр.

  2. Неизменяемая архитектура. Практическая проверка кодом. Аутентификация / Хабр.

Были описаны технологии, используемые в этом примере (Spring Boot, Kotlin, H2, Python, FastApi). В первой статье был проверен самый простой пример обработки запросов на статус документа, описаны плюсы и минусы технологии неизменяемой архитектуры. Во второй части было описано решение для аутентификации с помощью закрытых/открытых ключей.

Введение

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

Для реализации выбран абстрактный бизнес-процесс запроса абстрактных ресурсов. Пример придуман максимально общим. Необходимо реализовать обработку бизнес-процесса с несколькими стадиями.

Цель

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

Описание процесса

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

Диаграмма процесса показана на рисунке ниже.

В процессе обработки бизнес-процесса будут создаваться внутренние документы в БД, описанные на рисунке ниже. Направление стрелок идет от подчиненного документа к инициирующему документу или, другими словами, документу предку. Так рекомендует книга.

В процессе обработки процесса он меняет свой статус. На рисунке ниже описаны статусы процесса.

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

В текущем решении статус процесса записывается в последний документ процесса. Последним документом процесса является документ, у которого нет других подчинённых документов. Т. е. последний документ указывает на статус всего процесса. Конечно, это сильное ограничение технологии: процесс с несколькими параллельными ветками обработки не сможет иметь чёткого статуса. Этот нюанс и то как его обходить описан в книге; в рамках данного цикла статей сделано допущение, что такое решение корректно.

Техническая реализация

Статусы/документы процессов хранятся в ResourceRequestDocument. Перечисленные выше статусы описаны в перечислении ResourceRequestStatus. Связь статусов делается через поле parentDocumentHash. Для обеспечения производительности для полей поиска созданы индексы.

Обработка процесса сделана в сервисе ResourceRequestServiceAsync. Эмуляция сделана на основе механизма сообщений, встроенного в Spring Boot. При получении первого сообщения делается отправка сообщения о необходимости обработки процессов. При обработке сообщения, если был выставлен не FINISHED статус, делается повторный вызов обработчика статусов.

Поиск не законченных процессов сделан через поиск документов без наследников, кроме документов в статусе FINISHED.

Чтобы приблизить тестовое решение к реальной жизни, для каждого этапа, где делается выбор предусмотрена возможность с 10%-ной вероятностью получить статус ошибки и закончить процесс, не пройдя полный цикл.

Пример начального запроса приложен в проекте. Поле "request-id" не обязательно должно быть, как в примере, датой - это может быть любая уникальная строка.

Для того чтобы получить информацию о всех этапах определённого бизнес-процесса, можно использовать SQL запрос с CTE. Пример можно найти в проекте. Возврат результатов SQL CTE в отформатированном виде выходит за рамки текущего цикла статей. При разработке обнаружено ограничение H2 базы данных, которое вынудило приводить к varchar поля сравнения, для построения иерархии.

Технологии тестирования

Для тестирования создан Python FastAPI-скрипт. Скрипт в 20 потоков через asyncio отправляет заданное количество запросов к серверу работы с бизнес-процессом.

Итоговое состояние базы данных после обработки всех процессов проверяется через скрипт поиска незакрытых процессов.

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

Результаты тестирования решения

Тестирование решения проводилось с нагрузкой по созданию 200 тысяч процессов при запуске и сервера, и тестирующего скрипта на одном компьютере Intel Core i5-10310U. 200 тысяч процессов были обработаны примерно за 4.5 минуты.

Результат удивил. Ожидалось, что при увеличении количества процессов в БД будет падать и время их обработки. Собственно, было интересно узнать, с какой скоростью падает производительность при росте количества процессов, которое в данной архитектуре - суть количество этапов или документов одного процесса. Однако результат получился обратный. Замедление есть на первых секундах работы. После чего приложение выходит на одну скорость обработки процесса. Т. е. скорость выборки данных из БД не изменяется при увеличении количества записей до примерно 1.2 миллиона записей о статусах процесса.

Я решил запустить тестирование на 400 тысячах записей. 400 тысяч процессов дали 2 465 830 записей в таблице статусов/документов. Обработка заняла примерно 9 минут Результат оказался таким же: замедление на старте и примерно одинаковая скорость работы дальше. Замедления в середине связаны с тем, что я делал запросы к БД в момент работы скрипта тестирования.

Результат для 400 тысяч процессов, в виде графика, показан на рисунке ниже.

Как видно на графике, в начале работы приложения скорость обработки одного процесса может составлять до 8 секунд, однако уже через несколько секунд скорость обработки одного процесса падает до менее чем 10 миллисекунд, после чего остаётся на этом уровне. Замедления происходят если обратиться к БД с каким-то «другим» запросом.

Анализ причин такого странного поведения выходит за рамки данного цикла. Предполагаю, что работает оптимизация в JVM и использование индекса для поиска данных в БД.

Выводы.

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

Решение «как обычно», скорей всего, имело бы 2 таблицы. Одну таблицу с описанием процесса и его статусом. И еще одну таблицу с историей. В обычном решении история, наверное, была бы похожа на таблицу RESOURCE_REQUEST_DOCUMENT из текущей реализации. Однако таблица со статусами постоянно обновлялась бы через update для отслеживания своего состояния.

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

Опасения, что из одной таблицы будет сложно получить полный, цикл также не подтвердились. Скрипт построения истории обработки бизнес-процесса получился не сложным, и понятным. Данный скрипт в H2 работает медленно, для таблицы RESOURCE_REQUEST_DOCUMENT с 2.4 млн. записей построение истории занимает примерно 14 секунд. Данное поведение связанно с фильтром cast(c.parent_document_hash as varchar) = p.json_hash, который не позволяет использовать индекс. Это ограничение БД H2.

Конечно, данное решение сделано на основе in-memory БД. При использовании более «классических» БД результат мог бы быть другим, но даже этот результат позволяет сказать, что использование технологий неизменяемой архитектуры, описанных в книге «Искусство неизменяемой архитектуры: теория и практика управления данными в распределенных системах» дает выигрыш в простоте и производительности.