1. Cały kurs trzyma się jednego starter repo
Są dwa równie złe sposoby uczenia tematu infrastrukturalnego. Pierwszy — żyć w abstrakcyjnych przykładach, które w ogóle nie przypominają normalnego backendu. Drugi — każać studentowi najpierw napisać cały nowy serwis, a dopiero potem, „gdzieś bliżej końca", pokazać Dockera. Oba warianty po prostu kradną uwagę od głównego.
Starter repo rozwiązuje ten problem bardzo uczciwie. Jest już wystarczająco realny, żeby na nim widać było zachowanie normalnego serwisu Boot: warstwę HTTP, konfigurację, profile, eksport do pliku, Actuator, sygnały techniczne. Przy tym celowo nie zamienia się w osobny kurs z modelu domenowego, JPA, security czy messagingu. Potrzebujemy tu serwisu jako nośnika tematu kontenerowego, a nie nowej dużej dziedziny przedmiotowej.
Właśnie dlatego jedno repozytorium jest metodycznie silniejsze niż pięć „mini-demo". Nie tracisz sił na ciągłe ponowne zapoznawanie się z nowym projektem, nowym pakietem, nowym kontekstem i nową logiką biznesową. Zamiast tego widzisz, jak ta sama aplikacja stopniowo dojrzewa z punktu widzenia runtime i infrastruktury.
2. Jak czytać repozytorium, nie tonąc w szczegółach
Pierwsze spojrzenie na starter repo powinno przypominać nie archeologię, lecz mapę. Najpierw ważne jest zrozumienie górnego poziomu: które pliki odpowiadają za uruchomienie, gdzie leżą żądania do szybkich sprawdzeń, gdzie mieszka scenariusz plikowy, gdzie jest kod i jakie artefakty pojawią się obok w toku kursu.
Typowa struktura górnego poziomu wygląda tak:
# Korzeń repozytorium (folder projektu)
container-ready-catalog-service/
├── Dockerfile # Instrukcja budowy Docker image (przyda się później w kursie)
├── compose.yaml # Opis środowiska Compose (np. app + baza)
├── requests/ # Szybkie żądania HTTP do smoke-checku i reprodukowalnych weryfikacji
├── scripts/ # Skrypty pomocnicze do powtarzalnych działań wokół serwisu
├── data/exports/ # Katalog, do którego serwis może pisać eksport (scenariusz plikowy)
└── src/main/ # Kod źródłowy aplikacji Spring Boot
Na starcie nie trzeba postrzegać tego jako suchej listy plików. Każdy element ma swoją rolę. src/main/ — tam żyje sama aplikacja. requests/ — to szybkie weryfikacje HTTP, żeby baseline był reprodukowalny nie „z pamięci", lecz rękami. scripts/ — miejsce na małe powtarzalne operacje wokół serwisu. data/exports/ — przypomnienie, że serwis ma też plikowe życie, a nie tylko REST. Dockerfile i compose.yaml leżą obok nie dla piękna: właśnie one staną się potem kontenerową częścią tego samego projektu.
Ważny jest tu jeszcze jeden moment. Nie czytamy artefaktów Dockera przed czasem, ale już widzimy, że żyją obok kodu. To właściwa intuicja projektowa. Konteneryzacja nie powinna być „osobistą magią autora" gdzieś w osobnym folderze z niezrozumiałymi skryptami. Powinna być częścią tego samego repozytorium co serwis. ⚙️
3. Serwis jest bardzo prosty
Domena projektu została celowo wybrana bardzo spokojna. Mamy katalog elementów i mamy scenariusz eksportu tego katalogu do pliku. Dokładnie tyle, żeby na jednym serwisie zobaczyć naraz kilka ważnych dla kursu wątków: zwykłe API HTTP, stan danych, tryby uruchomienia, system plików i sygnały operacyjne.
Mówiąc prościej, serwis nie robi „wszystkiego na świecie", lecz tylko to, co potrzebne dla tematu kontenerowego. Umie zwracać listę elementów katalogu, wykonywać na nich podstawowe operacje i uruchamiać eksport. Nie ma tu zadania imponowania Ci skomplikowaną logiką biznesową. Wręcz przeciwnie, domena celowo jest utrzymana w prostych granicach, żeby uwaga nie uciekła na bok. 🧪
To ważna część rozumienia. Dobry kurs nie udaje, że konteneryzację trzeba wyjaśniać przez pół backendu, macierz security, trzy mikroserwisy i osiem tabel w bazie. Wystarczy jeden normalny serwis o zrozumiałym zachowaniu. I Container-Ready Catalog Service jest zbudowany właśnie tak.
4. standalone i postgres: dwa tryby
Starter repo ma co najmniej dwa podstawowe tryby, i bardzo ważne jest, żeby zobaczyć je już w pierwszym dniu. standalone służy do szybkiego uruchomienia bez zewnętrznej bazy: dane żyją w pamięci, serwis można podnieść szybko i sprawdzić jego zachowanie HTTP niemal od razu. postgres — to już bardziej realistyczny tryb, gdzie serwis ma zewnętrzną zależność i cały runtime staje się nieco bardziej dorosły.
Kluczowy moment tu polega na tym, że to nie dwie różne aplikacje. Nie pojawia się osobny „docker-serwis" i osobny „lokalny serwis". Kontrolery, logika i ogólny charakter zachowania pozostają te same. Zmienia się właśnie środowisko uruchomienia. I to bardzo ważny nawyk: jeden serwis, różne założenia środowiskowe.
Ta sama idea będzie ciągnąć się przez cały kurs. Jeśli potrzebujesz innego zachowania środowiska, nie powinieneś klonować projektu ani mnożyć drugiej ścieżki kodu. Powinieneś umieć zarządzać runtime jednego i tego samego serwisu. Właśnie tu myślenie kontenerowe zaczyna wyglądać dojrzale, a nie kosmetycznie.
5. Minimalny operacyjny baseline
Bardzo kusi uznawanie serwisu za „żywy" na podstawie jednego faktu: proces nie padł, a w logach mignął napis o starcie. Ale to zbyt słabe kryterium. Dla normalnego baseline potrzebne są przynajmniej dwa sygnały: biznesowy i techniczny.
Sygnał biznesowy odpowiada na pytanie „serwis naprawdę robi to, dla czego istnieje?". Dla naszego starter repo doskonale sprawdza się tu GET /api/catalog/items. Sygnał techniczny odpowiada na pytanie „aplikacja w ogóle się podniosła i może uczciwie poinformować o swoim stanie?". Do tego potrzebny jest GET /actuator/health.
Właśnie ta para daje dobry baseline smoke-check. Jeśli endpoint biznesowy odpowiada, warstwa web i minimum logiki jest żywe. Jeśli odpowiada health, serwis ma przynajmniej podstawowy kontrakt operacyjny. Razem te dwa sprawdzenia dają o wiele uczciwsze poczucie „serwis się podniósł" niż sam fakt, że proces Java jeszcze się nie zakończył.
Na pierwszym poziomie to już wystarczy. Nie potrzebujemy tu złożonych wzorców diagnostycznych ani głębokiego omówienia Actuatora. Na razie warto zobaczyć tylko jedno: żywy serwis to nie po prostu „nie padł", lecz „odpowiada po kluczowych oznakach życia".
6. Plan na dziś 🍹
Teraz można zrobić bardzo małe działanie, które daje pierwszy namacalny wynik bez przeciążenia. Najpierw podnieś starter repo lokalnie w sposób, który jest dla Ciebie już przyzwyczajony w projekcie Spring Boot. Nie potrzebujemy teraz nowego workflow budowania — ważne jest tylko uzyskanie działającego lokalnego baseline.
Potem zrób dwie szybkie weryfikacje. Jeśli masz standardowy port, mogą wyglądać tak:
# Sprawdzamy endpoint biznesowy: serwis naprawdę zwraca dane katalogu
curl http://localhost:8080/api/catalog/items
# Sprawdzamy stan techniczny: Actuator informuje, że aplikacja jest "żywa"
curl http://localhost:8080/actuator/health
Jeśli przywykłeś pracować przez Postman albo pliki .http, możesz skorzystać z requests/ z repozytorium — sens jest ten sam. Pierwsza weryfikacja odpowiada na pytanie „czy żyje trasa biznesowa", druga — „czy serwis widzi siebie jako żywą aplikację".
To właśnie jest pierwszy znaczący wynik dnia. Jeszcze nie budowałeś image, nie pisałeś Dockerfile ani nie podnosiłeś Compose, ale już umiesz patrzeć na starter repo jak na obiekt operacyjny, a nie jak na folder z kodem. I to bardzo dobry start dla kursu o Dockerze.
7. Karta baseline przed konteneryzacją
Poniżej — karta, której można używać nie tylko w tym projekcie, ale i w prawie każdym przyszłym serwisie Boot. Pomaga nie skakać do Dockera przed czasem i szybko utrwalać, co dokładnie będziesz konteneryzować.
| Co zapytać o każdy serwis Boot przed konteneryzacją | Jak to wygląda w Container-Ready Catalog Service |
|---|---|
| Co dokładnie startuje? | Aplikacja Spring Boot z czytelnym punktem wejścia |
| Jaki jest minimalny business smoke-check? | GET /api/catalog/items |
| Jaki jest minimalny technical smoke-check? | GET /actuator/health |
| Jakie tryby uruchomienia trzeba rozróżniać? | standalone i |
| Jakie zewnętrzne założenia już istnieją? | Port, przy postgres — baza, dla eksportu — katalog |
| Gdzie szukać pierwszego operacyjnego potwierdzenia? | Logi startowe + odpowiedzi na oba endpointy |
To wygląda jak prosta tabela, ale w praktyce bardzo mocno oszczędza siły. Gdy tylko potrafisz szybko odpowiedzieć na te pytania, konteneryzacja przestaje być skokiem w ciemność. Masz już baseline, a Docker dalej nie będzie go zastępować, lecz stabilizować.
8. Drabina ewolucji projektu
Teraz najlepsze: można zobaczyć kurs jako jedną ciągłą linię, a nie zbiór tematów. Poniżej — krótka mapa tego, jak będzie dojrzewał ten sam Container-Ready Catalog Service:
| Etap | Co zmienia się w tym samym serwisie | Co zyskujesz |
|---|---|---|
| Teraz | Lokalny baseline Spring Boot + smoke-check | Zrozumiały obiekt konteneryzacji |
| Dalej | Pierwszy image i pierwszy container | Reprodukowalny runtime jednego serwisu |
| Potem | Porządny Dockerfile, cache, multi-stage, warstwy, buildpacks | Profesjonalne pakowanie serwisu Java |
| Następnie | Externalized config, pliki, logi, health, shutdown | Kontrolowane zachowanie jednego image w różnych trybach |
| Po tym | app + PostgreSQL przez Compose | Normalny lokalny stand backendowy |
| Jeszcze dalej | Dodanie Redis i RabbitMQ | Umiejętność życia z zależnościami infrastrukturalnymi bez chaosu |
| Finał | Wielokrotnie używany szablon i umiejętność systematycznej diagnozy problemów | Powtarzalny baseline dla własnych serwisów |
Ten sam serwis przechodzi całą drogę — od lokalnego uruchomienia do dojrzałego kontenerowo szablonu. I dzięki temu każdy kolejny krok nie wydaje się nowym tematem od zera, lecz naturalnym kontynuowaniem już znajomego kontekstu.
Jeśli po pierwszym dniu trzymać w głowie tylko jedną rzecz, niech to będzie to: konteneryzuje się nie Docker dla Dockera, lecz konkretny serwis Spring Boot z zrozumiałym baseline. Jutro ten baseline zacznie już zamieniać się w obiekt inżynierski z własnym cyklem życia, logami i obserwowalnym zachowaniem w środowisku kontenerowym.
9. Typowe błędy 🚧
Błąd nr 1: czytanie starter repo jako zestawu przypadkowych folderów.
Najpierw zawsze potrzebna jest mapa górnego poziomu: gdzie kod, gdzie żądania, gdzie scenariusz plikowy, gdzie przyszłe artefakty kontenerowe. Jeśli od razu zanurkujesz w klasy i pakiety, bardzo łatwo stracić sam dydaktyczny sens projektu.
Błąd nr 2: postrzeganie standalone i postgres jako dwóch różnych aplikacji.
To jeden serwis, a nie dwie równoległe bazy kodu. Różnią się warunki jego życia, a nie jego natura. Ta myśl będzie potem krytycznie ważna dla konfiguracji kontenerowej i Compose.
Błąd nr 3: uznawanie serwisu za „żywy" tylko na podstawie startu procesu.
Proces może się podnieść, a użytecznego zachowania nie będzie. Dlatego minimalny baseline zawsze lepiej jest utrwalać przynajmniej parą żądań: endpoint biznesowy plus endpoint health.
Błąd nr 4: patrzenie na Dockerfile i Compose zanim zrozumiany jest sam serwis.
To bardzo częsta pułapka. Chce się od razu przejść do „interesującej części kontenerowej", ale bez lokalnego baseline zamienia się to w zgadywanie. Najpierw trzeba zrozumieć, co dokładnie konteneryzujesz i po jakich sygnałach rozpoznasz, że serwis żyje.
GO TO FULL VERSION