Artykuły o technologiach IT, programowaniu, testowaniu i nie tylko

DDD w praktyce, cz.1: Value Objects w PHP

DDD to bardzo nośne słowo w kontekście PHP. O ile sama technika nie jest specyficzna dla języka (to raczej sposób rozumowania oraz reprezentacji potrzeb biznesowych w kodzie) o tyle specyficzne są już implementacje pewnych struktur typowych dla DDD.

W tym artykule chcę się skupić na jednym z najbardziej przydatnych elementów w DDD – Value Object. VO możemy rozumieć jako zaawansowaną zmienną, gdyż reprezentuje jakąś wartość. W PHP mamy dostępne kilka bazowych typów zmiennych, przy czym reprezentują one głównie najprostsze typy: numer (Integer), ciąg znaków (String), tablicę (Array) itp.

Założeniem Value Object jest tworzenie obiektów, które posiadają wartość. Co ważne, gdy uda nam się stworzyć taki obiekt to wartość musi być zawsze poprawna (nie możemy utworzyć np. VO daty z nieprawidłową datą). Walidacja takiego obiektu powinna być ograniczana do sprawdzenia poprawności przechowywanej wartości i tylko tego – VO e-maila powinien sprawdzać, czy podany adres e-mail jest poprawny, ale już nie powinien sprawdzać czy e-mail istnieje już w bazie lub np. jest z czarnej listy – takie odpowiedzialności powinny być w domenie obiektu.

Named constructors

Często popełnianym na początku błędem jest tworzenie “inteligentnych” konstruktorów dla VO. Prawdą jest, że konstruktor VO powinien być restrykcyjny co do przyjmowanej wartości i oczekiwać dokładnie takiego typu jaki jest mu potrzebny, bez zbędnych konwersji i parsowania. Dlatego przygotowując np. VO GeoPoint oczekujemy w konstruktorze dokładnie tego czego obiekt potrzebuje, czyli składowych Latitude i Longitude:

Jak można się domyśleć mało wygodne byłoby w locie konstruowanie elementów składowych (konstrukcja z wartości “string” wyglądałaby następująco

Nie jest to najszczęśliwszy zapis, dlatego aby  umożliwić wygodne tworzenie VO w oparciu o inne niż oczekiwane typy możemy zastosować tak zwane nazwane konstruktory. Dokonują one konwersji innych typów (lub wstępnego parsowania). W przypadku GeoPoint możemy przygotować takie przykładowe konstruktory nazwane:

Złożone obiekty

Projektując VO szybko okaże się, że często będą one się składać z kilku mniejszych VO lub rozszerzać jakieś podstawowe typy. Idealnym przykładem złożonego VO jest np. koordynata składająca się z szerokości i długości geograficznej. Za to wspomniana długość i szerokość rozszerzają podstawowy typ zmiennoprzecinkowy o dostępny zakres. W tym miejscu warto zwrócić uwagę, że klasyczne dziedziczenie jest złą praktyką w konkteście Value Object. Nawet jeśli nasz obiekt rozszerza lub grupuje tylko jeden obiekt podrzędny to powinniśmy unikać używania dziedziczenia oferowanego przez język a użyć enkapsulacji, tj. przechowywać instancję podrzędnego obiektu wewnątrz klasy.

Zły przykład:

Dobry przykład:

Takie podejście spowodowane jest tym, że każdy obiekt typu VO powinien stanowić odrębną całość a ponadto dzięki takiej konstrukcji możemy łatwo wstrzykiwać zmockowane obiekty podrzędne na potrzeby testów.

Testowanie Value Objects

Testowanie takich obiektów jest niezwykle wdzięczne, dlatego przy okazji pisania ich warto pomyśleć o stosowaniu techniki TDD (Test Driven Development). W dużym skrócie polega ona na tym, że najpierw piszemy test, który dany obiekt ma spełnić, a dopiero później kod. Jakie to ma plusy? Od razu znamy docelową specyfikację obiektu, dzięki czemu możemy szybko określić jakie mamy oczekiwania wobec obiektu.

Nie wynajduj koła na nowo

Mimo, iż koncepcja VO jest dopiero od niedawna z powodzeniem stosowana w PHP to powstało już sporo repozytoriów oferujących wszystkie typowe obiekty. Należy jednak być ostrożnym, gdyż część z nich to kod, który tak naprawdę nie spełnia założeń jakie stawiane są przed Value Objects.


Nawigacja