CodeGym /Kursy /Spring REST & MVC /Świetny projekt przewodni Task Tracker API

Świetny projekt przewodni Task Tracker API

Spring REST & MVC
Poziom 1, Lekcja 2
Dostępny

1. Ten kurs potrzebuje własnego projektu

Projekt przewodni w naszym kursie rozwiązuje kilka ważnych zadań naraz. Po pierwsze, redukuje szum poznawczy. Zamiast za każdym razem rozgryzać, jakie tu są encje, jak są powiązane i po co w ogóle istnieją, raz wchodzisz do domeny i dalej pracujesz już z ideami API.

Po drugie, projekt przewodni daje poczucie narastającego rezultatu. Jeśli kurs składa się z niepowiązanych mini-dem, trudno poczuć postęp: widzisz wiele różnych fragmentów, ale nie widzisz jednego obiektu, który naprawdę stał się silniejszy. Tu jest odwrotnie: jedno API stopniowo przechodzi drogę od pomysłu i mapy zasobów do DTO, walidacji, kontraktu błędów, wzorców pracy z listami, endpointów plikowych, dokumentacji i testów webowych.

Po trzecie, jeden projekt czyni kurs bardziej uczciwą historią inżynierską. W prawdziwej pracy backend-deweloper prawie nigdy nie pisze „dziś jeden endpoint, jutro o nim zapomnimy i zaczniemy inny projekt". Prawdziwa praca to rozwijanie już istniejącego API. Dodawanie nowych scenariuszy. Utrzymanie wstecznej kompatybilności. Dopasowywanie nowych decyzji do już istniejącego kontraktu.

Dlatego Task Tracker API to nie „przykład dla przykładu", lecz centralny wątek kursu. Wszystkie duże tematy będą do niego regularnie wracać. I to bardzo przydatne: ten sam obiekt zobaczysz najpierw jako domenę, potem jako zestaw zasobów, potem jako kontrakt JSON, potem jako obiekt walidacji, potem jako źródło błędów biznesowych, a wreszcie — jako przedmiot testowania.

2. Task Tracker — trafny kompromis ⚖️

Domena zadań jest niemal idealna dla kursu o projektowaniu REST API. Jest wystarczająco znajoma, żeby nie tracić czasu na tłumaczenie „co tu się w ogóle dzieje", i wystarczająco bogata, żeby pokazać wiele rzeczywistych scenariuszy. Niemal każdy deweloper widział zadania w Jira, Trello, Notion, Asana albo choćby w osobistej liście to-do.

Przy tym zadanie to nie „jeden wiersz tekstu" 📝. Naturalnie pojawiają się przy nim różne typy danych i różne rodzaje zachowania. Jest tytuł i opis. Jest status i priorytet. Jest termin. Są tagi. Są komentarze. Są załączniki. Jest moment utworzenia i aktualizacji. Na jednym takim obiekcie można już pokazać wiele rzeczy, które w prawdziwym API okazują się kluczowe: jakie pola są publiczne, jakie obowiązkowe, jakie zarządzane przez serwer, jak wygląda list-view i detail-view, jak projektować update semantics.

Gdybyśmy wybrali zbyt prostą domenę, kurs szybko trafiłby w sufit. Nie byłoby na czym pokazać list patterns, file upload, business transitions, subresources i porządnego modelu błędów. Gdybyśmy wybrali zbyt ciężką domenę, kurs utopiłby się w złożoności biznesowej. Dlatego Task Tracker jest dobry właśnie jako kompromis: jest wystarczająco żywy, ale nie wymaga zamieniania projektu w mini-Jira.

I jeszcze jeden plus: ta domena dobrze przenosi się na komercyjny development. Nawet jeśli potem będziesz pisać nie task tracker, lecz API wniosków, zgłoszeń, zamówień, case'ów supportu albo wewnętrznych workflow — większość decyzji kontraktowych okaże się bardzo podobna.

3. Centralny obiekt projektu: Task i jego robocza forma

Sercem projektu jest zadanie. I właśnie dlatego, że zadanie jest centralna, musi być wystarczająco treściwa, żeby nieść przyszłe pytania API 🔍. Tu warto spojrzeć nie tylko na nazwy pól, ale i na to, po co w ogóle istnieją z punktu widzenia kursu.

Mniej więcej tak można myśleć o wewnętrznej roboczej formie zadania:

// Status — klucz do dyskusji o przejściach stanów i konfliktach (409 itp.)
enum TaskStatus {
    TODO,        // zadanie utworzone, ale jeszcze nie wzięte do pracy
    IN_PROGRESS, // w trakcie realizacji
    BLOCKED,     // jest bloker, nie można iść dalej
    DONE,        // zakończone
    ARCHIVED     // "schowane z oczu": istnieje, ale zazwyczaj nie jest edytowane
}

// Priorytet — wygodna oś do sortowania/filtrowania i reguł biznesowych
enum TaskPriority {
    LOW,      // można poczekać
    MEDIUM,   // normalny poziom
    HIGH,     // ważne
    CRITICAL  // pilne, wpływa na działanie systemu/zespołu
}

class Task {
    String id;                // serwerowy identyfikator zasobu (adresowanie /tasks/{id})
    String title;             // krótki tytuł: prawie zawsze obowiązkowy → walidacja
    String description;       // szczegóły: często opcjonalne, ale ważne dla UX
    TaskStatus status;        // stan: od niego zależą dozwolone operacje
    TaskPriority priority;    // priorytet: wpływa na sortowanie i scenariusze podobne do SLA
    LocalDate dueDate;        // termin: przydatny do filtrów "przeterminowane/w tym tygodniu"
    String assigneeName;      // realizator: uproszczone (bez osobnej encji User)
    List<String> tags;        // tagi: lekki model "value-like" do wyszukiwania/filtrowania
    Instant createdAt;        // pole zarządzane przez serwer: ustawiane przez serwer przy tworzeniu
    Instant updatedAt;        // pole zarządzane przez serwer: aktualizowane przez serwer przy zmianach
}

Już ten krótki fragment jest bardzo przydatny dla kursu. id od razu podnosi kwestię zasobu i adresowania. title i description prowadzą do walidacji wejścia i modeli JSON. status pozwala dyskutować o przejściach stanów i scenariuszach konfliktów. priority, dueDate, assigneeName i tags dają materiał do filtrowania, sortowania i wyszukiwania. createdAt i updatedAt przypominają, że serwer sam zarządza częścią danych.

Żeby lepiej to poczuć, warto spojrzeć na pola nie jako na „listę atrybutów", lecz jako na przyszłe tematy kursu:

Pole zadania Co wnosi do kursu
id adresowanie zasobu i budowę detail-endpointów
title /
description
request DTO, walidację i formę odpowiedzi
status semantykę aktualizacji, przejścia stanów i 409 Conflict
priority /
dueDate
sortowanie, filtrowanie i zachowanie listy
tags wyszukiwanie, filtrowanie i lekkie scenariusze lookup
createdAt /
updatedAt
pola zarządzane przez serwer i dyscyplinę publicznego modelu

Oto co ważne tu zobaczyć. Świadomie wybieramy nie abstrakcyjną „Entity with id and name", lecz obiekt, który naturalnie wywołuje pytania API. To sprawia, że projekt jest żywym nośnikiem kursu, a nie tylko tłem dla adnotacji.

4. Komentarze, tagi, załączniki i statusy 🔗

Gdyby w projekcie była tylko encja Task, kurs byłby uboższy. Dlatego obok Task są powiązane obiekty, które nie rozbudowują domeny, ale znacznie podwyższają wartość edukacyjną 💪.

Komentarze są potrzebne, żeby w projekcie pojawił się normalny pod-zasób. To bardzo życiowy scenariusz. Zadanie istnieje samo w sobie, a komentarze do niej należą i żyją obok. Pomaga to później bardzo naturalnie mówić o subresources, zachowaniu list/detail i o tym, jak nie zamieniać wszystkiego w niepowiązany zestaw RPC.

Mniej więcej tak można myśleć o komentarzu:

class Comment {
    String id;           // identyfikator komentarza (zazwyczaj unikalny w całym API)
    String taskId;       // referencja do zadania nadrzędnej: pod-zasób /tasks/{taskId}/comments
    String authorName;   // kto napisał (w kursie — prosty string bez encji User)
    String text;         // tekst komentarza: doskonały kandydat do walidacji (nie pusty, limity)
    Instant createdAt;   // zarządzane przez serwer: czas utworzenia jest ustalany po stronie serwera
}

Tagi są potrzebne nie dlatego, że „tak modnie w trackerach zadań", lecz dlatego, że doskonale nadają się do scenariuszy filtrowania i lookup. Przy tym w tym kursie tagi nie powinny zamieniać się w osobną dużą encję z własnym rozbudowanym CRUD. Tu są value-like, lekkie i użyteczne.

Załączniki są potrzebne, bo scenariusze pracy z plikami to normalna część pracy serwera, a nie rzadka egzotyka 📎. Przez nie kurs zyskuje bardzo naturalny powód, żeby mówić o multipart upload, pobieraniu plików, rozdziale metadata/content i konfiguracji lokalnego storage — bez wchodzenia w chmurę i ciężką infrastrukturę.

Statusy dają bardzo przydatną warstwę zachowania 🚦. Dopóki zadanie po prostu przechowuje tekst, API łatwo sprowadza się do banalnego CRUD. Ale gdy zadanie ma stan, pojawia się dojrzalszy temat: nie każda zmiana jest jednakowo uzasadniona. Archiwizacja zadania, blokowanie, kończenie, powrót do pracy — to wszystko pomaga później mówić nie tylko o polach, lecz o biznesowym sensie operacji.

5. Ta domena pokrywa niemal cały kurs

Dobry projekt przewodni to nie tylko „zawiera dużo encji". Jest tak skonstruowany, żeby różne kawałki domeny naturalnie wspierały różne tematy kursu. Task Tracker API jest właśnie taki.

Kawałek projektu Jaki temat kursu pomaga rozwinąć
tasks
jako główny zasób
bazowy CRUD i myślenie REST
GET /tasks
list-endpointy, paginację, sortowanie i filtrowanie
status semantykę aktualizacji, konflikty biznesowe i obsługę błędów
comments
subresources i zależne kolekcje
attachments
upload przez multipart, pobieranie plików i rozdział metadata/content
tags
filtrowanie, wyszukiwanie i lekkie scenariusze lookup
title,
description
,
authorName
walidację i wejściowy kontrakt
wszystkie modele odpowiedzi razem DTO, Jackson i dyscyplinę formy odpowiedzi
negative-path dla brakujących danych i konfliktów ProblemDetail i jednolity kontrakt błędów
gotowa warstwa webowa OpenAPI i @WebMvcTest

To bardzo ważna tabela. Pokazuje, że kurs nie składa się ze 150 przypadkowych lektur. Naprawdę buduje jedno API warstwa po warstwie. A Task Tracker API jest tu potrzebny nie jako „ciekawy wątek", lecz jako dobrze dobrana konstrukcja nośna.

Zwróć też uwagę na coś innego. Nie dodawaliśmy do projektu sztucznych encji tylko po to, żeby „zamknąć program". Wręcz przeciwnie — prawie wszystko wygląda naturalnie. Zadania mają komentarze. Do zadań dołącza się pliki. Zadania filtruje się po statusie, priorytecie, terminie i tagach. Zadania przechodzą z jednego stanu do drugiego. To całkowicie normalne życie zwykłego backend API.

6. Jak projekt wygląda oczami klienta

Do tej pory mówiliśmy o domenie. Ale już teraz warto lekko obrócić go na zewnątrz — nie w stronę pełnego tematu URI design, lecz w stronę tego, jak widzi go klient. Z zewnątrz Task Tracker API nie będzie odczuwany jako zestaw klas Task, Comment i AttachmentMetadata, lecz jako zestaw zasobów i powiązanych scenariuszy.

Na najbardziej ogólnym poziomie klient oczekuje, że mapa zasobów API wygląda mniej więcej tak:

# Podstawowa kolekcja zadań (list/create)
 /api/v1/tasks
# Konkretna zadanie (detail/update/delete)
 /api/v1/tasks/{taskId}
# Pod-zasób: komentarze konkretnej zadania (list/create)
 /api/v1/tasks/{taskId}/comments
# Pod-zasób: załączniki konkretnej zadania (upload/list/download)
 /api/v1/tasks/{taskId}/attachments
# Lekki lookup/słownik tagów (jeśli robimy osobnym endpointem)
 /api/v1/tags

To jeszcze nie finalna mapa endpointów ani nie rozmowa o wszystkich zasadach URI design. Ale już z tych linii widać coś ważnego: z zewnątrz projekt skupia się wokół zasobu task, a pozostałe części żyją wokół niego jako obiekty wspierające.

Żeby lepiej to poczuć, wystarczy spojrzeć na to, jak może wyglądać jedno zadanie w JSON:

{
  // Identyfikator: zazwyczaj nadawany przez serwer i używany w URL
  "id": "t-42",
  // Czytelny dla człowieka tytuł: kluczowe pole do walidacji
  "title": "Fix validation error format",
  // Opis: pomaga pokazać różnicę między detail-view a list-view
  "description": "Return a consistent error payload for invalid task input",
  // Status: podstawa do dyskusji o przejściach i konfliktach
  "status": "IN_PROGRESS",
  // Priorytet: typowe pole do sortowania
  "priority": "HIGH",
  // Termin: dobry przykład daty (bez czasu) i filtrów po okresach
  "dueDate": "2026-04-01",
  // Realizator: w kursie — string, żeby nie ciągnąć osobnej domeny user
  "assigneeName": "Alex",
  // Tagi: lekki model do wyszukiwania/filtrowania (i do demonstracji tablic)
  "tags": ["api", "validation"]
}

A komentarz może wyglądać tak:

{
  // Identyfikator komentarza
  "id": "c-10",
  // Referencja do zadania nadrzędnej (ważne dla pod-zasobów i spójności)
  "taskId": "t-42",
  // Autor (uproszczone stringiem)
  "authorName": "Maria",
  // Tekst komentarza (świetne miejsce dla reguł: nie pusty, limit długości)
  "text": "I reproduced it with empty title",
  // Czas utworzenia: zarządzane przez serwer, przydatne do sortowania chronologicznego
  "createdAt": "2026-03-21T11:10:00Z"
}

I to już wystarczy, żeby zobaczyć czerwoną nić kursu. Nie będziemy każdego dnia zmieniać świata. Będziemy brać to samo API i robić je coraz dojrzalszym oczami klienta.

7. Typowe błędy przy ocenie projektu edukacyjnego 🚧

Błąd №1: postrzegać projekt edukacyjny jako „zbyt prosty, więc niepoważny".
O powadze projektu edukacyjnego nie decyduje liczba encji, lecz to, jak uczciwie niesie potrzebne pytania inżynierskie. Task Tracker API ma wystarczająco dużo materiału na porządny REST API design bez zbędnej złożoności domenowej.

Błąd №2: próbować zamienić projekt w mini-Jira.
Bardzo szybko pojawia się pokusa dodania użytkowników, zespołów, tablic, epics, historii zmian, powiadomień i jeszcze dwudziestu encji. To prawie zawsze niszczy fokus edukacyjny. Projekt ma być nie „większy", lecz lepiej dobrany.

Błąd №3: nie widzieć związku między domeną a tematami kursu.
Jeśli patrzeć na Task, Comment czy Attachment po prostu jako na nazwy klas, projekt wygląda zwyczajnie. Ale gdy zauważysz, jakie wzorce API ze sobą niosą, staje się widoczne, dlaczego domena jest dobrana właśnie tak.

Błąd №4: myśleć, że projekt przewodni to tylko tło.
Tak naprawdę to jedna z głównych podpór edukacyjnych kursu. Jest potrzebny, żeby każda nowa warstwa jakości nie wisiała w próżni, lecz kładła się na już znany system.

1
Zadanie
Spring REST & MVC, poziom 1, lekcja 2
Niedostępne
Domenowy model zadania
Domenowy model zadania
1
Zadanie
Spring REST & MVC, poziom 1, lekcja 2
Niedostępne
Komentarze i metadane zalacznika obok zadania
Komentarze i metadane zalacznika obok zadania
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION