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

推荐订阅源

博客园_首页
T
Threat Research - Cisco Blogs
GbyAI
GbyAI
Y
Y Combinator Blog
美团技术团队
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 【当耐特】
S
SegmentFault 最新的问题
IT之家
IT之家
Recent Announcements
Recent Announcements
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
阮一峰的网络日志
阮一峰的网络日志
T
The Blog of Author Tim Ferriss
Martin Fowler
Martin Fowler
Microsoft Azure Blog
Microsoft Azure Blog
V
Visual Studio Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
U
Unit 42
WordPress大学
WordPress大学
博客园 - Franky
L
LangChain Blog
人人都是产品经理
人人都是产品经理
小众软件
小众软件
博客园 - 叶小钗
罗磊的独立博客
酷 壳 – CoolShell
酷 壳 – CoolShell
大猫的无限游戏
大猫的无限游戏
云风的 BLOG
云风的 BLOG
Vercel News
Vercel News
雷峰网
雷峰网
腾讯CDC
Google DeepMind News
Google DeepMind News
博客园 - 三生石上(FineUI控件)
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Help Net Security
Help Net Security
C
Check Point Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
N
News and Events Feed by Topic
V2EX - 技术
V2EX - 技术
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Schneier on Security
Schneier on Security
博客园 - 聂微东
A
Arctic Wolf
H
Heimdal Security Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Recent Commits to openclaw:main
Recent Commits to openclaw:main
T
The Exploit Database - CXSecurity.com
C
Cyber Attacks, Cyber Crime and Cyber Security
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Google DeepMind News
Google DeepMind News

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

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет 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 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Неизменяемая архитектура. Практическая проверка кодом. Проверка работы с бизнес-процессом
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 БД. При использовании более «классических» БД результат мог бы быть другим, но даже этот результат позволяет сказать, что использование технологий неизменяемой архитектуры, описанных в книге «Искусство неизменяемой архитектуры: теория и практика управления данными в распределенных системах» дает выигрыш в простоте и производительности.