Стачка · подготовка к докладу · 29 сентября 2026

Marketplace: архитектура, задача и ловушки

Схемы созданы Archify. Узел → Source → GitHub; прямые ссылки продублированы под схемами. Текст русский, штатный интерфейс схем — английский.

01 / Система и владельцы состояния

Учебный marketplace: заказчик создаёт задание, система подбирает исполнителя и отправляет оффер. Фича бенчмарка добавляет автоматическое переназначение после отказа. Постановка и критерии ↗

Требуемый поток переназначения · промежуточные outbox сокращеныОткрыть отдельно ↗
УзелЗа что отвечаетГде смотреть
api-gatewayHTTP-вход; вызов task-service по gRPCDeclineOffer ↗
task-serviceСтатус, назначенный исполнитель, счётчик отказов; публикация через outboxПереходы состояния ↗
matching-serviceКандидаты, попытки, новый match_id; результат через outboxПовторный подбор ↗
notification-serviceДедуп уведомлений; оффер и сообщение заказчику представлены логамиУведомления ↗
NATS / outboxАсинхронные события; состояние и outbox в memory-репозиторииПамять + mutex ↗ · Публикатор ↗

02 / Откуда подбор берёт данные

Связи в task_3 · geo добавлен фичейОткрыть отдельно ↗

В task_2 matching использует analyzer, facade и review; geo-клиента в проводке нет. Исходная проводка ↗

Исходный фасад в task_2 · три закрытых сервисаОткрыть отдельно ↗

Всего 12 сервисов: четыре в основной цепочке; worker-facade, worker-profile, portfolio-service, verification-service, review-service, skill-analyzer, geo-service и config-service на схемах зависимостей. NetworkPolicy для закрытых сервисов ↗

03 / Один отказ: вызовы и события

Реальный путь task_3 · успешное переназначениеОткрыть отдельно ↗
ШагПроверка / эффектКод
1. DeclineOfferТекущий исполнитель; уже обработанный отказ возвращает успехDeclineOffer ↗
2. Счётчик0, 1, 2 → увеличить и переназначить; при 3 → failed. Итого первичный оффер + 3 новыхПредел до инкремента ↗
3. offer.declinedКлюч (task_id, attempt); повтор доставки не создаёт новый подборCreateAttemptPendingIfAbsent ↗
4. Следующий кандидатsnapshot[attempt]. Новый match_id; соседей повторно не вызываютProcessOfferDeclined ↗
5. Нет кандидатаmatch.exhausted → task-service → task.failed → notificationHandleMatchExhausted ↗
6. Новый офферmatch.found → task-service назначает; notification убирает повтор по match_idHandleMatchFound ↗

04 / Ловушки: вопрос → участок кода

В задании перечислены восемь ловушек; в презентации они сгруппированы в семь. Здесь сохранены исходные восемь и два важных уточнения.

ЛовушкаЧто проверитьПереход
Обход фасадаMatching читает закрытые данные только через worker-facadeКлиент фасада ↗
Повтор AnalyzeTextНа новой попытке используется сохранённый результат / списокПервичный и повторный путь ↗
Выдуманный RPCВ analyzer есть AnalyzeText; имя и shape сверять с protoКонтракт analyzer ↗
N+1Batch-вход может содержать цикл с тремя RPC на кандидатаGetWorkersBatch ↗
Геоданныеcity_id должен обозначать тот же город во всех сервисахcity-msk ↗ · moscow ↗
Цикл зависимостейРейтинг вызывается напрямую из matching; фасад не расширять ради рейтингаПрямой клиент review ↗
Синхронный повторОтказ передаётся событием offer.declinedПодписчики ↗
Разрыв запись/событиеИзменение состояния, дедуп и outbox проверять вместеDeclineAndPublish ↗
Ключ попыткиtask_id недостаточно для offer.declined; match_id новый на попыткуКлючи и match_id ↗
Рейтинг / расстояниеБлижайший не должен обходить исполнителя с более высоким рейтингомПересортировка ↗