Dzisiaj pokażę Ci architekturę heksagonalną, która jest znana jako wzorzec portów i adapterów. Nauczysz się podstaw tego wzorca architektonicznego, a także zobaczysz jakie są jego jego zalety i wady. Dodatkowo pokażę Ci przypadki, w których warto zastosować ten sposób projektowania aplikacji. Artykuł porusza temat połączenia architektury heksagonalnej z technikami modelowania Domain Driven Design (DDD).
Architektura heksagonalna, nazywana również architekturą ośmioboczną lub architekturą portów i adapterów, to podejście do projektowania oprogramowania, w którym logika biznesowa jest odizolowana od szczegółów implementacyjnych.
W architekturze heksagonalnej, aplikacja składa się z trzech głównych części:
-
Portów - interfejsów, które definiują sposoby komunikacji aplikacji z otoczeniem zewnętrznym. Porty są punktami wejścia i wyjścia aplikacji, przez które przepływają informacje.
-
Adapterów - implementacji portów, które umożliwiają komunikację między aplikacją a otoczeniem zewnętrznym. Adaptery tłumaczą informacje z formatu zrozumiałego dla zewnętrznego systemu na format zrozumiały dla aplikacji i vice versa.
-
Logiki biznesowej - centralnej części aplikacji, która przetwarza informacje przekazywane przez porty i adaptery. Logika biznesowa jest odizolowana od warstwy technicznej, co umożliwia łatwą wymienność adapterów i portów bez zmian w logice biznesowej.
Porty
Oddzielenie logiki biznesowej od komponentów infrastruktury to główne zadanie architektury heksagonalnej. W warstwie logiki biznesowej zamiast bezpośrednio odwoływać się do komponentów infrastrukturalnych definiujesz “porty”, które są niczym drzwi dla komponentów niższego rzędu.
Pomyśl o tych portach jako o portach USB. jest to wtyczka w którą możesz coś wsadzić. Możesz tam wsadzić metodę zapisu do bazy danych a w innym przypadku metodę zapisu do pliku. Logika biznesowa ma w głębokim poważaniu gdzie te dane zostaną zapisane. Wysyła dane do portu i co dalej dzieje się z danymi zależy od tego co jest do tego portu “wsadzone”.
Adaptery
Adapter to jest właśnie to coś co chcesz wsadzić do portu. Na podstawie przykładu powyżej w którym logika biznesowa chce zapisać dane możemy mieć zdefiniowany taki port:
Widzisz tutaj interfejs w którym jest funkcja saveData, która przyjmuje argument data typu string i zwraca true/false.
Zobacz teraz jak mogą wyglądać adaptery dla tego portu:
Teraz definiujesz klasę która na potrzeby przykładu ma dość enigmatyczna nazwę: BussinesLogic:
Klasa oczekuje, że dostanie Writer jako argument. Poniżej widzisz metodę execute, która w naszym prostym przykładzie zapisuje dane.
Teraz mając zdefiniowane dwa różne adaptery możesz użyć ich tak jak poniżej:
Ma to sens?
Zobacz teraz na bardziej skomplikowany przykład w którym robisz stronę produktu sklepu internetowego. Masz takie przypadki użycia:
-
Jako użytkownik mogę zobaczyć informację o produkcie i jego cenę
-
Jako użytkownik mogę dodać produkt do koszyka
Zacznijmy od zdefiniowania typów i interfejsów dla trzech encji których potrzebujemy:
Mamy tutaj cenę, produkt i koszyk. Mówiłem, że będzie trochę bardziej skomplikowany przykład i chyba trochę jest. :)
Teraz zobacz jak może wyglądać logika biznesowa:
Logika biznesowa wystawia dwa porty: product i cart. Napiszmy teraz adaptery do tych portów. Klient mówi, że chce się zintegrować z systemem eCommerce Magento. Mówi i ma:
BTW te przykłady sa bardzo prostym pseudo kodem w TypeScripcie bardziej, żeby pokazać Ci ideę niż dać gotowy produkcyjny kod, więc jak widzisz takie coś:
Teraz zamknij na chwilę oczy i wyobraż sobie, żę ten kod wysyła request do systemu eCommerce i pobiera prawdziwe dane.
Mój kod nie ma wyobraźni dlatego musiałem tam wpisać na sztywno jakieś dane typu:
W każdym razie - adaptery powyżej pobierają kod zsystemu eCommerce. W tym przypadku jest to Magento. Zobaczjak można ten kod wykonać:
Konsolawydrukuje coś takiego:
Brawo, właśnie napisaliśmy kod w architekturze Porty i Adaptery!
To nie wszystko. Teraz wyobraż sobie, że po trzech miesiącach okazało się, że Twój klient jest niestabilny emocjonalnie i stwierdził, że chce teraz się zintegrować z systemem BigCommerce. Logika biznesowa zostaje taka sama. Co robisz?
Dopisujesz adaptery dla BigCommerce:
No i wciskasz je w swoje porty:
Opdalasz:
Console mówi:
Infrastruktura
W architekturze heksagonalnej warstwa prezentacji i warstwa dostępu do danych to integracja z komponentami zewnętrznymi takimi jak:
-
Baza danych
-
UI
-
dostawca zewnętrzny
-
magistrala komunikatów
Driving side
Interfejs aplikacji mobilnej lub kod interfejsu użytkownika aplikacji internetowej (UI) to coś co rozpoczyna interakcję z aplikacją. Dane od użytkownika z UI są popierane przez adapter i wysyłane do logiki biznesowej przez port. Po angielsku mówi się o tym: driving side. Najlepsze tłumaczenie jakie znalazłem w języku polskim to… driving side
Driven side
Bazy danych a nawet serwisy zewnętrzne potrzebują aplikacji żeby działać. W tym przypadku aplikacja wywołuje serwis zewnętrzny lub wysyła request do bazy danych. Następnie adapter implementuje port z którego ma korzystać.
Zasada inwersji zależności
Zasada inwersji zależności (Dependency Inversion Principle) mówi, że moduły wyższego poziomu, które implementują logikę biznesową, nie powinny zależeć od modułów niższego poziomu. Oznacza to, że interfejsy powinny być zdefiniowane przez moduły wyższego poziomu. Dzięki temu system staje się bardziej elastyczny i łatwiejszy w modyfikacji, ponieważ zmiany wprowadzone w jednym module nie wpłyną na pozostałe moduły, jeśli interfejsy pozostaną niezmienione.
W architekturze warstwowej jest dokładnie na odwrót - moduły wyższego poziomu i mówiąc wprost - logika biznesowa - zależą od modułów niższego poziomu.
Dzięki odwróceniu zależności logika biznesowa nie jest pomieszana z szczegółami implementacyjnymi ani z problemami technologicznymi.
Żeby to wszystko miało sens, potrzebujesz jeszcze coś co w architekturze warstwowej jest warstwą usług, a w architekturze Porty i adaptery jest publicznym interfejsem, który opisuje wszystkie operacje systemu.
Architektura heksagonalna – korzyści
-
Łatwa skalowalność
-
Rozwój aplikacji
-
Izolacja logiki biznesowej od warstwy technicznej, ułatwiająca wprowadzanie zmian bez wpływu na cały system
Architektura heksagonalna – wady
-
Zwiększona złożoność - architektura heksagonalna dodaje komponenty, które są pośrednikami co wpływa na złożoność
-
debugowanie - aplikacje utworzone przy użyciu wzorca architektury heksagonalnej mogą być trudniejsze do debugowania, ponieważ nie korzystają bezpośrednio z konkretnych implementacji.
-
tłumaczenie - gdy domena biznesowa jest modelowana niezależnie od bazy danych lub innej technologii, tłumaczenie między modelami używanymi do trwałości lub komunikacji i modelem domeny może być niewygodne. Problem ten pogarsza się, gdy modele zasadniczo różnią się od siebie pod względem technicznym i koncepcyjnym.
-
krzywa uczenia się - Architektura heksagonalna różni się od tradycyjnych wzorców architektonicznych, które często narzucane są programistom przez frameworki. Może to być trudniejsze dla nowych programistów ze względu na potrzebę pośrednictwa, tłumaczenia i wzorców projektowych.
Kiedy użyć architektury heksagonalnej?
Prawidłowa odpowiedź brzmi zapewne jak zawsze: to zależy.
Jeśli budujesz prostą aplikację CRUD, prawdopodobnie nie warto pchać się w porty i adaptery.
Architektura porty i adaptery nadaje się do do złożonej logiki biznesowej bardziej niż architektura warstwowa.
Gdy używasz różnych systemów zewnętrznych, farameworków, metod odczytu i zapisu danych to wtedy warto rozważyć architekturę heksagonalną.
Możesz też rozważyć wdrożenie tylko niektórych aspektów architektury, aby poprawić separację problemów. Można to zrobić na wiele sposobów i jest to coś, co należy omówić ze swoim zespołem programistów, ponieważ odpowiedź może być inna dla każdego projektu.
Architektura heksagonalna i DDD (Domain Driven Design)
Architektura heksagonalna i Domain Driven Design (DDD) to dwa uzupełniające się podejścia do projektowania oprogramowania, które mają ten sam cel - ułatwienie elastyczności, skalowalności i łatwości konserwacji systemów oprogramowania.
Oba podejścia podkreślają ważność oddzielenia biznesowej logiki od technicznej warstwy i oba korzystają z interfejsów do określenia sposobów, w jakie różne części systemu komunikują się ze sobą.
W DDD chodzi o to, żeby stworzyć jasny i spójny model domeny biznesowej i wykorzystać go do projektowania systemu oprogramowania.
Architektura heksagonalna pozwala na wprowadzenie tego modelu w sposób elastyczny i skalowalny, przez oddzielenie biznesowej logiki od technicznej warstwy i zapewnienie przejrzystych interfejsów komunikacyjnych.
Łącząc zasady architektury heksagonalnej z technikami modelowania DDD, można tworzyć systemy oprogramowania, które są zarówno elastyczne, jak i łatwe w utrzymaniu oraz idealnie dopasowane do potrzeb klienta.
Trzeba jednak pamiętać, że oba podejścia wymagają dokładnego planowania i projektowania oraz że mogą nie być odpowiednie dla wszystkich projektów oprogramowania.
Podsumowanie
Architektura heksagonalna, zwana również porty i adaptery, to wzorzec architektoniczny, który pozwala na oddzielenie logiki biznesowej od warstwy technicznej i ułatwia wprowadzanie zmian bez wpływu na cały system.
W tym wzorcu, logika biznesowa wystawia porty, których implementacja zależy od adapterów napisanych dla konkretnych technologii. W ten sposób, każda warstwa jest oddzielona i może być rozwijana niezależnie.
Architektura heksagonalna jest szczególnie przydatna w złożonych projektach, które wymagają integracji z różnymi systemami zewnętrznymi, takimi jak bazy danych, UI, dostawcy zewnętrzni i magistrala komunikatów.
Wadami tej architektury to zwiększona złożoność, trudności w debugowaniu, tłumaczeniu i krzywa uczenia się.
Warto rozważyć zastosowanie architektury heksagonalnej w projektach, które wymagają oddzielenia warstwy biznesowej od warstwy technicznej i łatwej integracji z różnymi systemami zewnętrznymi.
Można także połączyć zasady architektury heksagonalnej z technikami modelowania DDD, aby stworzyć systemy oprogramowania, które są zarówno elastyczne, jak i łatwe w utrzymaniu oraz idealnie dopasowane do potrzeb klienta.
Źródła
-
Eric Evans’ Domain-Driven Design: Tackling Complexity in the Heart of Software
-
Learning Domain-Driven Design: Aligning Software Architecture and Business Strategy - Vlad Khononov