Долго думал, в каком формате это написать, в итоге решил по-простому — рассказать, что есть, и кому это может пригодиться.
Мы с коллегой делали выпускной проект для курсов CloudJava и CloudJava K8S. Это техзадание на бэкенд распределенной системы — четыре микросервиса, Gateway, обвязка. Изначально оно было доступно только участникам курсов, теперь решили открыть бесплатно, без регистрации или “оставьте email”. Просто страница с текстом и тремя OpenAPI-спеками.
Ссылки сразу, чтобы не листать:
-
Описание проекта: https://javaops.ru/view/cloudjava3
-
Само ТЗ: https://javaops.ru/view/cloudjava3/rescue-service.html
Дальше — о чем, собственно, проект и почему на него стоит (или не стоит) обратить внимание.
Идея
Бэкенд платформы поиска пропавших людей. Звучит, может, чересчур масштабно для пет-проекта, но в реальности это четыре сервиса со вполне понятными границами.
Сценарий примерно такой. Администратор заводит инцидент — пропал человек, последний раз видели там-то, координаты такие-то. Система ищет волонтеров, которые сейчас ближе всего к этой точке. Достает их контакты, кидает им уведомления. Волонтер отвечает — согласен или нет. Когда поиск завершается (нашли человека или нет — увы, бывает по-разному), всем участникам уходит финальное сообщение.
Сервисы:
-
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/