Введение
История о том, как очередная «быстрая костыль-интеграция» на коленке неожиданно превратилась в почти полноценную Order Management System (OMS) с элементами event-driven архитектуры. Всё это — без предварительного проектирования и без единой строчки кода на Java/Scala/Python (хотя тут немного лукавства, так как пару скриптов на Groovy все-таки имеется), на чистом Apache NiFi и SQLite.
Девизом этого проекта мог бы стать слоган: «Мы не ищем лёгких путей, мы ищем работающие решения». Я инженер в одной ритейл компании, который любит решать задачи, и сегодня расскажу, как закрыл боль бизнеса малой кровью, используя не совсем типичный для веб-сервисов инструмент.

Предыстория: боль бизнеса длиною в несколько лет
Задача звучала на первый взгляд просто: предоставить клиентам возможность получать в качестве подарка услуги в обмен на бонусы программы лояльности. Но за этой простотой скрывались годы неудачных попыток. Решения получались либо дорогими, либо сложными и дорогими. Вечная история.
На очередном совещании, выслушав стенания коллег, я поймал себя на мысли: «А почему бы не попробовать?». У меня уже был положительный опыт быстрого прототипирования интеграций на Apache NiFi, и я решил рискнуть.
Идея-спринтер была проста: телеграм-бот, в который продавец вводит номер карты лояльности. Бот дергает API системы лояльности и списывает заранее прописанное в потоке количество бонусов. Всё. Быстро и сердито.

- Коллеги, а как вы отнесетесь к тому, чтобы продавцы просто вводили номер карты в телеграм-бот, а он сам всё списывал?» - выдал я
Воцарилась пауза. Первым нарушил молчание руководитель отдела продаж: - Телеграм-бот? Мы же не умеем с ботами работать, у нас даже чатов таких нет. -
Коллеги выглядели удивленными, ничего себе, какие от них скрывали возможности...
Я быстро переключил их с удивления на обсуждение конкретики:
- Коллеги, давайте технические детали оставим мне, а сейчас перейдем к описанию бизнес-процесса. Самое сложное здесь - даже не списание бонусов, а организация процесса записи на услугу. Как вы сейчас работаете с расписанием? -
Ведущий менеджер смущенно улыбнулась:
- Ну... У нас тетрадочка в каждом магазине. Записываем туда клиентов на нужное время и берем их телефон для контакта. -
Я знал это, потому что ранее мы упирались именно в эту тетрадочку, услуга-то предоставляется и сейчас, просто не за бонусы. Парадокс, но мы так и не нашли подходящего инструмента для записи на рынке. Все решения, или не давали нужной функциональности из коробки, или были дорогими и игра не стоила свеч.
- Вот видите! - обрадовался я. - Автоматизацию расписания мы точно оставим на потом. Сейчас говорим только о списании бонусов. Бот будет просто фиксировать факт оплаты.
Казалось, всех устроило такое упрощение. Но тут активизировался руководитель по CRM: - Минуточку. А как же антифрод? У нас же есть требование - подтверждение списания SMS-кодом. Мы не можем просто так позволить списывать бонусы без подтверждения от клиента.
В воздухе повисло напряженное молчание. Затем я не сдержал улыбки:
- Отлично! В API системы лояльности как раз есть метод подтверждения по SMS. Значит, добавим двухфакторную аутентификацию. Теперь нашему боту нужно будет: найти телефон клиента по карте, отправить запрос на списание, запросить SMS-подтверждение... Задача усложнилась, но всё ещё решаема!
Коллеги переглянулись. Руководитель продаж с одобрением. Руководитель CRM обрадовался: - Ну, если технически возможно... Давайте пробовать.
Так изначальная идея обрастала требованиями: теперь нужно было и телефон клиента по карте найти, и два метода API вызвать, а не один. Задача усложнилась, но все в разумных пределах.
День первый: вдохновение и рождение OMS
На следующий день я сел с целью набросать поток за 30 минут и… завис на полдня. Не из-за проблем, а из-за нахлынувшего вдохновения и идей.
Я осознал: если услугу покупают (пусть и за бонусы), значит, это товар. А если товар — почему бы не добавить его в заказ? А если заказ — почему бы не добавить в него статусы? Его можно оплатить, отменить, сделать возврат. Так родилась идея не просто «списывалки», а целой Order Management System.
При этом все эти мысли казались вполне выполнимыми. Технически это вылилось в:
Таблицы в SQLite:
orders
payments
payment_confirmations
status_history
.
Выбор SQLite был очевиден: для пилота с низкой нагрузкой — идеально, а при необходимости миграция на PostgreSQL или MySQL — дело техники.
Набор HTTP-эндпоинтов, обрабатываемых NiFi:
POST /order/create
– создание заказа.POST /order/update
– обновление (в основном статуса).GET /order/search
– поиск заказа.POST /status/add
– добавление записи в историю статусов.

Скрытый текст
Да, понимаю, что сейчас это выглядит как монстр, но нужно раскидать по ProcessingGroup соответствующие потоки, станет намного понятнее и управляемее.
День второй: углубление в кроличью нору — платежи и асинхронность
С оплатой я решил не упрощать. Вместо простого вызова списания родилась целая мини-платёжная система.
POST /payment/create
– создание платежа, привязанного к заказу.POST /payment/status/add
– добавление статуса платежа («создан», «ожидает подтверждения», «подтверждён», «отменён» и т.д.).POST /payment/async/confirm/add
– асинхронное подтверждение. Этот эндпоинт не ждёт верификации кода, а лишь создаёт задачу на проверку.GET /payment/async/confirm/search
– проверка результата асинхронной задачи.
Такой подход позволил создать слабосвязанную систему. Процесс оплаты был разбит на этапы, каждый из которых управлялся своим автоматическим процессом в NiFi.
Архитектура «бегающих процессов» вместо событий
Так как у меня не было готового брокера сообщений вроде Kafka, или RabbitMQ, я реализовал логику через фоновые процессы в NiFi, которые периодически опрашивали базу данных.
Процесс «Создание заказа»: ищет заказы со статусом
new
, переводит их вorder_created
и пишет запись в историю.Процесс «Создание платежа»: видит платёж со статусом
payment_created
и создаёт запись вpayment_confirmations
.Процесс «Отправка SMS»: для записей
confirmation_created
генерирует код, отправляет SMS, сохраняет код и ставит статусconfirmation_pending
.Процесс «Верификация кода»: проверяет, совпал ли введённый код с отправленным. Если да — меняет статус на
payment_verified
.Процесс «Подтверждение платежа»: для верифицированных платежей обновляет статусы заказа и платежа.

Именно здесь я осознал мощь NiFi для построения устойчивых ETL-цепочек. Каждый такой процесс — это независимый ProcessingGroup
в NiFi, который можно легко вынести на отдельный сервер, превратив в микросервис. Решение уже сейчас было слабосвязанным и готовым к декомпозиции.
Меня искушала идея заменить это на чисто событийную архитектуру с одной таблицей-бородом событий (events
), которую бы слушал один главный процесс-оркестратор. Но я вовремя остановился, решив сначала получить работающий прототип, а потом уже рефакторить. Нельзя погрязнуть в over-engineering на стадии MVP.
Неожиданный бонус: данные для аналитики и календаря
Самое интересное началось после того, как основные процессы были отлажены. Я вдруг осознал, что построил не просто инструмент для списания бонусов, а полноценную систему учёта заказов (OMS). А это значит, что все данные уже структурированы и готовы к аналитике.
Что это дало бизнесу сразу же:
-
Отчетность:
По продавцам и магазинам: кто из сотрудников оформил больше всего услуг за день/неделю/месяц. Это сразу же добавило элемент здоровой конкуренции и мотивации.
По списаниям: сколько бонусов было списано в разрезе по каждому магазину. Финансовому отделу больше не нужно было вручную сводить данные из разных систем.
По клиентам: кто чаще всего пользуется услугой, оплачивая бонусами. Это готовый список для дальнейшего анализа лояльности и персональных предложений.
Всё это стало возможно благодаря всего одному дополнительному процессу в NiFi, который периодически агрегировал данные из таблиц заказов и платежей и складывал их в отдельные витрины данных или просто формировал готовые отчеты в виде CSV-файлов. NiFi здесь снова оказался идеальным инструментом.
-
Фундамент для будущего календаря:
Вся информация о забронированном времени уже хранится в таблице заказов. Для реализации функции показа свободных слотов достаточно просто сделать выборку по дате и статусу заказа (
paid
илиconfirmed
).Таким образом, в будущем интерфейсе календаря не будет никаких лишних интеграций — он будет просто обращаться к методу
GET /api/order/busy-slots?date=YYYY-MM-DD
, который легко реализовать на том же NiFi, чтобы вернуть список занятого времени.
Это был ключевой момент окупаемости решения. Мы получили не просто функционал «списать бонусы», а центр данных по этому процессу, который сразу же начал приносить бизнесу дополнительную пользу.
День третий: телеграм-бот и необходимость API-шлюза
Для фронтенда я использовал телеграм-бота. У меня уже был наработанный опыт создания телеграм-ботов в NiFi, поэтому я быстро интегрировал его со своим loyalty.orders.api
.
Но тут ждал сюрприз. Мои «сырые» API-методы требовали JSON с более чем 15 полями: номер карты, телефон, адрес магазина и т.д. Просить продавца вводить это всё в бота — утопия.
Так стихийно родился API-гейтвей/оркестратор. Я создал прокси-методы:
POST /proxy/order/create
– принимает всего три параметра: код продавца, карту клиента и дату. Всю остальную информацию (магазин, телефоны) он подтягивает сам на основе кода продавца.POST /proxy/payment/create
– принимает только номер заказа.POST /proxy/payment/confirm
– принимает номер заказа и SMS-код.
Это был ключевой момент осознания. Система сама подсказала мне, где нужен следующий уровень абстракции. Архитектура рождалась в процессе, а не проектировалась заранее.

Для MVP я реализовал 4 команды:
/new НОМЕР_КАРТЫ ДАТА ВРЕМЯ
– создание заказа./pay НОМЕР_ЗАКАЗА
– создание платежа./payconfirm НОМЕР_ЗАКАЗА КОД
– подтверждение платежа./ordersearch НОМЕР_ЗАКАЗА
– просмотр заказа.
Так можно ли делать веб-сервисы на NiFi?
Короткий ответ: да, но осторожно.
NiFi — не нативный инструмент для создания веб-сервисов. У него нет встроенной экосистемы фреймворков, как у Spring или Express.js. Однако его мощь заключается в другом:
Устойчивость «из коробки»: NiFi гарантирует доставку данных, имеет механизмы повторных попыток и прекрасно работает с очередями.
Визуальное программирование: сложные цепочки обработки гораздо нагляднее, чем код.
Модульность через Processing Groups: каждый процесс можно изолировать, протестировать и потом легко перенести в отдельный микросервис.
Скриптование: процессоры вроде
ExecuteScript
(Groovy, Jython, JavaScript) позволяют быстро реализовать сложную логику там, где стандартных процессоров не хватает.
Это решение идеально подходит для:
Быстрого прототипирования и MVP.
Пилотирования гипотез без привлечения больших команд разработки.
Внутренних автоматизаций и интеграций, где важна надёжность, а не низкая latency.
Для высоконагруженных API, конечно, лучше подойдут классические фреймворки. Но для многих задач NiFi оказывается более чем достаточным и невероятно эффективным по времени внедрения.
Планы на будущее
Система работает, но это только начало. В планах:
Инлайн-кнопки в боте («Оплатить», «Отменить», «Вернуть») для улучшения UX.
Диалоговый сценарий создания заказа вместо ввода одной командой.
Интеграция с календарём для отображения только свободных слотов (благо, все данные для этого уже есть!).
Рефакторинг в сторону событийности. Здесь встаёт вопрос: остаться на SQL-таблицах или поднять Kafka? Первое - проще, второе - правильнее для масштабирования.
Сбор фидбека от первых пользователей и итеративное улучшение.
Выводы
Этот проект — отличный пример того, как практика рождает архитектуру. Мы не проводили предварительный анализ и не рисовали диаграммы UML. Архитектура выкристаллизовывалась сама в процессе кодинга, когда приходило понимание слабых мест и точек роста.
Главные инсайты:
NiFi — мощный инструмент для прототипов и не только. На нём можно не только сделать MVP для проверки гипотезы, но и получить вполне себе готовое к внедрению решение благодаря встроенной отказоустойчивости.
Не бойтесь начинать без архитектуры. Иногда нужно просто начать делать, и архитектура придёт сама, подсказанная практическими потребностями. А вместе с ней появятся и неожиданные бонусы в виде готовой аналитики.
Слабосвязность и модульность заложены в NiFi изначально. Благодаря
Processing Groups
ваш прототип может плавно эволюционировать в микросервисную архитектуру.
И самое главное — задача бизнеса, которая годами не решалась, была закрыта за несколько дней малой кровью. А это лучшая награда для инженера.
А вы пробовали использовать NiFi в качестве бэкенда?