Aplikacja do zarządzania projektami w Kotlinie

Praktyczny przykład w Kotlinie

Featured image

Ostatnimi czasy bardzo zainteresowały mnie tematy DDD, TDD/BDD oraz architektur. Jest to takie uzupełnienie układanki jaką próbowałem sobie ułożyć w głowie. Dają one kilka fajnych rzeczy dzięki, którym jest łatwiej zrozumieć czyiś kod oraz samemu pisać lepszy. Oczywiście nie jestem jakimś guru w tym temacie. Prawdopodobnie popełnię kilka gaf podczas tworzenia projektu, ale właśnie o to chodzi w tej serii. Tak, żeby stworzyć/odtworzyć cały ten proces. Aż do efektu końcowego. W tym wpisie znajdziesz co chcemy zrobić, a w kolejnych wpisach omówię krok po kroku implementację.

Parę słów na wstęp

Najczęściej powtarzaną radą na początku jaką słyszałem było, aby pisać jak najwięcej. Faktycznie jest to najlepsza metoda na naukę programowania. W ten sposób nauczysz się najwięcej. To jest, aż tak łatwe. Przez podstawy jest dość łatwo przejść, ale co potem? Co w momencie kiedy trzeba coś zrobić dobrze, a nie tylko, że działa? Zamiast tworzyć potwora spaghetti wystarczy usiąść chwilkę i przemyśleć kilka koncepcji. Z pomocą przychodzi tu cała masa wzorców, idei, paradygmatów, metodyk, praktyk. Jest tego po prostu cała masa. Łatwo jest się w tym pogubić. Dlatego postanowiłem stworzyć ten projekt, w którym będę inkrementalnie dodawać/refaktorować nowe rzeczy. Poruszymy takie tematy jak DDD ( Domain-Driven-Design), TDD/BDD (Test/Behavior Driven Development) oraz czasami wrzucimy tu i ówdzie jakiś wzorzec - jeśli będzie akurat pasował. Takie tematy to lata doświadczenia. Postaramy się trochę zhakować ten czas wzorując się na istniejących ideach/przykładach oraz wiedzy ludzi, którzy przeszli już tą ścieżkę. W ten sposób uda nam się szybciej dojść do celu poznając po drodze wszystkie fajne koncepty. Myślę, że jest to spory krok, aby zostać lepszym programistą. W miarę poszerzania wiedzy z tych zakresów mam nadzieję, że projekt będzie ewoluować co w końcowym efekcie może dać całkiem fajny efekt. Obecny plan na aplikację znajdziesz poniżej - mam nadzieję, że w miarę upływu czasu zacznie on jeszcze bardziej ewoluować. Wszelkie konstruktywne uwagi mile widziane. Bez zbędnego przedłużania bierzemy się do roboty!

Co zrobimy?

Znasz Trello? Zrobimy coś podobnego. Opiszę krok po kroku tworzenie tej aplikacji. Jeśli już potrafisz trochę programować to pewnie masz za sobą pierwszą to-do-listę. Ta będzie trochę bardziej zaawansowana, a przynajmniej mam taką nadzieję.

Częściowo ukończony projekt znajdziesz na Githubie tutaj.

Co/gdzie? W czym?

TDD/BDD - w TDD chodzi o pisanie testów przed logiką domenową. Zaś w BDD chodzi, żeby te testy jak najlepiej oddawały problem biznesowy. Ładnie opisane testy w takiej formie, że nawet osoba nie-techniczna mogłaby z łatwością dowiedzieć się co robi przypadek testowy. Pozwalając tym samym na zrozumienie działania aplikacji.

DDD - czyli takie modelowanie aplikacji na podstawie domeny w jakiej pracujemy (np. e-commerce). Skupiamy się tutaj na domenie, czyli rdzeniu naszej aplikacji. Można powiedzieć, że mapujemy wymagania od biznesu na kod. Jest to część strategiczna DDD. Druga część to taktyczna, czyli to w jaki sposób implementujemy różne rzeczy. Bierze odpowiedzialność za rzeczy z bounded-contextu. Wszystkie te rzeczy takie jak services, factories, repositories, entities wywodzą się właśnie z DDD.

Pierwsza wersja

W architekturze warstwowej (aka. layered architecture) - prawdopodobnie najbardziej popularna. Często domyślny wybór podczas pisania aplikacji. Po jej poznaniu łatwiej jest się uczyć kolejnych.

Druga wersja

W heksagonalnej z prostym CQRSem, którą to osobiście bardziej lubię. Pozwala według mnie w łatwiejszy sposób zamknąć odpowiedzialność wewnątrz modułu (bounded contexu) oraz wystawić na świat tylko to co nas interesuje. Wyobraź sobie miasto z dużą ilością budynków. W całym tym tłoku jest tylko jeden wielki wieżowiec, który jest widoczny nawet z kosmosu. Chcielibyśmy właśnie, żeby ten bounded contex miał takie skyscraperFacade co wystawia wszystkie metody potrzebne do modyfikacji obiektów. Nie można po prostu wjechać do miasta. Trzeba polecieć balonem, albo helikopterem do wieżowca i tam dopiero przez odpowiednie tunele przejść do innych części miasta. Do tego łatwiej jest powiedzieć co jest tutaj unitem w testach jednostkowych.

Trzecia wersja

Dodana do mikroserwisowego ZOO. Na szczęście spring ma te wszystkie funkcjonalności w podstawowej formie zaimplementowane. Dzięki czemu wiele z tych rzeczy ogranicza się do stworzenia adnotacji. Myślę, że jest to w miarę prosta architektura na początek. Te mikroserwisy nazywają się backing-services. Takie wspomagacze do działania całego ekosystemu.

Nasze zoo można ponazywać w następujący sposób:

user-autorization-service - furtka do zasobów dostępnych tylko dla zalogowanych użytkowników. Zasoby dzielą się na protected oraz unprotected. Lista produktów jest przykładem zasobu jaki możemy pobrać bez logowania się do strony. Dostępne zazwyczaj w formie read-only. Z drugiej strony są zasoby, do których mamy dostęp tylko po zalogowaniu. Chociażby informacje o użytkowniku/ach.

edge-service - łączy requesty z frontu do backendu poprzez strasznie brzmiącą rzecz - reverse-proxy. W skrócie unifikuje zasoby z różnych backendów do jednego wspólnego REST API. Zazwyczaj na całą architekturę mikroserwisów składa się wiele, wiele backendów. Każdy z nich może wystawiać różne rzeczy. Czasami publiczne, czasami nie. Chodzi tutaj o to, aby powiedzieć co dokładnie chcemy, aby było publiczne.

discovery-service - w całym mikroserwisowym zoo jest bardzo duża ilość aplikacji. U nas będzie jedna, dwie niemniej w prawdziwym świecie jest tego kilkadziesiąt, kilkaset, a nawet więcej. Trzeba jakoś trzymać pieczę nad tym wszystkim. Z pomocą przychodzi bardzo prosty koncept.

centralized-configuration-server - globalne konfiguracje dla docelowego środowiska. Jest tutaj config-server oraz jakiś client. Załóżmy, że client jest to prosta Springowa aplikacja typu HelloWorld. W momencie kiedy jej serwer HTTP startuje zasysa ona domyślną konfigurację z config-servera. Oczywiście tylko i wyłącznie jeśli ten serwer jest uruchomiony. Można też dynamicznie wstrzykiwać/zmieniać configi podczas runtime.

load-balancer- rozdziela ruch na instancje naszej aplikacji. Dodatkowo może pingować do tych instancji, czy aby na pewno nadal żyją (health check).

Memcached, Redis, BigCache - słowem coś do cache’owania (najlepiej in-memory, bo RAM jest szybszy niż najlepszy SSD). Chodzi generalnie o to, aby requesty dostawały zasoby jeszcze szybciej. Dlatego pomijamy bazę danych i serwujemy niektóre dane prosto z RAMu - bardzo upraszczając, bo nie wszystkie rozwiązania działają w ten sposób.

Co jeszcze można wymyślić?

Endpoints (jednocześnie bounded contexts)

Team

Projects

Częściowo ukończony projekt znajdziesz na Githubie tutaj.

API

To co wchodzi do naszej aplikacji DTOsy. Brak tu logiki - czyste requesty.

APPLICATION

Taki mediator pomiędzy domeną, a infrastrukturą. Broni modele z domeny przed zewnętrznymi rzeczami. Tylko ta warstwa oddziaływuje bezpośrednio na Domenę. Przykładowy ApplicationService nie powinien mieć żadnej logiki biznesowej. Wie tylko jaki model użyć, ale nie wie jak model działa. Wie jaki agregat wywołać oraz potencjalnie wie troszczkę o domenowych serwisach - tylko tyle.

DOMAIN

Praktycznie najważniejsza część naszej aplikacji. Tutaj znajduje się rozwiązanie real-life problemu, który dał nam biznes. Zaimplementowane różne zachowania modelu oraz walidacje. Po prostu serce, rdzeń naszej aplikacji. Mamy tutaj nasze agregaty oraz serwisy domenowe.

PERSISTANCE

Ostatni przystanek. Mapujemy modele z domeny do tej warstwy. Tutaj jest powiedziane dokładnie jak nasze dane będą przechowywane. Abstrakcyjnym przykładem mogłoby być, że lista ficzerów z domeny (słowo klucz: lista) byłyby przechowywane w formie zwykłego stringa, który oddziela je przecinkiem. Takie zachowanie raczej nie ma to większego sensu niemniej to tylko przykład.

INFRASTRUCTURE

Tutaj można wrzucić klienta do zewnętrznego API (np. pogody, kalendarz).