Przejdź do treści

Architektura Hexagonalna [Cebula/Porty i Adaptery] 🔥👍

Architektura Hexagonalna znana też jako Onion Architecture lub Ports & Adapters została wymyślona przez Alistaira Cockburna i opublikowana w 2005 roku. Jej celem jest uniknięcie znanych pułapek strukturalnych w OOP jak np. zależności między wartswami czy też wplatanie kodu odpowiedzialnego za GUI do warstwy logiki biznesowej. Ma ona na celu stworzenie luźno powiązanych komponentów aplikacji, które można łatwo połączyć za pomocą portów i adapterów. To sprawia, że komponenty są wymienialne na każdym poziomie i ułatwia automatyzację testów.


Cześć 🙂

To jest już trzeci wpis z mini-serii blogów o architekturach aplikacyjnych. W poprzednich wpisach mogłeś przeczytać o

W dzisiejszym artykule dowiesz się:


Czym jest architektura hexagonalna

Celem architektury hexagonalnej jest umożliwienie, aby aplikacja którą rozwijamy była traktowana tak samo przez użytkowników, programy zewnętrzne, testy czy też skrypty. Dodatkowo ma ona umożliwiać rozwój i testowanie aplikacji w oderwaniu od zewnętrznych zależności (baza danych / zewnętrzne API).

Architektura hexagonalna w centrum aplikacji stawia na przypadki użycia i domenę – to też jest powód dlaczego sprawdza się ona dobrze z DDD. Warto zauważyć, że ten styl architektoniczny jest przeciwny architekturze wartstwowej – tutaj zależności są kierowane do środka domeny a nie jak w przypadku warstw od góry do dołu.

 

Nazwa architektury wzięła się z efektu wizualnego jaki przedstawia hexagon – więcej o tym przeczytasz w artykule Alistair’a Cockburn’a, twórcy architektury:

The hexagon is not a hexagon because the number six is important, but rather to allow the people doing the drawing to have room to insert ports and adapters as they need, not being constrained by a one-dimensional layered drawing. The term ‘’hexagonal architecture’’ comes from this visual effect.


Jak wygląda struktura hexagonu

Hexagon składa się z trzech kluczowych elementów: domeny (core), portów i adapterów. Jeżeli spojrzymy na hexagon zaprezentowany na rysunku powyżej można też zauważyć, że nasz core (domena i przypadki użycia) używają portów – czyli domena wie na co dany port pozwala, np. zapis użytkownika ale nie wie jak to się stanie. I do tego właśnie służą adaptery. Aplikacja może mieć np. prawdziwą bazę danych, która rzeczywiście zapisze użytkownika w bazie ale może to też być hashmapa, która po restarcie aplikacji nie będzie już niczego pamiętać.

Porty dzielą się na wejściowe i wyjściowe. Wejściowe to te, które sterują naszą logiką np. GUI, gdzie użytkownik wywołuję daną akcję. Wyjściowe są używane (sterowane) przez logikę biznesową. Tutaj najlepszym przykładem są bazy danych i zewnętrzne API. Aplikacja musi porozumieć się z jakimiś zewnętrznymi komponentami i w tym celu używa właśnie portów wyjściowych.

Domena

Są to nasze obiekty domenowe. Ich implementacja powinna być na tyle jasna, żeby nawet ktoś nie techniczny mógł podejść i powiedzieć ok, rozumiem co tu się dzieje. Obiekty domenowe nie powinny mieć żadnych zewnętrznych zależności. Domena opisuje jak coś zrobić.

Przypadki użycia

To klasy, które obsługują konkretne problemy biznesowe. Jest to faktyzcna wartość dla biznesu, która rozwiązuje ich problem. Przypadki użycia mogą zawierać wszystkie niezbędne walidacje i logikę reguł biznesowych, które są specyficzne dla konkretnego przypadku użycia (dlatego też nie mogą być implementowane w obiektach domeny) a następnie delegują pracę do domeny. Use case’y nie są zależne od zewnętrznych komponentów, natomiast jeżeli potrzebują czegoś z zewnątrz (np. pobranie użytkownika) to tworzą do tego port (albo używają istniejącego). Przypadki użycia mówią co zrobić.

Porty wejściowe i wyjściowe

Porty możesz sobie wyobrażać dokładnie tak jak porty w komputerze, np. USB. Nie jest ważne czy podłączysz urządzenie firmy X czy Y – oba powinny działać.

W architekturze hexagonalnej komunikacja do i z aplikacji odbywa się przez porty. Port wejściowy będzie zazwyczaj interfejsem, który jest implementowany przez konkretny przypadek użycia. Podobnie jest z portem wyjściowym – to również będzie interfejs. Tym razem jednak przypadek użycia będzie go używał – a nie tak jak w przypadku portu wejściowego implementował.

Dzięki portom wejściowym i wyjściowym mamy bardzo wyraźne miejsca, w których dane wchodzą i wychodzą z naszego systemu, ułatwiając rozumowanie na temat architektury.

Adaptery

Adaptery tworzą zewnętrzną warstwę architektury hexagonalnej. Nie są częścią rdzenia, ale wchodzą z nim w interakcję. Adaptery również dzielimy na dwie grupy: wejściowe i wyjściowe. Wejściowe (lub sterujące) wywołują porty wejściowe, aby coś zrobić w aplikacji – w końcu wywołują konkretny przypadek użycia. Wyjściowe (sterowane) są natomiast wywoływane przez przypadki użycia.

Adaptery ułatwiają wymianę określonej warstwy aplikacji, np. zmiana bazy danych na inną. Wystarczy napisać tylko nowy adapter.


Wady i zalety architektury

Zalety:

    • testowalność
    • rozwijalność i utrzymanie
    • wymiana technologii

Wady:

    • trudniejsza nawigacja po kodzie źródłowym – musimy ogarniać co, gdzie jest używane
    • wiele adapterów = wiele testów

Podsumowanie

W tym krótkim wpisie chciałem przedstawić Ci ideę architektury hexagonalnej. Jeżeli czytałeś wcześniejsze artykuły to zapewne już wiesz, że użycie konkretnej architektury zależy od tego czego chce biznes. W przypadku architektury hexagonalnej dobrym wyborem jej zastosowania wydają się być projekty o zmiennej i złożonej logice biznesowej. Natomiast zastosowanie jej w CRUD-ach będzie zdecydowanie przesadą i przerostem formy nad treścią. Zamiast uprościć zrozumienie, możesz je tylko niepotrzebnie skomplikować.

Źródła:


Za tydzień

Moja lista todo robi się coraz dłuższa… jednak zanim zacznę ją czyścić (może powinienem pisać 2 artykuły tygodniowo) chciałbym dokończyć serię o architekturach aplikacyjnych. Dlatego też za tydzień opiszemy popularną architekturę mikroserwisów.

5 2 votes
Article Rating
Subscribe
Powiadom o
guest
5 komentarzy
najnowszy
najstarszy oceniany
Inline Feedbacks
View all comments
Jarek
1 miesiąc temu
  • trudniejsza nawigacja po kodzie źródłowym – musimy ogarniać co, gdzie jest używane

no raczej jest właśnie łatwiej

  • wiele adapterów = wiele testów

te adaptery to dostarczone przez ich producentów elementy środowiska, tego używa a nie testuje

Damian
Damian
2 lat temu

yo, fajny artykuł, mam pytanie odnośnie fragmentu tekstu ” Przypadki użyciaTo klasy, które obsługują konkretne problemy biznesowe. Jest to faktyzcna wartość dla biznesu, która rozwiązuje ich problem. Przypadki użycia mogą zawierać wszystkie niezbędne walidacje i logikę reguł biznesowych, które są specyficzne dla konkretnego przypadku użycia (dlatego też nie mogą być implementowane w obiektach domeny) a następnie delegują pracę do domeny.” nie rozumiem do końca podziału przypadków użycia i domeny w tym opisie rozumiem to tak, że przypadki użycia są wystawionym API z modułu(opisanym jako port wejściowy w Twoim artykule) i one bezpośrednio kierują ruch do domeny, która posiada walidację i… Czytaj więcej »

Jarek
1 miesiąc temu
Reply to  Damian

przypadki użycia to nie klasy a scenariusze

Czarek
4 lat temu

Cześć!

Dzięki za wyjaśnienie architektury Ports & Adapters. Koncepcja nie wydaje się trudna i daje sporo możliwości, ale pewnie trzeba spędzić z nią trochę czasu, aby znaleźć jej ciemne strony 😉 Wykorzystywałeś ją może w jakimś projekcie produkcyjnym?

Pozdrawiam

5
0
Would love your thoughts, please comment.x