9 listopada 2008
Odsłon: 1425
W poprzedniej części zapowiedziałem, że przy tworzeniu aplikacji zgodnie z Domain-Driven Design będziemy stosować w maksymalnym stopniu zasady programowania zorientowanego obiektowo oraz izolować logikę biznesową od infrastruktury. Jednym z ważniejszych kroków w stronę osiągnięcia tego celu jest prawidłowe zdefiniowanie architektury systemu, czyli umiejętne wydzielenie warstw i określenie ich odpowiedzialności.
Gdy mówimy o warstwach, tak naprawdę możemy mieć na myśli dwie rzeczy: wartswy fizyczne (ang. tiers) i warstwy logiczne (layers). Dla pełnego zrozumienia problemów, jakie wiążą się z projektowaniem aplikacji biznesowych, niezbędne jest poprawne rozróżnienie tych terminów.
Warstwy fizyczne
Podział fizyczny wynika z dążenia do zapewnienia maksymalnej wydajności i skalowalności oprogramowania. Ze względu na metody podziału, możemy wyróżnić kilka najważniejszych typów systemów:
- Systemy desktopowe. Aplikacje działające w całości na maszynie klienta.
- Systemy klient-serwer. Nieco bardziej złożone, zgodnie z nazwą dzielą się na dwie warstwy fizyczne. Aplikacja kliencka odwołuje się do zasobów na serwerze bazodanowym, który funkcjonuje jako repozytorium danych, ale też umożliwia delegowanie do niego części operacji, które są realizowane np. poprzez procedury składowane.
- Systemy n-warstwowe. Gdy liczba i złożoność operacji delegowanych przez klienta do serwera bazodanowego rośnie do tego stopnia, że utrudnia mu spełnianie swojej podstawowej funkcji - udostępniania i składowania danych, wówczas wprowadza się dodatkową warstwę pośrednią, która ma za zadanie realizację części tych delegowanych operacji. Inne powody wprowadzania warstw pośrednich mogą być związane z konstrukcją aplikacji internetowych (serwery WWW), implementacją connection pooling, mechanizmu cache-owania danych itp.
- Systemy rozproszone. W niektórych sytuacjach obciążenie może być na tyle duże, że w ramach poszczególnych warstw potrzebne jest wprowadzenie większej liczby maszyn obsługujących żądania i współdziałających w realizacji odpowiedzialności danej warstwy. Wiąże się to z licznymi dodatkowymi problemami, jak choćby utrudniona komunikacja pomiędzy warstwami, ale za to znacząco zwiększa skalowalność aplikacji.
Warstwy logiczne
Żeby zrealizować podział na warstwy fizyczne, trzeba rozwiązać szereg problemów od strony programowej. Po pierwsze, kod aplikacji musi zostać odpowiednio podzielony, tak aby poszczególne jego części mogły zostać umieszczone w różnych warstwach fizycznych. Po drugie, trzeba umożliwić komunikację między tymi wydzielonymi elementami, np. gdy kod wdrożony na maszynie klienckiej musi wywoływać kod umieszczony na serwerze. Żeby te problemy rozwiązać, należy najpierw podzielić system na warstwy logiczne, takie jak:
- Warstwa prezentacji. Odpowiedzialna za interakcję z użytkownikiem. Nie musi być w całości umieszczona na maszynie klienta, np. w przypadku interfejsu webowego jej część będzie wykonywana na serwerze WWW.
- Warstwa logiki. Meritum naszej aplikacji. Zawiera komponenty biznesowe, a także tzw. logikę aplikacji (nie mylić z biznesową - chodzi o koordynację operacji, obsługę błędów, transakcje itd.). Warstwa ta jest często dzielona na dwie dodatkowe, co jest też zalecane przez DDD i będę to jeszcze opisywał dokładniej.
- Warstwa dostępu do danych. Pośredniczy pomiędzy bazą danych (lub innym ich źródłem) a pozostałymi warstwami.
- Baza danych. Poza danymi często zawiera również kod, np. w postaci procedur składowanych.
Typowy sposób organizacji warstw w aplikacjach biznesowych
W której z tych warstw powinniśmy umieścić logikę biznesową? Pytanie jest proste tylko pozornie, bo choć każdy odpowie instynktownie: "w warstwie logiki, jak sama nazwa wskazuje", to większość z nas jest też świadoma jak to wygląda w praktyce. Logika bywa rozproszona po dokładnie wszystkich warstwach wymienionych powyżej. Często spotkamy się z sytuacją, w której nawet baza danych realizuje całkiem sporą jej część, poprzez procedury składowane czy triggery. Jakie problemy to powoduje opisywałem krótko w poprzedniej części: utrudnione testowanie, niska odporność na zmiany, a w dłuższej perspektywie: koszmar przy migracji infrastruktury i powolne gnicie systemu. Dlatego naszym celem w kolejnych częściach będzie ograniczenie rozproszenia logiki biznesowej i umieszczenie jej w jednej warstwie, niezależnej od pozostałych.