Slovník pojmů

Hexagonální architektura

Aplikace je střed systému; její okolí jsou vyměnitelné způsoby, jak do ní vstoupit nebo z ní získat výsledek.

Stručná definice

Port je kontrakt aplikace, adaptér jej překládá pro konkrétní okolí.

Hexagonální architektura neboli Ports and Adapters popisuje aplikaci jako jádro obklopené rozhraními. Vstupní adaptér přijme podnět z okolí a zavolá vstupní port. Výstupní port naopak vyjadřuje schopnost, kterou jádro potřebuje od okolního systému; jeho konkrétní implementaci dodá výstupní adaptér.

Název neznamená šest vrstev ani síťový TCP port. Hexagon je jen kresba, která má zabránit představě, že aplikace musí mít jeden privilegovaný vstup, například HTTP controller. Jediný případ použití může obsloužit web, příkazovou řádku, message consumer i test, aniž by se jeho pravidla kopírovala do každého z nich.

Jaký problém řeší

Technický vstup ani dodavatel služby nemá určovat pravidla případu použití.

Přístup je přínosný, když tentýž aplikační scénář potřebuje více vstupů, více implementací nebo nezávislé testování.

  • HTTP endpoint, CLI import a message consumer nad stejnou aplikační operací
  • výběr mezi HTTP API, XML feedem a připravenými testovacími daty pro jeden zdroj informací
  • oddělení úlohy aplikace od konkrétního databázového klienta nebo fronty zpráv
  • rychlé testy případů použití bez sítě, souborového systému a skutečné databáze
  • postupná výměna externího poskytovatele bez přepsání míst, která používají jeho data
  • sjednocení významu operace napříč interním nástrojem, veřejným API a dávkovým zpracováním

Praktický příklad

Získání výdejních míst dopravce

Aplikace potřebuje pro checkout vrátit výdejní místa zvoleného dopravce. Vstupní port ListCarrierPoints vyjadřuje operaci dostupnou okolí aplikace. HTTP controller jej může zavolat po webovém požadavku, ale stejný port může obsloužit také interní CLI nástroj pro kontrolu dostupnosti.

Jádro si pro načtení dat vyžádá výstupní kontrakt CarrierPointSource. Produkce může mít HTTP API adaptér, starší integrace XML adaptér a test jednoduchý fake s předem připravenými body. Nejde o to skrýt každý objekt za rozhraní; kontrakt má smysl právě proto, že zdroj se může lišit a aplikace potřebuje stabilní význam výsledku.

Textový diagram

HTTP controller / CLI příkaz
  → ListCarrierPoints (vstupní port a use case)
    → CarrierPointSource (výstupní port)
      → HTTP API adaptér | XML adaptér | testovací fake
    → seznam výdejních míst pro aplikaci

Jak funguje

Dvě strany rozhraní kolem aplikačního jádra

Textový diagram je alternativou k hexagonu: několik různých adaptérů může vstoupit stejným portem a několik jiných může obsloužit potřebu jádra.

  1. Vstupní adaptér HTTP controller, CLI příkaz nebo consumer zprávy převede konkrétní transport na volání aplikace. Validuje technický tvar vstupu a předá mu srozumitelná data.
  2. Vstupní port Port popisuje, co může okolí po aplikaci chtít. Může mít podobu rozhraní, veřejné aplikační služby nebo jiného explicitního kontraktu; není to síťový port ani automaticky jedna třída.
  3. Aplikační jádro Případ použití koordinuje pravidla, rozhodne o výsledku a zná význam operace. Nemá být svázán s tím, zda jej spustilo HTTP, plánovač nebo příjem zprávy.
  4. Výstupní port Jádro pojmenuje schopnost, kterou potřebuje od okolí: načíst data, uložit změnu, odeslat příkaz nebo zjistit čas. Kontrakt má vyjadřovat potřebu aplikace, ne kopírovat API dodavatele.
  5. Výstupní adaptér Databázový klient, HTTP integrace, souborové úložiště či fake přeloží kontrakt do konkrétní technologie. Zvolená implementace se zapojí při sestavení aplikace.

Hlavní části

Porty a adaptéry mají odlišnou odpovědnost.

Rozdělení není předpis pro počet souborů. Je to způsob, jak poznat, které rozhodnutí patří aplikaci a které konkrétní technologii.

Inbound port

Vstupní port dává okolí stabilní způsob, jak vyvolat případ použití. Jeho kontrakt popisuje operaci aplikace, například vytvořit objednávku nebo vypsat výdejní místa.

Inbound adapter

Vstupní adaptér připojuje konkrétní kanál. Controller rozumí HTTP statusům a formulářovým datům, CLI adaptér argumentům a consumer formátu zprávy; tyto detaily nepatří do jádra.

Outbound port

Výstupní port je kontrakt definovaný z pohledu potřeby aplikace. CarrierPointSource nemusí prozrazovat, zda za ním je REST, XML nebo databáze, pokud tyto rozdíly nejsou významné pro use case.

Outbound adapter

Výstupní adaptér implementuje port přes konkrétní systém. Může obsahovat autentizaci, převod formátu, retry nebo mapování chyb dodavatele na význam, kterému jádro rozumí.

Sestavení aplikace

Místo, kde se vybere konkrétní adaptér, patří na okraj systému. Předání závislostí do use casu umožní zaměnit implementaci v testu i při provozní změně.

Vztah k podobným přístupům

Porty nejsou vrstvy ani mikroslužby.

Přístupy se mohou překrývat, ale není vhodné z nich dělat jeden univerzální recept.

Hexagonální a Clean Architecture
Oba směry chrání aplikační pravidla před technickými detaily. Hexagonální architektura je zvlášť užitečná pro rozlišení vstupních a výstupních adaptérů, kdežto Clean Architecture akcentuje dependency rule a soustředné oblasti odpovědností.
Port a adaptér
Port je kontrakt na hranici aplikace. Adaptér je kód, který tento kontrakt překládá pro konkrétní technologii. Jeden adaptér může tvořit více tříd a jeden port může mít více adaptérů.
Hexagonální architektura a DDD
Ports and Adapters organizuje kontakt aplikace s okolím. Domain-Driven Design pomáhá modelovat problémovou doménu a její jazyk. Lze je spojit, ale aplikace může mít porty i bez bohatého doménového modelu.
Kontrakt a dependency injection
Kontrakt určuje, jakou schopnost aplikační část potřebuje. Dependency injection je technika, jak vybraný adaptér předat při vytváření objektu. Samotné předání závislosti ještě neříká, zda kontrakt dává smysl.

Výhody a omezení

Výměnnost je přínos jen tam, kde se skutečně očekává změna.

Přínosy

  • jeden případ použití lze využít z více technologických vstupů
  • externí integrace a databáze mají zřetelnější místo na okraji aplikace
  • testy mohou pracovat s řízenými adaptéry bez skutečné infrastruktury
  • překlad chyb a formátů dodavatele je soustředěný mimo aplikační pravidla

Omezení a časté chyby

  • vytvářet port pro každou interní třídu, i když nemá alternativu ani hranici
  • kopírovat detailní DTO externího API do celého aplikačního modelu
  • považovat adapter za jednu povinnou třídu nebo port za síťový TCP port
  • předpokládat, že přístup vyžaduje mikroservisy, mnoho vrstev nebo oddělené nasazení

Pragmatičnost

Přímá služba může být lepší než abstrakce bez důvodu.

Pokud jednoduchá administrační akce volá jediný stabilní frameworkový komponent a nebude mít jiný vstup ani implementaci, další port a adaptér mohou jen prodloužit cestu k výsledku. Přímé použití dobře zapouzdřené služby je v takovém případě legitimní. Složitost se vyplatí přidat ve chvíli, kdy různé zdroje, vstupy nebo testy skutečně potřebují oddělený význam.

Dobrou hranici lze poznat podle toho, že ji umí tým popsat větou z jazyka aplikace: například „potřebujeme zdroj výdejních míst“, nikoli „potřebujeme wrapper nad klientem dodavatele“. Pokud kontrakt jen opakuje všechna volání jedné knihovny, jádro pravděpodobně chráněné není a přibylo pouze další rozhraní.

Na co myslet

Hranice má přeložit význam, ne jen přemístit frameworkové API.

Konkrétní adaptér může být složitý, ale jeho složitost nemá bezdůvodně protékat do případu použití.

  • pojmenovat port podle schopnosti, kterou aplikace opravdu potřebuje
  • udržet HTTP, CLI a formát zpráv ve vstupních adaptérech
  • převádět chyby a datové formáty dodavatele na chování srozumitelné aplikaci
  • používat testovací fake pro rozhodování a samostatně ověřit produkční adaptér proti jeho hranici
  • přidávat nové porty až pro skutečné zdroje změny, ne jako mechanickou konvenci

Časté otázky

Hexagonální architektura v praxi

Je port v hexagonální architektuře síťový port?

Ne. Jde o aplikační kontrakt na hranici systému. Může být vyjádřen rozhraním, metodou aplikační služby nebo jiným explicitním způsobem, ale nesouvisí s TCP či UDP portem.

Musí být každý adaptér jedna třída?

Ne. Adaptér je odpovědnost překladu mezi aplikací a konkrétním okolím. V praxi může obsahovat klienta, mapper, konfiguraci i několik tříd; malý adaptér může být naopak jediná služba.

Vyžaduje hexagonální architektura mikroslužby?

Ne. Hodí se i pro jeden modul modulárního monolitu nebo pro běžnou webovou aplikaci. Neříká nic o počtu nasazovaných služeb, pouze o hranici mezi aplikačním jádrem a okolím.

Kdy je vhodné port nepřidávat?

Když neexistuje samostatná aplikační hranice ani pravděpodobná změna vstupu či výstupu. Pokud kontrakt pouze obaluje jednu stabilní interní třídu bez jiného významu, zpravidla nepřináší hodnotu.

Jak architekturu používám v praxi

Porty volím pro místa, kde technologie nemá řídit význam aplikace.

Při integracích a více vstupních bodech odděluji aplikační rozhodnutí od konkrétního HTTP, databáze nebo fronty. Rozsah hranice přitom volím podle skutečné složitosti.

Zavolejte mi

Zavolám vám následující pracovní den mezi 9:00 a 17:00.

Můžete mi také zavolat rovnou.

+420 605 181 728

Nechte mi telefonní číslo a pošlete žádost o zpětné zavolání.

Odesláním souhlasíte se zpracováním údajů pro vyřízení žádosti.