Myślę, że każdy developer, jak usłyszy, że projekt to green field i robiony jest w mikroserwisach, to od razu zaświecą mu się oczy. Mikroserwisy na greenfieldzie to taka fraza-klucz. No bo żadnego legacy, nowoczesne technologie i w ogóle. Tylko to nie jest tak… zielono.
Pułapka czystej karty
Pułapką jest właśnie czysta karta, cały ten green field. Tak naprawdę nie wiemy nic o domenie i granicach kontekstów. Oczywiście mamy ją określoną przez wymagania biznesowe i możemy zacząć coś kodować – ale robimy to na czuja.
Możemy np. utworzyć 3 mikroserwisy zamiast 2, czyli wprowadziliśmy podział w złym miejscu. Co gorsze dowiemy się o tym dopiero daleko, daleko w projekcie. Nie będzie to oczywista wiedza, po prostu od czasu do czasu będą się pojawiać problemy na poziomie komunikacji między serwisami. Będzie trzeba przekazywać praktycznie całe obiekty domenowe w jedną i drugą stronę. Będzie ewidentnie widać wzajemny coupling tych serwisów. Jeden nie będzie w stanie działać bez drugiego.
Drugim przypadkiem jest oczywiście stworzenie jednego serwisu zamiast kilku. Tutaj teoretycznie jest lepiej, bo nie mamy strat wydajnościowych na komunikacji. Możemy starać się refactorować nasz kod próbując uniknąć spaghetti. Jest to jednak trudne, jeżeli tego nie zaplanujemy zawczasu. Bo de facto tworzymy mały monolit, a chyba wszyscy wiemy, jak to przeważnie się kończy, jeśli zespół nie jest uważny. Mimo wszystko jest to zdecydowanie lepsza droga. Lepiej jest zacząć od monolitu. Grunt to go zmodularyzować.
Modularny monolit
Założeniem jest tutaj takie zbudowanie monolitu, aby łatwo można był z niego wynieść konteksty do oddzielnych mikroserwisów. Jest to oczywiście zdecydowanie trudniejsze, niż się wydaje, ponieważ w monolicie granice są zdecydowanie łatwiejsze do przekroczenia.
Bardzo dobrze sprawdza się tutaj architektura hexagonalna na poziomie kontekstów. Czyli każdy kontekst, który zidentyfikowaliśmy, tworzymy jako oddzielny moduł. Co ważne, każdy z tych modułów musi być hermetycznie zamknięty. Nie możemy sięgać do innych modułów, do ich wewnętrznych usług, nie powinniśmy też wewnątrz modułu korzystać z modeli należących do innych. Jedyny kanał komunikacji między modułami to taki, które one same definiują jako swoje publiczne API.
Pierwsza reakcja każdego developera to – “przecież to jest zbędne mapowanie obiektów”. To prawda, tak to wygląda. Jednak tym narzutem zapewniamy sobie bezpieczeństwo na przyszłość. Nasze moduły są wewnętrznie niezależne od innych. Ewentualne problemy mają tylko na punktach komunikacji. Właśnie te punkty komunikacji są też największą zaletą! W momencie, kiedy dojdziemy do wniosku, że dany moduł musi stać się oddzielnym mikroserwisem, to po prostu go wyciągamy na zewnątrz, opakowujemy i po prostu zamieniamy nasze punkty komunikacji z wywołań metod na komunikację REST czy dowolną inną. Oczywiście, to nigdy nie jest tak proste, jednak zdecydowanie ułatwia taką migrację.
Dodatkową zaletą takiego podejścia jest danie sobie czasu na zaznajomienie się z tematem. Jeżeli wdrożymy takie rozwiązanie w postaci monolitu, będziemy mogli zaobserwować na produkcji, jakie jego elementy potrzebują lepszego skalowania. Zobaczymy gdzie mamy wąskie gardła itp. Będziemy po prostu w stanie podjąć decyzje bazujące na realnych danych, a nie na domysłach.