Открытое ТЗ для пет-проекта: сервис поиска пропавших людей

от автора

Долго думал, в каком формате это написать, в итоге решил по-простому — рассказать, что есть, и кому это может пригодиться.

Мы с коллегой делали выпускной проект для курсов CloudJava и CloudJava K8S. Это техзадание на бэкенд распределенной системы — четыре микросервиса, Gateway, обвязка. Изначально оно было доступно только участникам курсов, теперь решили открыть бесплатно, без регистрации или “оставьте email”. Просто страница с текстом и тремя OpenAPI-спеками.

Ссылки сразу, чтобы не листать:

Дальше — о чем, собственно, проект и почему на него стоит (или не стоит) обратить внимание.

Идея

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

Сценарий примерно такой. Администратор заводит инцидент — пропал человек, последний раз видели там-то, координаты такие-то. Система ищет волонтеров, которые сейчас ближе всего к этой точке. Достает их контакты, кидает им уведомления. Волонтер отвечает — согласен или нет. Когда поиск завершается (нашли человека или нет — увы, бывает по-разному), всем участникам уходит финальное сообщение.

Сервисы:

  • Volunteer Service — волонтеры, их профили, статусы, согласия/отказы на инциденты.

  • Tracking Service — координаты волонтеров и геопоиск. Тут самое “мясо” с PostGIS.

  • Incident Service — собственно инциденты, оркестрация при их создании.

  • Notification Service — уведомления. В учебной версии не шлет ничего реального, просто пишет в лог, но придется попотеть, чтобы реализовать сервис так, чтобы перевод на отправку сообщений был с минимальными затратами.

  • Gateway на Spring Cloud Gateway — единая точка входа, проверка JWT, rate limit.

Чем это интересно технически

Стек, в общем-то, мейнстримный для современного Java-бэкенда. Spring Boot 3.5+, Kafka, PostgreSQL, Keycloak, Kubernetes. Что важно — каждый компонент тут не для галочки.

Расскажу про несколько мест, которые мне самому показались самыми интересными.

Геопоиск. Tracking-сервис должен уметь возвращать волонтеров в прямоугольной области карты. Звучит просто, пока количество точек не переваливает за порог. Дальше нужно отдавать кластеры, а не сами точки. И вот тут начинается: ST_ClusterDBSCAN, расчет параметра eps в зависимости от размера прямоугольника (все в метрах, поэтому ST_Transform и переход в проекцию), minpoints = 1 чтобы одинокий волонтер тоже попадал в выдачу. Это задача, на которой можно реально потренировать PostGIS, а не выучить пару SELECT-ов.

Граница транзакции при создании инцидента. Сценарий такой: записать инцидент в БД, сходить в Tracking за ближайшими волонтерами, потом в Volunteer за их контактами, потом отправить событие в Kafka. В ТЗ требование прямое — сетевых вызовов в границах БД-транзакции быть не должно. И отправки в Kafka — тоже. Дальше думаете сами — Outbox? И если да, то какой — через Debezium или через свой шедулер? Каждый вариант с плюсами и минусами, обоснование своего решения надо зафиксировать в JavaDoc.

Дедупликация консьюмеров. Kafka гарантирует at-least-once, значит, возможны повторы. ТЗ намекает, что можно попробовать настроить exactly_once, но это сложно, дорого и часто не нужно. Намного интереснее — добиться идемпотентности на стороне приложения. Особенно в нотификациях: волонтеру не должно прилететь одно и то же уведомление три раза только потому, что консьюмер перезапустился. Кто работал с Kafka в проде, тот знает, как легко тут “выстрелить себе в ногу”.

Переупорядочивание событий. Это уже про Incident Service, который слушает топик с событиями о согласии/отказе волонтера участвовать в поиске. В редких случаях события приходят не в том порядке, в котором были отправлены. Many-to-many связь между волонтером и инцидентом, плюс возможность отказа после согласия, плюс возможные дубли — и вот вам ловушка, в которую очень легко влететь.

Идемпотентность создания инцидента. Через X-Idempotency-Key. Тут все стандартно, но конкретно реализовать — отдельная история, особенно если хочется сделать честно, а не “нашли ключ — вернули то же самое”.

Параллельная отправка уведомлений под feature-toggle. Предлагается выбрать наиболее оптимальную стратегию распараллеливания отправки сообщений и занести ее реализацию под feature-toggle. Задача далеко не самая тривиальная с учетом того, что необходимо сделать так, чтобы трассировка запросов сохранялась даже в кастомных Executor-ах. Если используете виртуальный потоки или Structured Concurrency, то тут придется отдельно поломать голову.

Что еще в наборе требований:

  • Аутентификация через Keycloak. Особенность: JWT валидируется на Gateway, дальше в сервисы идет X-USER-ID из sub. Сами сервисы про OAuth ничего не знают. Решение спорное (Gateway становится единой точкой отказа авторизации, любая внутренняя коммуникация в обход теряет проверки), но оно явно выбрано — и тут есть о чем подумать, почему так.

  • Observability: Prometheus + Grafana + Loki + Tempo. Полный набор. С парой доменных метрик — incident_count_total по регионам, incident_result_total по статусам успеха/неудачи.

  • Retry, Circuit Breaker, Rate Limit.

  • Деплой на выбор: Docker Compose или Kubernetes/Helm. Можно сначала собрать в композе, потом переехать в k8s — нормальный путь.

  • Мелочи, которые часто забывают: маскирование PII в логах (s***l@service.com), Flyway/Liquibase, UTC везде, очистка устаревших данных самим приложением.

Кому это вообще надо

Честно скажу — не всем. Это не первый пет-проект на Spring.

Подойдет, если:

  • Уверенно владеете Spring Boot, Spring Data, понимаете, как устроен Docker. На работе делаете типовые задачи и хотите выйти за их пределы.

  • Готовитесь к собеседованиям уровня middle+/senior и хотите портфолио, где есть про что говорить.

  • Не боитесь Kafka и PostgreSQL, хотите на практике реализовать Transactional Outbox.

  • Есть теоретическое понимание микросервисной архитектуры и основных паттернов, но нет опыта их самостоятельной реализации.

Не подойдет, если:

  • Только-только освоили Spring и ищете что-то “на потренироваться”. Тут, скорее всего, вы захлебнетесь и забросите. Я бы рекомендовал взять что-то попроще.

  • Ищете “проект на выходные”. Объем проекта — это два-три двухнедельных спринта по меркам продуктовой команды. У меня ушло около 2 месяцев кодирования по вечерам после основной работы.

Что бесплатно

Собственно говоря — все то, ради чего я этот пост и пишу:

  • Полное ТЗ. Со всеми сценариями, описанием примерных моделей данных, схемами сообщений в Avro, нефункциональными требованиями.

  • Три OpenAPI-спеки (volunteer, tracking, incident). Сразу можно генерировать клиенты и серверные стабы.

  • Архитектурная диаграмма.

  • Советы по реализации и декомпозиции на подзадачи.

Этого хватит, чтобы реализовать проект целиком. Контракты зафиксированы — сравнивайте свою реализацию с OpenAPI, доверяйте здравому смыслу там, где документация молчит.

Можно прекрасно делать проект и без платной части. Просто проверять придется самим, и сравнить будет не с чем. Для многих этого достаточно. Для кого-то — нет, и тогда есть платный вариант:

Скрытый текст
  • Набор end-to-end автотестов. Объективная приемка: прошли — сделали, не прошли — где-то ошиблись.

  • Референсная реализация автора курса. Чтобы сверить свои архитектурные решения с чужими.

  • Ревью каждого сервиса отдельно.

PS

Если будете делать и наткнетесь на места, где, на ваш взгляд, ТЗ непонятное, кривое или вообще не покрывает кейс — напишите мне или в комментариях. ТЗ открытое, и я готов его дорабатывать. Это, в общем-то, еще одна причина выложить его публично — собрать фидбэк от тех, кто на нем что-то реальное сделал.

На этом все. Удачи тем, кто решится. И, надеюсь, будет полезно!

ссылка на оригинал статьи https://habr.com/ru/articles/1034426/