Projekt TheGame #3 – Budujemy budynki z komponentem BuildingConstruction

gildia-developerow.pl 5 miesięcy temu

Projekt TheGame nabiera rozpędu! Właśnie dodałem nowy komponent i zintegrowałem go z dwoma poprzednimi. Dzieki temu możemy budować nowe budynki, o czym przeczytać możecie w dalszej części wpisu

Misja projektu

Małe przypomnienie dla wszystkich, którzy nie wiedzą, o co chodzi: jestem w trakcie tworzenia projektu gry MMO typu OGame / XWars / SpacePioneers. Główny motyw technologiczny tworzą: język PHP, framework Symfony oraz szeroko pojęte Domain Driven Design. Repozytorium projektu znajdziecie pod linkiem https://github.com/senghe/TheGame. Zachęcam Was serdecznie do gwiazdkowania oraz obserwowania repozytorium.

Nowy komponent i nowe procesy biznesowe

Miał być Doctrine i Messenger, a mamy nowy komponent Dla tych, którzy najpierw patrzą w PRki, link do GitHuba: https://github.com/senghe/TheGame/pull/2. Częstujcie się!

Jeszcze niedawno mieliśmy dwa komponenty: ResourceMine, który służy do wydobywania zasobów oraz ResourceStorage, który steruje wydobytymi już surowcami. Do tego dołączył właśnie nowy komponent: BuildingConstruction, który daje nam nowe procesy biznesowe: możemy budować budynki.

Wejściem do komponentu są komendy. o ile komponent nie udostępnia jakiejś komendy, to znaczy, iż takiego procesu biznesowego nie ma. w tej chwili mamy trzy nowe komendy:

  • StartConstructingCommand – będzie uruchomione, kiedy klikniemy przycisk budowania lub upgradowania budynku na planecie.
  • CancelConstructingCommand – będzie dostępne do kliknięcia wtedy, kiedy budynek będzie w trakcie konstrukcji (a trwa to określony czas), a my zdecydujemy się anulować budowanie.
  • FinishConstructingCommand – komenda, która zostanie wysłana na command busa, kiedy minie czas konstrukcji budynku.

Każda z powyższych komend przedstawia osobny proces, na który składają się takie rzeczy, jak:

  • sprawdzenie, czy mamy wymaganą ilość surowców dostępną do wykonania operacji,
  • weryfikacja, czy budynek na pewno znajduje się w fazie budowania,
  • pobranie surowców z magazynu w razie, kiedy rozpoczynamy budowę budynku,
  • zwrotka surowców do magazynu, kiedy anulujemy konstrukcję,
  • upgrade magazynu (limitu przechowywanych surowców), o ile skonstruowaliśmy kolejny poziom magazynu
  • upgrade ilości wydobywanych surowców, o ile poziom kopalni został podniesiony

Za część tych operacji odpowiada BuildingConstruction, ale jak możecie zauważyć – są to rzeczy, które wymagają wzajemnej integracji komponentów, o czym będzie w dalszej części wypisu.

Riki-tiki – agregaciki

Czymże byłoby DDD bez agregatów Tak się składa, iż mamy już w projekcie trzy agregaty:

  • Building – nowo powstały, malutki (jednoencjowy) agregat, który pilnuje nam stanu konstrukcji budynku,
  • StoragesCollection – odpowiadający wszystkim magazynom, które są dostępne w ramach planety,
  • MinesCollection – służący kontroli wszystkich kopalni wybudowanych na planecie.

Jak na agregaty przystało, cała logika odbywa się dzięki AggregateRoot – rdzenia agregatu. Tylko rdzeń agregatu wie, co i jak jest połączone wewnątrz całego agregatu. Świat zewnętrzny tego nie wie i wiedzieć nie powinien.

Same agregaty to rzecz nieco bardziej złożona niż tylko fasada na logice encji. o ile chcecie przeczytać więcej na ich temat, podrzucam Wam link do mojego wpisu: W poszukiwaniu agregatów w Domain Driven Design.

Nowy komponent – Balance

No dobra, macie mnie. Mamy też czwarty komponent: Balance. Jego ideą jest dostarczenie wszystkich wartości oraz ilości, które pojawiają się w w grze, ale ich wyliczenie nie jest bezpośrednio związane z każdym z istniejących w grze procesów.

Obecnie komponent Balance traktowany jest podobnie jak repozytoria – jako kontrakt, którego implementację odkładam na później. Na chwilę obecną najważniejsze są interfejsy, od których zależne są pozostałe komponenty. w tej chwili mamy dostępne następujące interfejsy:

<?php declare(strict_types=1); namespace TheGame\Application\Component\Balance\Bridge; use TheGame\Application\SharedKernel\Domain\BuildingType; use TheGame\Application\SharedKernel\Domain\ResourceRequirementsInterface; interface BuildingContextInterface { public function getBuildingDuration( int $level, BuildingType $buildingType ): int; public function getResourceRequirements( int $level, BuildingType $buildingType ): ResourceRequirementsInterface; } interface ResourceMinesContextInterface { public function getMiningSpeed(int $level, ResourceIdInterface $resourceId): int; } interface ResourceStoragesContextInterface { public function getLimit(int $level, ResourceIdInterface $resourceId): int; }

Mamy aż trzy interfejsy. Z jednej strony – moglibyśmy zrobić jeden wielki interfejs, bo czemu nie. Z drugiej strony istnieje zasada SOLID – Segregacji Interfejsów, która mówi, abyśmy tworzyli małe interfejsy, w zależności od kontekstu. No i tak tutaj robię. Podział tych interfejsów na chwilę obecną wygląda tak, iż mamy jeden interfejs per komponent, który z niego korzysta. o ile okaże się, iż jeden komponent będzie miał dwa różne konteksty (co nie powinno w sumie mieć miejsca), to wtedy będziem myśleć nad dalszym podziałem.

Coupling, czyli to, jak komponenty ze sobą gadają

Robimy Modularny Monolit, czyli jedną wielką aplikację podzieloną na samodzielne moduły. I tutaj mamy małe kłamstwo, które trzeba sprostować: samodzielne moduły. Każdy moduł musi mieć swoją domenę; swój zbiór pojęć, którymi operuje. Nawet, o ile technicznie może być to takie samo pojęcie, co inne, istniejące w kolejnym komponencie – z perspektywy DDD jest to ciągle osobna domena. Ale każda domena przecina się z innymi domenami. Tego nie unikniemy.

Coupling jest miarą tego, jak bardzo mamy zależne klasy / komponenty w naszej aplikacji. Czasami mówi się, iż coupling jest zły. Czyli, iż powinniśmy mieć całkowicie samodzielne jednostki. No ale jak wtedy upewnić się, czy mamy wystarczającą ilość surowców do zbudowania budynku, lub pobrać surowce z magazynu, kiedy rozpoczniemy budowę? To jest miejsce, gdzie spotykają się dwie domeny: konstruowania budynków oraz magazynowania surowców. No nie ma siły, muszą one ze sobą gadać. Musi być jakiś coupling między nimi.

Coupling jest dobry, o ile kontrolujemy go. Musimy mieć jakieś mechanizmy, po użyciu których będziemy mogli sprawdzić, jak bardzo komponenty zależą od siebie. o ile będziemy to kontrolowali, to będzie nam łatwiej pilnować, aby nie rozrósł się on więcej, niż potrzeba.

DDD, czyli budujemy mosty

W Domain Driven Design istnieją dwa sposoby na kontrolę couplingu, w zależności od strony zależności. Pierwszym z nich jest zależność, kiedy komponent potrzebuje informacji od innego komponentu. Musimy wtedy zawołać serwis innego komponentu. W tym miejscu DDD mówi jasno: o ile dwa komponenty bezpośrednio zależą od siebie, to znaczy, iż należy utworzyć dodatkowy moduł, tzw. most (ang. bridge), który będzie służył jako warstwa komunikacyjna między nimi. Jak możecie się domyślić, im większy coupling, tym więcej dodatkowych komponentów. Mnie się to nie podoba.

Ja chciałbym mieć u siebie trochę bardziej czysto. Dlatego wydzieliłem w każdym komponencie katalog Bridge, do którego wrzucam serwisy, które istnieją tylko po to, aby komunikować się z innymi komponentami. Z jednej strony, jest inaczej niż w klasyce, ale z drugiej strony mamy ten sam efekt: komponenty nie korzystają z bebechów innych komponentów. A o ile będziemy chcieli sprawdzić, jak duży coupling mamy, to wystarczy posprawdzać w PHPStormie, co gdzie korzysta z serwisów z wydzielonego katalogu.

Komunikacja w drugą stronę, czyli nasłuchiwanie zdarzeń

Druga forma komunikacji komponentów ze sobą, to komunikacja reaktywna. o ile coś wydarzy się w jednym komponencie, to my w drugim również chcielibyśmy coś zadziałać. Stąd mamy nasłuchiwanie zdarzeń wzajemnie, między komponentami. o ile budynek rozpoczął budowę, to zostało utworzone konkretne zdarzenie, na które może nasłuchiwać komponent odpowiedzialny za przechowywanie surowców. o ile budynek ukończył swoją konstrukcję, i przy okazji jest to budynek kopalni, to znaczy, iż kopalnia powinna zrobić upgrade. Bez nasłuchiwania zdarzeń na tej samej szynie – chyba nie dałoby radę tego zrobić.

Nowe obraaaaaazki!

Tak, jak w poprzednim wpisie, tak i dzisiaj chciałbym podzielić sie z Wami nowymi obrazkami, które prawdopodobnie wykorzystam w fixturkach gry. Przedstawiam Wam magazyny, czyli kolejno: magazyn do przechowywania metalu, kryształu oraz gazu.

Z takimi obrazkami, to aż chce się robić grę!

Idź do oryginalnego materiału