1. Введение
Когда проект «вроде запускается», это примерно как фраза «самолёт вроде летит» — звучит бодро, но хочется уточнить: куда, как долго, и не держит ли пилот штурвал зубами. Финальный checklist нужен, чтобы у вас были проверяемые критерии готовности, а не ощущение. Он помогает одинаково оценивать проект сегодня, через неделю и после чужих правок.
Маршрут самодиагностики у нас уже есть: по симптому мы умеем дойти до keyword, docs, runtime и при необходимости до source. Теперь задача жёстче и полезнее — пройтись по сервису как по audit‑листу и честно проверить, готов ли template к передаче дальше.
В этой лекции мы соберём критерии в несколько групп и будем смотреть на каждую как на маленький «контракт готовности». В идеале вы сможете открыть checklist и спокойно сказать: «Да, catalog-service — это шаблон, который можно копировать в следующий проект без стыда и без шаманских танцев».
2. Группа №1 — запуск: IDE, bootRun, java -jar
Здесь не нужно заново разбирать main() построчно. Вопрос проще: одинаково ли предсказуемо сервис живёт в IDE, через bootRun и как jar. Если один из трёх режимов требует особых магических действий, шаблон ещё сырой.
Для catalog-service этот блок считается закрытым, если одновременно выполняются четыре проверки:
Точка входа скучная и стабильная. CatalogServiceApplication лежит в корневом пакете, @SpringBootApplication и @ConfigurationPropertiesScan на месте.
IDE-run не требует ручных правок кода. Запуск из IDE поднимает тот же сервис, а не “специальную локальную версию”.
./gradlew bootRun работает так же. Это базовый способ запуска без привязки к конкретной IDE.
./gradlew clean bootJar и java -jar build/libs/...jar проходят без сюрпризов. Если jar живёт иначе, значит в проекте были скрытые assumptions про classpath, ресурсы или конфиг.
3. Группа №2 — структура: слои и web
Когда проект маленький, почти любой код «влезает в голову». Когда проект становится шаблоном, он должен читаться даже тем, кто его не писал. В Spring Boot это особенно важно: часть поведения приносит платформа через auto‑configuration, и ваша задача — сделать прикладную часть максимально ясной. Поэтому в checklist структура — это не эстетика, а средство диагностики и будущего роста.
Для catalog-service здесь важны четыре спокойные проверки:
- Пакеты имеют роли. config, catalog, actuator, support, static читаются как карта местности, а не как случайный набор папок.
- В catalog видны границы. domain, repository, service, web, bootstrap не смешаны в один ком.
- Controller тонкий. HTTP-граница принимает запрос и делегирует дальше, а не превращается в мини-приложение.
- Нет “свалки на потом”. Если в проекте начинает расти utils, misc или ещё один пакет “ну сюда пока сложим”, структура уже поплыла.
4. Группа №3 — конфигурация: YAML и профили
В Boot‑шаблоне конфигурация — это управляемый слой, а не фон. Здесь важно не количество YAML‑файлов, а то, понятно ли, откуда берётся поведение сервиса и где его менять без перекомпиляции.
Для финального audit здесь достаточно нескольких жёстких вопросов:
- Роли файлов разделены. application.yaml держит базу, application-local/dev/prod.yaml — отличия окружений, catalog-data.yaml — отдельные данные каталога.
- Есть единый typed contract. CatalogProperties собирает app.catalog.* в нормальный объект, а не распыляет ключи строками по проекту.
- Нет конфигурационной пыли. Лимиты, флаги и режимы не живут в литералах и не размазаны @Value по сервисам.
- Broken config валит старт. Неправильное значение лучше поймать на старте, чем получить “оно как-то странно работает” через пять минут.
5. Группа №4 — runtime: логи и health
Готовый шаблон должен быть объяснимым не только исходниками, но и по runtime‑сигналам. Если сервис запустился, а вы не можете быстро понять, какой профиль активен, какие настройки применились и жив ли вообще каталог, template недоговорил важные вещи.
Для catalog-service этот блок считается здоровым, если выполняются такие проверки:
- Startup summary управляется конфигурацией. StartupSummaryRunner не висит безусловным @Component; его включение контролируется через app.catalog.startup-report-enabled.
- Логи дают сигнал, а не шум. На старте есть полезная информация о сервисе, но нет System.out.println-хаоса и слива чувствительных значений.
- Actuator включён осознанно. health и info доступны как минимум всегда, а расширенный exposure остаётся под контролем профилей.
- Health доменно осмыслен. CatalogDataHealthIndicator проверяет готовность каталога, а не просто факт жизни процесса.
6. Группа №5 — packaging: jar и артефакты
Packaging здесь — это не теория про jar, а вопрос “можно ли отдать артефакт другому человеку и не краснеть”. Шаблон выигрывает только если jar самодостаточен и не зависит от неявных локальных удобств.
С практической стороны здесь нужны четыре вещи:
- Executable jar собирается стабильно. bootJar не должен быть “магией, которая иногда срабатывает”.
- Профили и overrides приходят снаружи. --spring.profiles.active=..., env vars и external config меняют поведение без правки Java-кода.
- Jar не опирается на IDE-магии. Ресурсы, конфиги и landing page живут одинаково предсказуемо и в IDE, и вне неё.
- Layered jar понятен как packaging-механика. Не нужно копаться в слоях каждый день, но термин не должен звучать как заклинание из чужого Docker-ритуала.
7. Группа №6 — tests: минимум
Здесь не нужен большой testing-playbook. Нужна маленькая сетка, которая быстро орёт, если template сломали. У шаблона самая дорогая поломка — тихая: кто-то скопировал проект, чуть-чуть поправил конфиг или controller, и проблема всплыла уже потом.
Для финального baseline хватает четырёх проверок:
- contextLoads() — контекст вообще поднимается.
- Property override test — CatalogProperties реально реагирует на переопределение свойств.
- Web smoke — базовый endpoint отвечает 200 OK.
- Health smoke — /actuator/health доступен.
Эти тесты не делают проект идеальным, но они перестают оставлять вам только ручную проверку “ну я же открыл браузер”.
8. Группа №7 — handoff: README и запросы
Последняя практическая проверка простая: сможете ли вы отдать сервис другому разработчику так, чтобы он не писал вам в мессенджер через пять минут. Если нет — шаблон ещё слишком сильно держится на авторе.
Для handoff здесь нужны три вещи:
- README короткий, но точный. В нём есть build, run, profiles, main endpoints и способы override свойств.
- Sample requests актуальны. .http-файл или аналог позволяет быстро дернуть /api/catalog/courses, /api/catalog/featured, /actuator/health.
- Handoff воспроизводим. Другой человек может собрать, запустить и проверить сервис без археологии по исходникам.
9. Что этот template уже готов отдать дальше
Финальный template полезен не только тем, что он аккуратный сам по себе. Важно ещё видеть, на какой следующий инженерный слой его уже можно наращивать без переписывания, а что сюда специально не тащили. Тогда catalog-service воспринимается не как недоделанный сервис, а как честный Boot‑baseline.
| Следующий курс | Что уже готово в catalog-service | Что сюда сознательно не тащим |
|---|---|---|
| Spring REST & MVC | рабочий MVC-baseline, JSON-ответы, тонкий controller, config-driven service | DTO-heavy границу, validation входа, полноценный error contract, design-level REST решения |
| Spring Data JPA | service/repository границы, externalized config, smoke-tests, понятный runtime | entities, SQL/JPA, repositories БД, transactions |
| Spring Security | привычку к safe exposure, работу с profiles и external config, Actuator-awareness | authentication, authorization, users/roles, security filter chain |
| Spring Test | spring-boot-starter-test, contextLoads(), property overrides, базовый MockMvc и health smoke | test slices, integration strategy, contract-level web testing, broader tooling |
| Docker for Spring | bootJar, java -jar, external config mindset, layered-jar awareness | Dockerfile, Compose, container runtime и эксплуатационные детали |
Если этой границы не видно, template легко принять либо за “недоделанный сервис”, либо за “универсальную заготовку на всё сразу”. Оба вывода мимо цели.
10. Сводный checklist для catalog-service
Чтобы не держать всё в голове, ниже — компактная таблица. Её удобно прогонять после заметных правок, перед handoff и просто когда хочется проверить, не начал ли template тихо расползаться.
| Группа | Критерий | Как проверить (конкретно) | Что считается “OK” |
|---|---|---|---|
| Запуск | IDE‑run работает | Запустить CatalogServiceApplication из IDE | Приложение поднялось, нет ошибок контекста |
| Запуск | bootRun работает | ./gradlew bootRun | Приложение поднялось так же, как в IDE |
| Запуск | bootJar собирается | ./gradlew clean bootJar | В build/libs появился executable jar |
| Запуск | java -jar работает | java -jar build/libs/...jar | Сервер слушает порт, endpoints отвечают |
| Структура | Main‑класс в корневом пакете | Открыть CatalogServiceApplication | Base package соответствует проекту, scanning предсказуем |
| Структура | Пакеты имеют роли | Посмотреть дерево пакетов | Нет свалки “всё в одном пакете”, роли читаемы |
| Структура | Controller тонкий | Открыть CourseCatalogController | В контроллере нет бизнес-логики и фильтрации |
| Конфигурация | YAML‑first | Проверить src/main/resources | application.yaml основной, нет хаотичного mix .properties |
| Конфигурация | Профили local/dev/prod | Проверить файлы application-*.yaml | Различия минимальны, общее в base config |
| Конфигурация | Modular config | Проверить spring.config.import | Данные каталога вынесены в catalog-data.yaml |
| Конфигурация | Typed config есть | Найти CatalogProperties | Есть @ConfigurationProperties("app.catalog") |
| Конфигурация | Typed config зарегистрирован | Проверить @ConfigurationPropertiesScan | Properties реально поднимаются как бин |
| Конфигурация | Валидация включена | Проверить наличие constraints и starter | Broken config валит старт (fail‑fast) |
| Runtime visibility | Logging baseline есть | Запуск и просмотр логов | Логи осмысленные, нет System.out.println‑хаоса |
| Runtime visibility | Startup summary есть | Проверить StartupSummaryRunner, StartupConfiguration и app.catalog.startup-report-enabled | На старте есть короткий summary, который включается конфигом |
| Runtime visibility | Actuator подключён | Проверить dependency и /actuator/health | /actuator/health доступен по политике профиля |
| Runtime visibility | Safe exposure | Проверить management.endpoints.web.exposure.* | В prod минимум, в local/dev — расширенно, но не * |
| Runtime visibility | Custom health есть | Проверить CatalogDataHealthIndicator | Health учитывает доменные условия |
| Packaging | Артефакт самодостаточен | Запуск jar в чистом окружении | Никаких скрытых зависимостей на IDE |
| Tests | contextLoads() есть | ./gradlew test | Тесты проходят, контекст поднимается |
| Tests | Override properties тестируется | Проверить CatalogPropertiesOverrideTest | Override реально влияет на binding |
| Tests | Web smoke есть | Проверить MockMvc test на endpoint | Endpoint отвечает 200 OK |
| Tests | Health smoke есть | Проверить MockMvc test на /actuator/health | Health доступен и отвечает 200 OK |
| Handoff | README есть | Открыть README.md | Есть команды build/run и список endpoint’ов |
| Handoff | Sample requests есть | Открыть http/*.http | Есть готовые запросы для ручной проверки |
Если хотя бы в одной группе вы не можете сказать “OK” без оправданий — шаблон ещё не готов. В этом и смысл checklist: он не ругает, он просто честно показывает, где слабое место.
11. Типичные ошибки при работе с финальным checklist
Ошибка №1: “Чек‑лист есть, но мы его не прогоняем”.
Это классика: документ написали, положили в README, но дальше живут по принципу «ну оно же вчера запускалось». Через пару правок в конфиге или после обновления зависимостей внезапно падает java -jar, и начинается охота на ведьм. Лечится просто: прогон checklist — это ритуал, который занимает минуты, а экономит часы.
Ошибка №2: тесты превращают в один contextLoads() и успокаиваются.
contextLoads() — обязательный, но он не доказывает, что endpoints отвечают, что Actuator поднят, что overrides работают. Такой шаблон всё равно ломается “тихо”: контекст стартует, а /api/catalog/courses внезапно 404 из-за ошибки в mapping. Минимальный MockMvc smoke‑тест и health‑smoke как раз закрывают эту дыру.
Ошибка №3: “в IDE работает” принимают за финальную проверку packaging.
IDE может простить многое: ресурсы подхватятся, classpath будет удобный, devtools иногда скрывает неприятные моменты. Но jar‑run живёт по другим правилам, и это нормально. В checklist jar‑run — главный экзамен. Если он не проходит, шаблон нельзя считать готовым, даже если IDE‑run идеально зелёный.
Ошибка №4: конфигурацию считают второстепенной, и часть поведения остаётся в коде “просто так”.
Очень легко оставить в сервисе литерал 4 как лимит featured‑курсов, потому что “пока так”. Но именно из таких “пока так” и собирается проект, который невозможно управлять через окружения. Checklist специально заставляет вас спрашивать: лимиты, флаги и режимы работы читаются из CatalogProperties или они спрятаны в коде?
Ошибка №5: Actuator открывают “для удобства” слишком широко и забывают закрыть.
В local хочется включить всё, и это понятно. Но когда привычка превращается в дефолт и вы видите include: "*" в базовом application.yaml, это уже не удобство, а риск и анти‑шаблон. Checklist помогает держать это в рамках: local/dev расширенно, prod минимально — и это правило видно в конфиге.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ