1. Увесь курс тримається на одному starter repo
Є два однаково невдалі способи вивчати інфраструктурну тему. Перший — жити в абстрактних прикладах, які зовсім не схожі на нормальний бекенд. Другий — змусити студента спершу написати новий сервіс, а вже потім, «десь ближче до кінця», показати Docker. Обидва варіанти просто крадуть увагу в головного.
Starter repo розв’язує цю проблему дуже чесно. Він уже достатньо реалістичний, щоб на ньому було видно поведінку звичайного Boot-сервісу: HTTP-шар, конфігурацію, профілі, експорт у файл, Actuator, технічні сигнали. Але водночас він навмисно не перетворюється на окремий курс із предметної області, JPA, security або messaging. Нам тут потрібен сервіс як носій контейнерної теми, а не як нова велика предметна область.
Саме тому один репозиторій методично сильніший за п’ять «міні-демо». Ви не витрачаєте сили на постійне повторне знайомство з новим проєктом, новим пакетом, новим контекстом і новою бізнес-логікою. Замість цього ви бачите, як один і той самий застосунок поступово доростає з погляду runtime та інфраструктури.
2. Як читати репозиторій, не потонувши в деталях
Перший погляд на starter repo має бути схожим не на археологію, а на карту. Спершу важливо зрозуміти верхній рівень: які файли відповідають за запуск, де лежать запити для швидких перевірок, де живе файловий сценарій, де сам код і які артефакти з’являться поруч упродовж курсу.
Типова верхньорівнева структура виглядає так:
# Корінь репозиторію (папка проєкту)
container-ready-catalog-service/
├── Dockerfile # Інструкція збирання Docker-образу (знадобиться пізніше в курсі)
├── compose.yaml # Compose-опис середовища (наприклад, app + БД)
├── requests/ # Швидкі HTTP-запити для smoke-check і відтворюваних перевірок
├── scripts/ # Допоміжні скрипти для повторюваних дій навколо сервісу
├── data/exports/ # Каталог, куди сервіс може записувати експорт (файловий сценарій)
└── src/main/ # Вихідний код Spring Boot-застосунку
На старті не потрібно сприймати це як сухий список файлів. У кожного елемента своя роль. src/main/ — там живе сам застосунок. requests/ — це швидкі HTTP-перевірки, щоб baseline можна було відтворити не «з пам’яті», а вручну. scripts/ — місце для маленьких повторюваних операцій навколо сервісу. data/exports/ — нагадування про те, що у сервісу є і файлове життя, а не лише REST. Dockerfile і compose.yaml лежать поруч не для краси: саме вони потім стануть контейнерною частиною цього самого проєкту.
Тут важливий іще один момент. Ми не читаємо Docker-артефакти завчасно, але вже бачимо, що вони живуть поруч із кодом. Це правильна проєктна інтуїція. Контейнеризація не має бути «особистою магією автора» десь в окремій папці з незрозумілими скриптами. Вона має бути частиною того самого репозиторію, що й сервіс. ⚙️
3. Сервіс дуже простий
Домен у проєкті спеціально зроблено дуже спокійним. У нас є каталог елементів і сценарій експорту цього каталогу у файл. Цього рівно достатньо, щоб на одному сервісі побачити одразу кілька важливих для курсу ліній: звичайний HTTP API, стан даних, режими запуску, файлову систему та операційні сигнали.
Якщо сказати простіше, сервіс робить не «все на світі», а лише те, що потрібно для контейнерної теми. Він уміє повертати список елементів каталогу, виконувати базові операції над ними та запускати експорт. Тут немає завдання вразити вас складністю бізнес-логіки. Навпаки, домен навмисно тримають у простих межах, щоб увага не розсіювалася. 🧪
Це важлива частина розуміння. Добрий курс не вдає, що контейнеризацію треба пояснювати через пів бекенда, матрицю безпеки, три мікросервіси та вісім таблиць у базі. Достатньо одного нормального сервісу зі зрозумілою поведінкою. І Container-Ready Catalog Service улаштовано саме так.
4. standalone і postgres: два режими
У starter repo є щонайменше два базові режими, і дуже важливо побачити їх уже в перший день. standalone потрібен для швидкого запуску без зовнішньої бази: дані живуть у пам’яті, сервіс можна підняти швидко й перевірити його HTTP-поведінку майже одразу. postgres — це вже більш реалістичний режим, де у сервісу з’являється зовнішня залежність і весь runtime стає трохи дорослішим.
Ключовий момент тут у тому, що це не два різні застосунки. У нас не з’являється окремий «docker-сервіс» і окремий «локальний сервіс». Контролери, логіка й загальний характер поведінки залишаються тими самими. Змінюються саме умови запуску. І це дуже важлива звичка: один сервіс, різні передумови середовища.
Ця сама ідея потім тягнутиметься через увесь курс. Коли вам потрібна інша поведінка середовища, ви не повинні клонувати проєкт або плодити другий кодовий шлях. Ви повинні вміти керувати runtime одного й того самого сервісу. Саме тут контейнерне мислення починає виглядати зрілим, а не косметичним.
5. Мінімальний операційний baseline
Дуже спокусливо вважати сервіс «живим» за одним фактом: процес не впав, а в логах майнула стрічка про старт. Але це надто слабкий критерій. Для нормального baseline потрібні хоча б два сигнали: бізнесовий і технічний.
Бізнесовий сигнал відповідає на запитання «сервіс реально робить те, заради чого існує?». Для нашого starter repo таким сигналом чудово працює GET /api/catalog/items. Технічний сигнал відповідає на запитання «застосунок узагалі піднявся і може чесно повідомити про свій стан?». Для цього потрібен GET /actuator/health.
Саме ця пара дає добрий smoke-check baseline. Якщо бізнесова кінцева точка відповідає, значить web-шар і мінімум логіки живі. Якщо відповідає health, значить у сервісу є принаймні базовий операційний контракт. Разом ці дві перевірки дають набагато чесніше відчуття «сервіс піднявся», ніж просто факт, що Java-процес ще не завершився.
На першому рівні цього вже досить. Нам не потрібні складні шаблони діагностики, не потрібен і глибокий розбір Actuator. Поки корисно побачити лише одне: живий сервіс — це не просто «не впав», а той, що відповідає хоча б за ключовими ознаками життя.
6. План на сьогодні 🍹
Зараз можна зробити дуже маленьку дію, яка дає перший відчутний результат без перевантаження. Спершу підніміть starter repo локально тим способом, який для вас уже звичний у Spring Boot-проєкті. Нам зараз не потрібен новий build workflow — важливо лише отримати працездатний локальний baseline.
Після цього зробіть дві швидкі перевірки. Якщо у вас стандартний порт, вони можуть виглядати так:
# Перевіряємо бізнесову кінцеву точку: сервіс реально повертає дані каталогу
curl http://localhost:8080/api/catalog/items
# Перевіряємо технічний стан: Actuator повідомляє, що застосунок "живий"
curl http://localhost:8080/actuator/health
Якщо ви звикли працювати через Postman або .http-файли, можна скористатися requests/ із репозиторію — зміст той самий. Перша перевірка відповідає на запитання «чи жива бізнесова кінцева точка», друга — «чи сприймає сервіс сам себе як живий застосунок».
Ось це і є перший значущий результат дня. Ви ще не збирали image, не писали Dockerfile і не піднімали Compose, але вже вмієте дивитися на starter repo як на операційний об’єкт, а не як на папку з кодом. І це дуже добрий старт для Docker-курсу.
7. Картка baseline перед контейнеризацією
Нижче — картка, яку можна використовувати не лише в цьому проєкті, а й майже в будь-якому майбутньому Boot-сервісі. Вона допомагає не стрибати в Docker раніше часу й швидко зафіксувати, що саме ви потім контейнеризуватимете.
| Що запитати в будь-якого Boot-сервісу перед контейнеризацією | Як це виглядає в Container-Ready Catalog Service |
|---|---|
| Що саме стартує? | Spring Boot-застосунок із зрозумілою точкою входу |
| Який мінімальний business smoke-check? | GET /api/catalog/items |
| Який мінімальний technical smoke-check? | GET /actuator/health |
| Які режими запуску важливо розрізняти? | standalone і |
| Які зовнішні передумови вже є? | Порт, у режимі postgres — база, для експорту — каталог |
| Де шукати перше операційне підтвердження? | Стартові логи + відповіді на обидві кінцеві точки |
Це здається простою таблицею, але на практиці вона дуже сильно економить сили. Щойно ви вмієте швидко відповідати на ці запитання, контейнеризація перестає бути стрибком у невідомість. У вас уже є baseline, а Docker далі буде не замінювати його, а стабілізувати.
8. Сходи еволюції проєкту
Тепер найприємніше: можна побачити курс як одну безперервну лінію, а не як набір тем. Нижче — коротка карта того, як доростатиме цей самий Container-Ready Catalog Service:
| Етап | Що змінюється в цьому самому сервісі | Що ви отримуєте |
|---|---|---|
| Зараз | Локальний Spring Boot baseline + smoke-check | Зрозумілий об’єкт контейнеризації |
| Далі | Перший image і перший container | Відтворюваний runtime одного сервісу |
| Потім | Нормальний Dockerfile, cache, multi-stage, layers, buildpacks | Професійне пакування Java-сервісу |
| Потім | Винесена назовні конфігурація, файли, логи, health, shutdown | Керовану поведінку одного image в різних режимах |
| Після цього | app + PostgreSQL через Compose | Нормальний локальний бекенд-стенд |
| Ще далі | Додавання Redis і RabbitMQ | Уміння жити з інфраструктурними залежностями без хаосу |
| Фінал | Повторно використовуваний шаблон і навичка системного розбору проблем | Відтворюваний baseline для власних сервісів |
Один і той самий сервіс проходить увесь шлях — від локального запуску до контейнерно зрілого шаблону. І завдяки цьому кожен наступний крок відчувається не як нова тема з нуля, а як природне продовження вже знайомого контексту.
Якщо тримати в голові лише одну річ після першого дня, нехай це буде ось що: контейнеризують не Docker заради Docker, а конкретний Spring Boot-сервіс зі зрозумілим baseline. Завтра цей baseline уже почне перетворюватися на інженерний об’єкт із власним життєвим циклом, логами та спостережуваною поведінкою в контейнерному середовищі.
9. Типові помилки 🚧
Помилка №1: читати starter repo як набір випадкових папок.
Спершу завжди потрібна карта верхнього рівня: де код, де запити, де файловий сценарій, де майбутні контейнерні артефакти. Якщо одразу почати тонути в класах і пакетах, дуже легко втратити сам навчальний сенс проєкту.
Помилка №2: сприймати standalone і postgres як два різні застосунки.
Це один сервіс, а не дві паралельні кодові бази. Відрізняються умови його існування, а не його природа. Ця думка потім буде критично важливою для контейнерної конфігурації та Compose.
Помилка №3: вважати сервіс «живим» лише за фактом старту процесу.
Процес може піднятися, а корисної поведінки не буде. Тому мінімальний baseline завжди краще фіксувати хоча б парою запитів: business endpoint плюс health endpoint.
Помилка №4: дивитися на Dockerfile і Compose раніше, ніж зрозумілий сам сервіс.
Це дуже часта пастка. Хочеться одразу перейти до «цікавої контейнерної частини», але без локального baseline вона перетворюється на ворожіння. Спершу потрібно зрозуміти, що саме ви контейнеризуєте і за якими сигналами впізнаєте, що сервіс живий.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ