- Published on
Czym jest Domain Driven Design (DDD)?
Wpis ten powstał jako próba usystematyzowania mojej wiedzy po przeczytaniu książki „Domain-Driven Design: Tackling Complexity in the Heart of Software” autorstwa Erica Evansa.
Domena i model
Oprogramowanie jest najczęściej tworzone po to, aby rozwiązywać problemy biznesowe pochodzące z domeny świata rzeczywistego. W podejściu Domain-Driven Design musisz od samego początku zrozumieć, że oprogramowanie wywodzi się z tej domeny i jest z nią głęboko powiązane. Mimo że składa się z kodu, nie należy go postrzegać wyłącznie jako zbiór obiektów i metod.
NOTE
W książkach przełożonych na język polski pojęcie „Domain-Driven Design” tłumaczone jest jako „Projektowanie Sterowane Domeną”, „Projektowanie oparte na dziedzinie” lub „Projektowanie Dziedzinowe” i używam ich zamiennie.
Aby stworzyć dobre oprogramowanie, musisz wiedzieć, o co w nim chodzi. Nie możesz zaprojektować systemu bankowego, jeśli nie masz dobrego zrozumienia, jak działa bankowość. Najpierw musisz zrozumieć tę domenę.
Jak sprawić, by oprogramowanie harmonijnie pasowało do domeny? Najlepszym sposobem jest uczynienie go odzwierciedleniem domeny. Musi ono zawierać podstawowe koncepcje i elementy domeny oraz precyzyjnie odwzorowywać relacje między nimi. Inaczej mówiąc, oprogramowanie musi modelować domenę.
NOTE
Domena. Sfera wiedzy, wpływu lub działalności. Obszar tematyczny, do którego użytkownik stosuje program, to domena oprogramowania.
Zaczynamy więc od domeny. Domena jest czymś ze świata rzeczywistego. Aby stała się kodem, musimy stworzyć jej abstrakcję. Wiele dowiadujemy się o domenie podczas rozmów z ekspertami domenowymi. Jednak ta surowa wiedza nie przełoży się łatwo na konstrukcje oprogramowania, jeśli nie zbudujemy właściwej abstrakcji. Na początku będzie ona niekompletna, ale z czasem będziemy ją ulepszać. Czym jest ta abstrakcja? To model domenowy.
NOTE
Model. Zestaw abstrakcji, który opisuje wybrany aspekt domeny i może być użyty do rozwiązania problemu dotyczącego tej domeny.
Według Erica Evansa model domeny nie jest konkretnym diagramem, lecz ideą, którą dany diagram ma przekazać. Model jest istotną częścią projektowania oprogramowania. Potrzebujemy go, aby poradzić sobie z jego złożonością. Gdy zostanie on wyrażony, możemy zacząć projektować kod, stosując różne podejścia, takie jak Waterfall, Agile czy Extreme Programming.
Język wszechobecny
Eksperci domenowi i programiści powinni wymieniać się wiedzą, odkrywając pojęcia z domeny. Może to być proces chaotyczny, niemniej jednak niezbędny do zrozumienia dziedziny. Takie podejście zwykle ma pewne początkowe trudności ze względu na barierę komunikacyjną. Programiści myślą w kategoriach klas, metod, polimorfizmu, OOP, ale eksperci domenowi prawdopodobnie nic o tym nie wiedzą. Znają się za to na swojej specjalizacji. Projekt napotyka problemy, gdy członkowie zespołu nie mają wspólnego języka do omawiania domeny.
Podstawową zasadą DDD jest użycie języka opartego na modelu. Poproś zespół, aby konsekwentnie używał tego języka w kodzie oraz we wszystkich formach komunikacji. Z tego powodu język ten nazywany jest Językiem Wszechobecnym (Ubiquitous Language). Eksperci dziedzinowi powinni sprzeciwiać się terminom, których sami nie rozumieją, ponieważ oznacza to, że coś jest nie tak.
Czego można używać, aby posługiwać się tym językiem? Mowy, diagramów lub pisma. Zalecanym sposobem komunikowania modelu jest wykonanie kilku małych diagramów zawierających podzbiory modelu, czyli klasy i relacje między nimi. Dokumenty te mogą być nawet rysowane ręcznie, ponieważ są tymczasowe i mogą zostać zmienione w niedalekiej przyszłości, zanim model osiągnie bardziej stabilny status. Uważaj na długie dokumenty. Ich napisanie zajmuje dużo czasu i mogą stać się przestarzałe, zanim zostaną ukończone. Możliwa jest również komunikacja za pomocą kodu.
Język Wszechobecny obowiązuje w obrębie jednego Kontekstu Ograniczonego, gdyż pojęcia mogą mieć różne znaczenia. „Lot” w zależności od kontekstu może oznaczać start i lądowanie, inne definicje odnoszą się do konserwacji samolotu lub biletowania przelotów. Ponieważ każda z tych definicji „lotu” jest jasna wyłącznie we własnym kontekście, każda powinna zostać ujęta w osobnym Kontekście Ograniczonym. Ujęcie ich wszystkich w jednym wspólnym Kontekście Ograniczonym doprowadziłoby do pomieszania pojęć.
NOTE
Język Wszechobecny (Ubiquitous Language). Język wypracowany wokół modelu domeny i używany przez wszystkich członków zespołu w obrębie Kontekstu Ograniczonego do komunikacji przy pracy nad oprogramowaniem.
Kontekst ograniczony
Każdy model ma kontekst. Kiedy tworzymy aplikację, która ma współdziałać z inną, starszą aplikacją, jasne jest, że nowa aplikacja ma własny model i kontekst. Nie mogą być łączone, mieszane ani mylone. Kiedy pracujemy nad dużą aplikacją, musimy zdefiniować kontekst dla każdego tworzonego modelu.
Po połączeniu kodu opartego na różnych modelach oprogramowanie staje się niejasne i trudne do zrozumienia. Dlatego postaraj się umieścić w modelu te elementy, które są ze sobą powiązane i tworzą spójną koncepcję. Model powinien być na tyle mały, aby można go było przypisać do jednego zespołu. Trudno utrzymać czysty model, gdy obejmuje on cały projekt firmy, ale jest to znacznie łatwiejsze, gdy ogranicza się do jednego obszaru.
Za posiadanie wielu modeli trzeba zapłacić pewną cenę. Musimy określić granice i relacje między różnymi modelami. Wymaga to dodatkowej pracy. Nie będziemy w stanie przenosić żadnych obiektów między różnymi modelami ani swobodnie wywoływać zachowań, jakby granica nie istniała. Jednak korzyści są tego warte.
Na przykład chcemy stworzyć aplikację e-commerce, która obsługuje zamówienia oraz magazyn. Stworzenie osobnego modelu dla każdej z tych domen sprawi, że mogą one swobodnie ewoluować, a nawet stać się oddzielnymi aplikacjami. Aplikacja magazynu może wysyłać obiekty DTO (Data Transfer Object) zawierające informacje o produkcie do modułu zamówień. Oba modele można rozwijać niezależnie, ale należy zadbać o to, aby interfejs między nimi działał poprawnie.
NOTE
Kontekst Ograniczony (Bounded Context). Ustalony obszar zastosowania danego modelu. Kontekst pozwala członkom zespołu na jednoznaczne zrozumienie zakresu — obszaru, który ma pozostać spójny — oraz na oddzielenie go od tego, co może zostać zaprojektowane w sposób niezależny. Kontekst Ograniczony nie jest modułem. Obejmuje moduł(y) i zapewnia ramy, w których model ewoluuje.

Continuous Integration
Model nie jest w pełni zdefiniowany od początku. Powstaje, a następnie nieustannie ewoluuje w oparciu o informacje zwrotne z procesu rozwoju. Oznacza to, że do modelu mogą wchodzić nowe koncepcje, a do kodu dodawane są kolejne elementy. Dlatego ciągła integracja jest procesem niezbędnym w Ograniczonym Kontekście. Musimy mieć procedurę służącą do scalania kodu. Im szybciej zintegrowany zostanie kod, tym lepiej.
Subdomeny
Domena dzieli się na wiele subdomen (poddziedzin, ang. Subdomains), a każda z nich skupia się na mniejszych problemach. Na przykład w e-commerce, jako domenie, możemy wyróżnić subdomeny: koszyk, płatności czy katalog produktów. W DDD subdomena jest pojęciem relatywnym. Domena i subdomena mogą być używane zamiennie. Kiedy używamy słowa „subdomena”, podkreślamy, że domena, o której mówimy, jest dzieckiem innej domeny wyższego poziomu.
Subdomena, która jest częścią świata rzeczywistego, wdrażana jest jako Kontekst Ograniczony. Subdomena określa „co” produkt miałby osiągnąć i znajduje się w Przestrzeni Problemów (Problem Space). Kontekst Ograniczony określa „jak” produkt miałby to osiągnąć i leży w Przestrzeni Rozwiązań (Solution Space). W jednym Kontekście Ograniczonym powinniśmy zawrzeć jedną subdomenę. W niektórych przypadkach w jednym Kontekście Ograniczonym może znaleźć się wiele subdomen, ale nie jest to najbardziej optymalna postać modelu.
Destylacja
Duża domena ma rozbudowany model nawet po wielu refaktoryzacjach. W takich sytuacjach może nadejść czas na destylację. Ideą jest zdefiniowanie Domeny Głównej (Core Domain), która reprezentuje istotę modelu domeny, nie może zostać zaniedbana i powinni nad nią pracować najlepsi programiści. Produktami ubocznymi destylacji będą subdomeny generyczne (Generic) i pomocnicze (Supporting), które obejmują inne części domeny.
Mapa kontekstów
Duża aplikacja ma wiele modeli, a każdy z nich ma swój kontekst ograniczony. Choć zespoły powinny pracować nad swoimi modelami, dobrze jest, aby każdy miał wyobrażenie o ogólnym obrazie. Mapa Kontekstów (Context Map) to dokument, który przedstawia Konteksty Ograniczone i relacje między nimi. Mapa Kontekstów może być mało szczegółowym diagramem podobnym do tego poniżej.

Ważne, aby wszyscy pracujący nad projektem go podzielali i rozumieli, aby uniknąć nakładania się kontekstów na siebie oraz problemów, gdy system zostanie zintegrowany. Każdy Kontekst Ograniczony powinien mieć nazwę, która jest częścią Języka Wszechobecnego, ponieważ pomaga to w komunikacji, gdy mówi się o całym systemie.
Sposoby interakcji między kontekstami:
Partnerstwo (Partnership)
Każdy zespół odpowiada za własny Kontekst Ograniczony. Mają współzależne cele, więc sukces lub porażkę ponoszą razem.Wspólne Jądro (Shared Kernel)
Dwa (lub więcej) zespoły współdzielą niewielki fragment modelu, ustalając, które elementy będą wspólne.Klient-Dostawca (Customer-Supplier)
Dostawca dominuje, dostarczając to, czego potrzebuje Klient.Konformista (Conformist)
Zespół niższego szczebla dostosowuje się do modelu wyższego, bo nie ma zasobów lub motywacji na własne tłumaczenie.Warstwa Zapobiegająca Uszkodzeniu (Anticorruption Layer)
Tworzy się warstwę tłumaczącą i izolującą model niższego szczebla od modelu wyższego.Usługa Otwartego Hosta (Open Host Service)
Udostępnia protokół lub interfejs, dzięki któremu każdy może bezproblemowo korzystać z usług danego Kontekstu Ograniczonego.Język Opublikowany (Published Language)
Dobrze zdefiniowany i udokumentowany język do wymiany informacji, ułatwiający przekaz pojęć.Oddzielne Drogi (Separate Ways)
Gdy integracja nie przynosi wartości, lepiej stworzyć własne rozwiązanie i zrezygnować z połączenia.Wielka Kula Błota (Big Ball of Mud)
Nieuporządkowana komunikacja we wszystkich kierunkach. Stan, którego należy unikać.

Elementy składowe
Elementy składowe (Building Blocks) to wzorce, które należy stosować w projektowaniu opartym na modelu. Stanowią one część taktyczną DDD.

Czym jest więc DDD w skrócie?
Domain-Driven Design to podejście do wytwarzania oprogramowania, które charakteryzuje:
- Skupienie się na domenie.
- Odkrywanie modelu we współpracy ekspertów domenowych z programistami.
- Posługiwanie się językiem wszechobecnym w granicach kontekstu ograniczonego.
IMPORTANT
Bounded Context oraz Ubiquitous Language to dwa najważniejsze pojęcia do zapamiętania. Stanowią część strategiczną DDD, czyli filozofię stojącą za programowaniem opartym na domenie. Projektowanie strategiczne polega na tworzeniu wstępnego szkicu jeszcze przed przejściem do szczegółów implementacji, czyli projektowania taktycznego. Skuteczne projektowanie taktyczne jest w praktyce niemożliwe, jeżeli nie wychodzi się od projektu strategicznego.
Domain Driven Laravel
Active Record
Można spotkać się z opinią, że Laravel nie nadaje się do Projektowania Sterowanego Domeną. Jak już wiesz zgodnie z DDD w centrum naszych zainteresowań musi być domena. Domena powinna być wyizolowana i "głupia" w stosunku do świata zewnętrznego. ORM Eloquent jest implementacją wzorca Active Record co oznacza, że że łączy dwa światy: domenę i logikę dostępu do baz danych. Active Record otacza wiersz w tabeli bazy danych, hermetyzuje dostęp do bazy danych i dodaje do tych danych logikę domeny. Tak więc AR jest reprezentacją rekordu bazy danych w skali 1:1. Jeżeli jednak nagniemy tę zasadę DDD Laravel pozwoli zrobić nam wiele fajnych rzeczy. W rzeczywistości Laravel i DDD nie wykluczają się całkowicie.
Co dalej?
W kolejnych wpisach pokażę DDD Taktyczne w Laravelu trzymając się zasad:
- Skup się na domenie. Bez tego tracimy istotę DDD.
- Nie walcz z frameworkiem. Walka taka jest wyczerpująca i bezcelowa. Chcemy mieć dostęp do dobroci całego frameworka.
W przeciętnej wielkości projekcie ten rodzaj architekty jako całość jest prawie zawsze przesadą. Nie uda się zastosować "czystego" podejścia DDD. Zamiast tego weźmiemy z niego najlepsze części i połączymy je w pragmatyczny sposób.