Natalie Conklin
Natalie ConklinSenior Technology Executive

Modular Monolith - modularność drogą do mikroserwisów

Sprawdź, jak modularność może Cię doprowadzić do mikroserwisów oraz jakie ostatecznie płyną z tego korzyści.
11.05.20203 min
Modular Monolith - modularność drogą do mikroserwisów

Pisałam kiedyś, dlaczego warto budować systemy monolityczne. Jedną z ich zalet jest m.in. to, że są one proste i można je szybko zakodować. Ale, żebyśmy się dobrze zrozumieli — rozpoczynanie od monolitu nie oznacza rozpoczynania bez architektury. Architektury monolityczne stały się niemal synonimem wadliwego i trudnego do utrzymania kodu - „big ball of mud” to nazwa spopularyzowana przez Briana Foote'a i Josepha Yodera w 1997 roku. Wadliwość i bałagan nie jest jednak koniecznością.

Monolityczny zwykle oznacza, że kod systemu znajduje się w jednym repozytorium, które jest skompilowane i tworzy pojedynczy artefakt. Zgodnie z powyższą definicją chodzi tutaj bardziej o pakowanie i dystrybucję niż poziom modularności, powiązań modułów czy zdefiniowanych interfejsów.


Systemy monolityczne można budować jako zestaw luźno połączonych, modularnych komponentów z dobrze zdefiniowanymi interfejsami, umożliwiającymi łatwiejsze utrzymanie na całej linii. Skąd więc ta zła reputacja?

Pomyśl o typowej aplikacji webowej z trójwarstwową architekturą. Mamy warstwę webową, warstwę aplikacji lub logiki biznesowej oraz warstwę dostępu do danych. W przypadku Javy oznaczałoby to typowo stworzenie osobnych pakietów dla każdej warstwy, tak jak pokazano po lewej stronie diagramu.

Zmiana interakcji między pakietami w czasie


Problem polega na tym, że ponieważ każda warstwa jest w osobnym pakiecie, to każdy pakiet (lub warstwa) musi udostępniać interfejsy jako publiczne. Gdy tak się dzieje, to nie egzekwuje się tak naprawdę tego, kto może uzyskać dostęp do tych procedur, a w zasadzie możemy tego pilnować tylko na poziomie pewnej konwencji.

Tak więc, w powyższym przykładzie, z prawej strony komponent CustInterface może pominąć logikę CustApp i bezpośrednio wywołać procedury CustDataAccess. Co gorsza, po wprowadzeniu nowego zestawu funkcji, takiego jak Order, cały kontekst (a zatem konwencja) zostaje całkowicie zapomniany, interfejsy publiczne są wywoływane bezpośrednio i zaczyna się tworzyć big ball of mud (tj. zależności spaghetti).

Istnieją inne schematy architektury, takie jak hexagonal, czy ports & adaptors, ale są one po prostu kolejną formą architektury warstwowej, w której warstwę wewnętrzną oddziela się od zewnętrznej, ale nadal udostępnia się z nich interfejsy publiczne — powoduje to taki sam bałagan, jak w przypadku big ball of mud.

Częścią problemu okazuje się niewłaściwe użycie modyfikatorów dostępu, które są wbudowane w język. Jeśli, na przykład, upublicznisz wszystkie klasy, to w Javie pakiety zaczną służyć tylko do organizacji plików, podobnie do folderów, a nie wymuszaniem hermetyzacji.

Bardziej modułowy schemat tworzenia pakietów (jeszcze łatwiejszy, jeśli korzystasz z modułów wprowadzonych w Javie 9), wygląda następująco:


W powyższym schemacie klasy implementujące logikę biznesową i dostęp do danych są chronione (zaznaczone na szaro), a cały dostęp do tych procedur odbywa się za pośrednictwem zdefiniowanych interfejsów serwisów — jedynych interfejsów pakietów, które zostały upublicznione. 

Poniższy diagram powinien wyglądać znajomo — schemat ten jest prawie nie do rozróżnienia od mikroserwisu pod względem architektury.


Hermetyzowany komponent kontra mikroserwis

Monolit można napisać (lub łatwo zrefaktoryzować) przy użyciu komponentów, które zapewnią luźno powiązane moduły, dobrze zdefiniowane interfejsy i hermetyzowany dostęp do danych. Możesz także łatwo zastąpić jedną implementację inną, jeśli interfejsy pozostają spójne. Modularny monolit przybliży Cię do zwinnej architektury zapewnianej przez mikroserwisy.

Używanie mikroserwisów niesie ze sobą korzyści dla całego zespołu. Co najważniejsze, pozwolą one na osobne zarządzanie wersją i ich wydawaniem, skalowanie oraz umożliwią wykorzystanie różnych języków programowania w różnych serwisach. W miarę, jak system lub liczba zespołów zacznie się skalować, korzyści te staną się ważniejsze i ostatecznie przeważą złożoność i nieodłączne opóźnienia w komunikacji, które mikroserwisy powodują. 

Chodzi jednak o to, aby wybrać architekturę mikroserwisów, ponieważ te dodatkowe rzeczy są potrzebne i uzasadnione, a nie żeby poprawiać kod. Jeśli komponenty i ich interfejsy nie są dobrze zdefiniowane, mikroserwisy na pewno nie pomogą. Skończysz z big ball of mud… i dodatkowym opóźnieniem!

Materiały do dalszej lektury

Dołączam źródła, które bardzo pomogły mi w zrozumieniu tematu:
https://en.wikipedia.org/wiki/Decomposition_%28computer_science%29
https://mozaicworks.com/blog/modular-monolith-microservices/
http://www.codingthearchitecture.com/blogentries/1.html

<p>Loading...</p>