Architektura Warstwowa pozwala świetny w sposób zorganizować nasz kod, dzięki czemu architektura aplikacji przestaje być „płaska”. Dokładając do tego koncept Modularnego Monolitu sprawiamy, że nasza aplikacja zostaje pocięta na kawałki. Można dzięki temu lepiej poznać konteksty aplikacji. Dziś poznamy kolejny wzorzec, który bardzo dobrze wpływa na wymienność części aplikacji – Architekturę Hexagonalną.
Kontrakty, Porty i Adaptery
Zanim przejdziemy do definicji Architektury Hexagonalnej, potrzebujemy przejść przez koncept Kontraktu wewnątrz aplikacji.
Podczas poznawania teorii testowania jednostkowego na pewno obiło nam się o uszy, że powinniśmy testować kontrakt metody. Do kontraktu należą trzy elementy: parametry, zwracane wartości oraz wyrzucane przez metodę wyjątki. Kiedy spojrzymy nieco szerzej, w sposób bliższy architekturze, możemy dojrzeć element, który podobnie do testowania jednostkowego, może służyć za kontrakt. Tym elementem jest interfejs.
Kiedy próbujemy walczyć z zależnościami między warstwami, używamy do tego celu interfejsów. Nie wiążemy wtedy warstw z konkretnymi implementacjami. Jedyne, co warstwa powiązana powinna robić, to dawać gwarancję, że kod będzie działał tak, jak oczekuje tego warstwa wiążąca. I tym właśnie na polu architektury aplikacji jest kontrakt.
Architektura Hexagonalna w rozumieniu kontraktowym
Drugą nazwą, lepiej definiującą kwintesencję omawianego dziś wzorca, jest sformułowanie „Porty i Adaptery”. Portem nazywamy Kontrakt, którego spełnienia wymaga strona wiążąca wobec strony związanej. Mówiąc zarówno o stronie wiążącej jak i związanej kontraktem, mam na myśli pojedynczą klasę jak i cały moduł. Zatem wychodzi na to, że Portem będzie interfejs, którego wymaga strona wiążąca, a który musi być spełniony przez stronę związaną. Adapterem, czyli obietnicą spełnienia Kontraktu będzie zatem implementacja interfejsu.
Architektura Hexagonalna w kontekście Architektury Warstwowej
Kiedy spojrzymy na Architekturę Warstwową znaną z DDD, zauważymy cztery warstwy: Interfejs Użytkownika, Aplikację, Domenę oraz Infrastrukturę. Warstwa Domeny jest całkowicie otoczona przez warstwę Aplikacji (nic poza Aplikacją nie ma dostępu do Domeny), zatem na potrzeby obecnego eksperymentu myślowego możemy ją pominąć. Zostaje nam Interfejs Użytkownika, Aplikacja oraz Infrastruktura.
Porty Aktywne – Interfejs Użytkownika
Przypominając, warstwa Interfejsu Użytkownika to kontekst HTTP (czyli kontrolery) oraz kontekst CLI (skrypty konsolowe). Z punktu widzenia DDD, Aplikacja nie powinna mieć wiedzy o tym, w jakim kontekście została uruchomiona. Zatem wymienność stoi po stronie warstwy Interfejsu Użytkownika. Z kolei Interfejs Użytkownika nie powinien znać szczegółów implementacyjnych Aplikacji. Interesuje go jedynie komunikacja z Aplikacją. Zatem warstwa Aplikacji powinna wystawić odpowiedni interfejs.
Z powyższego wywodu wychodzi, że warstwa Aplikacji definiuje Porty, a Interfejs Użytkownika dostarcza Adaptery. Ponieważ to Interfejs Użytkownika komunikuje się z Aplikacją, to Porty wystawiane w tym celu będą nosiły nazwę Portów Aktywnych.
Porty Pasywne – Infrastruktura
Warstwa Infrastuktury odpowiedzialna jest za wystawianie implementacji służących do komunikacji z systemami zewnętrznymi. Za system zewnętrzny możemy wziąć zarówno serwis znajdujący się poza obecną jednostką serwerową (baza danych, indeksery, systemy cache, API wystawione przez inne aplikacje) jak i te, które znajdują się bezpośrednio na serwerze (cache w pamięci serwera, system plików, linia komend).
Do jednego zadania możemy wykorzystać wiele różnych systemów (np. notyfikacja przez maila, SMS, webhook), zatem dochodzi do wymienności, która jest głównym wyznacznikiem Adapterów. Z drugiej strony, warstwa Aplikacji nie powinna interesować się tym, w jaki sposób realizowana będzie wspomniana wcześniej funkcjonalność notyfikacji. Jedyna wiedza, jaką powinna mieć, to w jaki sposób tą notyfikację wyzwolić.
Z powyższego wynika, że Aplikacja będzie definiowała Porty, które będą rozwiązywane przez Infrastrukturę w postaci Adapterów. W przeciwieństwie do Interfejsu Użytkownika, Adaptery infrastrukturalne nie będą wyzwalały Aplikacji. Zatem Infrastruktura będzie pasywna względem Aplikacji. Porty utworzone w tym celu będą nosiły zatem nazwę Portów Pasywnych.
I tu dochodzimy do pojęcia Hexagonu
Dochodzimy do pytania, gdzie tutaj mamy do czynienia z Hexagonem? W jaki sposób Porty i Adaptery mogą kojarzyć się z sześciokątem? Otóż, Architekturę Hexagonalną zwyczajowo przenosi się na schemat zbliżony poniższemu:
Powyższy schemat już bardziej przypomina sześciokąt 🙂 Umownie, po lewej stronie zapisuje się porty aktywne, po prawej – porty pasywne.
Comments are closed.