Таксосервис кажется простым: пассажир заказал, водитель приехал. На деле это четыре приложения, которые должны видеть одну и ту же реальность в одну и ту же секунду. Если они рассинхронизировались — пассажир ждёт машину, которой уже нет, а водитель едет на отменённый заказ.
Разбираем на реальном проекте — «Авто-Таксист», локальный таксопарк, который ушёл от диспетчера на телефоне и комиссий агрегаторов. В проде: 1000+ пользователей, 100+ водителей.
01 · Зачем парку своя система вместо агрегатора
Агрегаторы заходят в регион и забирают до трети заработка комиссией. Для локального парка это прямой вычет из прибыли — навсегда. Своя система делает то же самое, что агрегатор, но принадлежит парку: правила, тарифы и деньги остаются у владельца.
Задача была не «сделать ещё один Uber», а собрать систему, которой парк сам управляет и в которой сам устанавливает правила — без чужой комиссии.
02 · Четыре роли — но одна система
Сервис состоит из четырёх частей, и это не четыре отдельных проекта, а одна система с общим бэкендом:
- Приложение пассажира. Вход по SMS, заказ с точками на карте и расчётом цены до поездки, отслеживание машины вживую.
- Приложение водителя. Выход на линию одной кнопкой, лента заказов с тарифом и ценой, карта с пинами, экран заработка.
- Операторская панель. Живая карта водителей и заказов, ручное создание и назначение — для звонков и сложных случаев.
- Админка владельца. Водители (с модерацией новых), заказы, тарифы, зоны города, метрики и выручка — вся сеть в одной сводке.
Все четыре работают поверх общего бэкенда: Node.js, Express, MongoDB, Redis. Мобильные — на Flutter (одна кодовая база на iOS и Android), панели — на React.
03 · Сердце всего: real-time
Главная инженерная задача такого проекта — реальное время. Координаты водителей, появление заказа, его принятие, отказ, статус поездки — всё обновляется вживую у пассажира, водителя, оператора и в админке одновременно. Это построено на Socket.IO: одно событие расходится по всем подключённым ролям сразу.
Отдельная боль, которую важно закрыть — честные статусы. «Водитель онлайн» должно значить, что он реально на линии сейчас, а не «заходил два часа назад». Протухшие статусы — частая болячка таких систем; мы сделали так, что статус отражает текущее состояние, а не последнее известное.
04 · Распределение заказов — это отдельный сервис
Наивная реализация — «показать заказ всем водителям». На практике это хаос. Правильно — отдельный сервис распределения, который:
- предлагает заказ подходящим водителям (по зоне подачи, тарифу, статусу);
- даёт время на принятие;
- если отказались — передаёт дальше, ведя список отказавшихся, чтобы не предлагать повторно.
Плюс тарифы и зоны: стоимость считается по адресу подачи и зоне города, а не по прямой линии. А грузовой заказ и стандартный в одной ленте подсвечены по-разному, чтобы водитель не путался.
05 · Оператор как страховка автоматики
Не всё решает автоматика. Звонок от пожилого пассажира, сложный адрес, спорная ситуация — здесь нужен человек. Поэтому оператор может создать заказ вручную, назначить водителя, поправить или отменить — поверх той же real-time системы, а не в отдельной программе.
Хорошая система автоматизирует 95% и оставляет человеку рычаги на оставшиеся 5%. Полная автоматизация без «ручного руля» ломается на первом нестандартном случае.
06 · Что забрать из этого кейса себе
Даже если у вас не такси, принципы переносятся на любую систему с несколькими ролями — доставку, сервис заявок, маркетплейс услуг:
- Несколько ролей — это одна система, а не несколько проектов. Общий бэкенд и общий источник правды экономят на стыках.
- Real-time — это архитектурное решение, а не фича. Закладывайте его с самого начала, если роли должны видеть общую картину.
- Логику распределения выносите в отдельный сервис. «Показать всем» — не стратегия.
- Оставьте человеку ручное управление. Оператор поверх автоматики спасает в нестандартных случаях.
Задумали систему с несколькими ролями и реальным временем? Опишите задачу — разложим её на части и дадим смету по архитектуре, как в этом кейсе.