Od ostatniego wpisu dotyczącego aplikacji AnimalShelter minęło ponad 2 miesiące. Spowodowane było to tym, że miałem trochę innych rzeczy na głowie. Jednak udało mi się z powrotem przysiąść do jej implementacji. Dorobiłem mechanizm wysyłki maili, jednak nie na tym chciałem się skupić w tym artykule. Pojawiało się u mnie ponownie sporo rozterek architektonicznych, które finalnie udało mi się rozstrzygnąć (przynajmniej mam taką nadzieję). Zapraszam Cię, więc do lektury podsumowania moich aktualnych działań.
Lista wszystkich wpisów dotyczących projektu AnimalShelter:
#1 - Opis projektu AnimalShelter
#2 - Pierwsze kroki w backendzie
#3 - Refactoring i prace rozwojowe części serwerowej
#4 - Tworzenie GUI w Angularze
#5 - Zatrzymaj się, przemyśl i zacznij działać!
#6 - Pomysł na architekturę
#7 - Wykorzystanie CQRS
#8 - Ponowna implementacja
#9 - Rozterki architektoniczne
#10 - Podsumowanie + implementacja wysyłki maili
#11 - Programowania ciąg dalszy
#12 - Dopinanie zadań do końca
Architektura rozwiązania
Kolejka jako port
Po ponownym zapoznaniu się z artykułem “DDD, Hexagonal, Onion, Clean, CQRS, … How I put it all together” Herberto Graça z 2017r. otworzyły mi się oczy na kilka kwestii. Pierwszą z nich jest fakt, że portem może być również interfejs do kolejkowania, czyli Command Bus
albo Query Bus
.
Alternatively, a Port can be a Command Bus or Query Bus interface. In this case, a concrete implementation of the Command or Query Bus is injected into the Controller, who then constructs a Command or Query and passes it to the relevant Bus.
Herberto Graça
Dzięki temu utwierdziłem się już na 100% z tym co przedstawiłem w siódmej części tej serii.
Odwrócenie zależności
Logika biznesowa powinna zależeć tylko od portu, czyli w naszym przypadku, od interfejsu. To adaptery są od tego, aby spełnić wymagania wystawionego portu i dostarczenia konkretnego rozwiązania np. połączenia z bazą danych MySQL.
A characteristic to note about this pattern is that the adapters depend on a specific tool and a specific port (by implementing an interface). But our business logic only depends on the port (interface), which is designed to fit the business logic needs, so it doesn’t depend on a specific adapter or tool.
Herberto Graça
Dlatego właśnie w AnimalShelter mam dwa główne moduły: application
i webservice
. Ten pierwszy zawiera logikę biznesową, a drugi łączy nas ze światem zewnętrznym.
Tak prezentuje się pakiet pl.devcezz.animalshelter.shelter
w module application
. Długo zastanawiałem się nad tym czy np. interfejs Animals
oraz ShelterRepository
powinien być publiczny. Doszedłem do wniosku, że niekoniecznie. Po prostu w module webservice
będzie istniał ten sam pakiet z klasą, która je zaimplementuje. Dzięki temu domena jest “czysta” od frameworków, a ja mogę dostarczyć rozwiązanie zależne od konkretnej bazy danych.
Komendy, zdarzenia i wyjątki są do dyspozycji wszystkich. Jeżeli chcę zmienić stan systemu to wysyłam komendę na szynę i tyle. Traktuję swoją aplikację jak czarną skrzynkę, która może wyemitować zdarzenie lub rzucić wyjątek jeżeli coś pójdzie nie tak. Każdy handler jak np. AcceptingAnimal
ma dostęp pakietowy, ale może zostać bez problemu zarejestrowany jako bean w module webservice
. Jest to możliwe dzięki temu o czym napisałem wcześniej.
W ten właśnie sposób będę się starał projektować system.
Klasy modelowe
Długo zastanawiałem się czy takie klasy jak Animal
, Species
czy AnimalId
powinny być dostępne publicznie, aby można je było użyć w zdarzeniach. Moją główną obawą było to czy jak stworzę zdarzenie składające się z samych typów prymitywnych to czy tworząc taki obiekt na pewno przypiszę odpowiednie wartości do danych pól. Obawa jednak nie jest uzasadniona, bo prędzej czy później to wyjdzie np. w testach jednostkowych, które możemy napisać sobie na boku. Z tego właśnie powodu zostawiłem te klasy z dostępem pakietowym. Weryfikację czy został zapisany prawidłowy zwierzak do bazy danych zostawiam pakietowi shelter
, światu zewnętrznemu przekazuję natomiast tylko wartości.
Podsumowanie
To chyba wszystkie obawy jakie spotkałem na ten moment, które wydaje mi się, że w jakimś stopniu wyprostowałem. Aktualnie biorę się za refaktoryzację rozwiązania do wysyłki maili, aby również spełniał te założenia, które tutaj przedstawiłem. Prosiłbym Ciebie o komentarz czy to rozwiązanie jest według Ciebie pomysłowe czy ma jakieś mankamenty, o których nie pomyślałem? 🙂 Obstawiam, że na pewno jest przekombinowane jeśli chodzi o złożoność problemu do rozwiązania, ale chciałem nauczyć się czegoś co nie przypomina architektury warstwowej.
Link do GitHuba: https://github.com/cezarysanecki/animal-shelter