Nasza strona używa cookies. Korzystając ze strony, wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki. Rozumiem

Architektura heksagonalna w Javie w 3 minuty

Alex Prut Senior Software Engineer
Sprawdź, czym jest architektura heksagonalna oraz zobacz zwięzły i praktyczny przykład implementacji tego wzorca w Javie.
Architektura heksagonalna w Javie w 3 minuty

Chciałbym w tym krótkim artykule pokazać przykłady użycia architektury heksagonalnej (ang. Hexagonal Architecture). Zaznaczam jednak, że nie wyczerpię tu tematu, a raczej pokażę pewne podejście oraz implementację podstaw w Javie. Architektura heksagonalna (lub Porty i adaptery) została zapoczątkowana przez Alistaira Cockburn'a. Jej głównym celem jest unikanie niektórych znanych problemów konstrukcyjnych w projektowaniu oprogramowania, np. zanieczyszczania kodu UI logiką biznesową lub niepożądanych zależności między warstwami.

Architektura ta ma zatem na celu tworzenie luźno powiązanych komponentów, które można połączyć za pomocą „portów” i „adapterów” (patrz Ilustracja 1). W rezultacie sprawia to, że komponenty można wymieniać na dowolnym poziomie, a testowanie pojedynczych części jest łatwiejsze.


Ilustracja 1. Architektura heksagonalna


Definicja

Słowo „port” przywołuje na myśl porty w gadżetach elektronicznych. Dla każdego urządzenia zewnętrznego istnieje „adapter”, który konwertuje protokół wymagany przez to urządzenie i odwrotnie. Adaptery mogą się również kojarzyć z elementami scalającymi komponenty aplikacji ze światem zewnętrznym, dlatego też jednemu portowi może odpowiadać wiele adapterów. Warto również zwrócić uwagę na to, że wszystkie urządzenia zewnętrzne są identyczne z punktu widzenia aplikacji.

Nie należy jednak myśleć, że słowo „heksagonalny” lub „sześciokątny” ma tutaj znaczenie. Celem nie było zasugerowanie, że potrzebnych jest sześć portów lub adapterów. Chodzi raczej o to, aby pozostawić wystarczająco dużo miejsca do zobrazowania różnych interfejsów między aplikacją a światem zewnętrznym. Dlatego zostało wybrane słowo „heksagonalny”. Naturalnym odpowiednikiem portu w Javie jest interfejs, a adapter jest jedną implementacją tego interfejsu.


Przykład

Za przykład posłuży nam aplikacja do przechowywania książek. Dla uproszczenia wyszukujemy tylko zapisane książki. Książki można przechowywać w bazie danych i przeszukiwać za pomocą API (HTTP, przeglądarka i tak dalej). Poniższa ilustracja przedstawia implementację w Javie.


Ilustracja 2. Przykład architektury heksagonalnej w Javie


Najpierw definiujemy encję naszej aplikacji:

public class Book {
    private String title;
    private String isbn;
    private String author;

    // standard constructor and getters
}


Następnie definiujemy odpowiednik portu przychodzącego:

public interface ApiInterface {
    Book get(String isbn);
}


Teraz definiujemy port wyjściowy:

public interface BookDaoInterface {
    Book get(String isbn);
}


Następnie implementujemy wcześniej zdefiniowany port wyjściowy. Stąd odpowiednik adaptera:

public class BookDaoPostgres implements BookDaoInterface {
    public Book get(String isbn) {
        // TODO implement PostgreSQL logic here
        return null;
    }
}


Oto alternatywny adapter dla tego samego interfejsu:

public class BookDaoMock implements BookDaoInterface {
    private HashMap<String, Book> books = new HashMap<String, Book>();

    public BookDaoMock() {
        books.put("mock", new Book("mock", "mock", "mock"));
    }

    public Book get(String isbn) {
        return books.get(isbn);
    }
}

Następnie definiujemy usługę domenową dla naszej aplikacji:

public class BookService {
    private BookDaoInterface dao;

    public BookService(BookDaoInterface bookDao) {
        dao = bookDao;
    }

    public Book search(String isbn) {
        return dao.get(isbn);
    }
}


No koniec tworzymy adapter dla portu wejściowego:

public class HttpApi implements ApiInterface {
    private BookService service;

    public HttpApi(BookService service) {
        this.service = service;
    }

    // TODO implement HTTP endpoint
    public Book get(String isbn) {
        return service.search(isbn);
    }
}


Podsumowanie

Używając takiego podejścia, będziesz mieć większą „elastyczność” w zastępowaniu różnych zależności. W rezultacie zwiększysz ogólną „testowalność” aplikacji.


Oryginał tekst w języku angielskim przeczytasz tutaj.

Lubisz dzielić się wiedzą i chcesz zostać autorem?

Podziel się wiedzą z 160 tysiącami naszych czytelników

Dowiedz się więcej