Jeśli zrobię zmianę łamiącą kontrakt to tak czy siak muszę zmienić kod po obu stronach - zarówno ten wystawiający API według kontraktu jak i ten korzystający z API. Niespecjalnie widzę jak cykl ma tu coś zmieniać.
Może rozpiszę co mam na myśli pisząc o typowej dla monolitów hierarchii modułów i hierarchii typowej dla mikroserwisów. Najpierw trzeba jeszcze podać założenie, że nie może być wprost cykli w zależnościach, tzn jeśli moduł A zależy od B, to moduł B nie może zależeć od A.
W hierarchii monolitowej nie ma rozdziału na moduł z API i moduł implementujący to API. Stąd nie da się zrobić cykli w ogóle. Ponadto hierarchia często jest dość głęboka. Dla przykładu możemy mieć:
- moduł common niezależący od niczego
- moduł core zależący od modułu common
- moduł orders zależący od modułu core
- moduł trading zależący od modułu orders
- itd
Zależności są przechodnie, więc moduł trading tak naprawdę zależy nie tylko od modułu orders ale także common i core, więc ma dostęp do klas z tych modułów. Taka architektura skutkuje pchaniem funkcjonalności do modułów najwyżej w hierarchii, tam gdzie będą najszerzej dostępne (to ułatwia sprawy na krótką metę, tzn jest to chodzenie po linii najmniejszego oporu). Stąd w monolicie będą dominować moduły common i core.
W hierarchii mikroserwisowej każdy serwis składa się z dwóch modułów - moduł-api i moduł-impl. moduł-api nie zależy od żadnego innego modułu, a moduł-impl zależy od dowolnej ilości modułów API. Dzięki temu nie ma cyklów w hierarchii modułów, a sama hierarchia modułów jest płaska. Nie ma centralnego modułu ze współdzielonymi funkcjonalnościami, więc nie ma rozpychania centralnego modułu. Przykładowa hierarchia może wyglądać tak:
- moduł orders-api bez zależności
- moduł trading-api bez zależności
- moduł accounts-api bez zależności
- moduł orders-impl zależący od orders-api i np accounts-api
- moduł trading-impl zależący od trading-api i np accounts-api
- moduł accounts-impl zależący od accounts-api i np orders-api
Jak widać jest faktyczny cykl między accounts i orders, ale dzięki rozbiciu na moduły API i impl nie ma tego cyklu w hierarchii modułów. Na koniec trzeba dorzucić moduł który spina całą resztę do kupy. Nazwijmy go wiring. Moduł ten zależy od wszystkich innych. Uruchamia wszystkie moduły typu impl, wyciąga z nich obiekty implementujące API i przekazuje te obiekty do modułów zależnych od tych API. Czyli w naszym przypadku moduł wiring:
- stawia orders-impl, trading-impl i accounts-impl i wyciąga z nich obiekty implementujące odpowiednio orders-api, trading-api i accounts-api
- obiekt implementujący orders-api jest podrzucany do modułu accounts-impl
- obiekt implementujący accounts-api jest podrzucany do modułów orders-impl i trading-impl
Mikroserwisowa hierarchia modułów trochę podobnych wad i zalet co prawdziwe mikroserwisy. Zaletą jest jak już wspomniałem możliwość bezproblemowego robienia cyklicznych zależności oraz brak tendencji upychania funkcjonalności do wspólnego modułu (gdyż takiego nie ma). Wadą jest to, że np moduły muszą być odporne na chwilowy brak zależności. Np przy starcie moduły typu impl nie widzą innych modułów, dopiero po wystartowaniu moduł wiring podrzuca implementacje API do konkretnych modułów korzystających z nich. Jest to taka sama sytuacja jak w rzeczywistych mikroserwisach. Rzeczywiste mikroserwisy startują w dowolnej kolejności, więc jeśli mikroserwis A zależy od mikroserwisu B, ale mikroserwis A wystartował przed mikroserwisem B, to mikroserwis A musi trochę poczekać zanim zacznie korzystać z mikroserwisu B. Wadą mikroserwisowej hierarchii modułów jest też to, że wygląda dość sztucznie, nietypowo, a ja nie widziałem takiej w praktyce w monolicie.
AventusAventusAventus