Modulárny monolit zachováva jedno spoločné nasadenie aplikácie, ale kód rozdeľuje do modulov s jasne definovanými zodpovednosťami. Každý modul publikuje iba obmedzené rozhranie a detaily svojej implementácie skrýva za ním. Táto štruktúra zviditeľňuje závislosti bez toho, aby bolo potrebné pridávať sieťové volania či zložitejšie prevádzkové prostredia.
Ako fungujú hranice modulov
Modul zoskupuje kód a dátové pravidlá pre jednu ucelenú funkciu. Ostatné moduly využívajú jeho verejné rozhranie namiesto toho, aby priamo importovali interné triedy alebo modifikovali jeho záznamy. Tieto pravidlá je možné vynútiť prostredníctvom viditeľnosti v danom programovacom jazyku, konfiguráciou buildu, testami závislostí alebo štruktúrou repozitára.
| Oblasť hranice | Slabé oddelenie | Explicitné pravidlo modulu |
|---|---|---|
| Prístup ku kódu | Akýkoľvek balíček importuje akúkoľvek triedu | Volať možno len verejné rozhrania |
| Prístup k dátam | Moduly priamo upravujú zdieľané tabuľky | Každú cestu zápisu vlastní práve jeden modul |
| Závislosti | Medzi funkciami vznikajú cykly | Smerovanie závislostí sa pravidelne kontroluje |
| Vlastníctvo zmien | Zodpovednosť kopíruje štruktúru priečinkov | Funkciu vlastní konkrétny tím alebo rola |
Aplikácia sa stále spúšťa, beží a nasadzuje ako jeden celok. Modularita mení jej vnútornú architektúru, nie topológiu nasadenia.
Prečo na tom v legacy kóde záleží
Legacy monolit často obsahuje užitočné hranice, ktoré sa však časom rozmazali. Refaktoring (refactoring) ich dokáže postupne obnoviť. Tím môže presunúť jednu funkciu za rozhranie, presmerovať volajúcich a overiť aktuálne správanie pred tým, než začne meniť ďalšiu časť.
Audit hľadá indície, ktoré najmä naznačujú životaschopný modul:
- Súdržné správanie (cohesive behaviour). Pravidlá a procesy, ktoré sa menia z rovnakého biznisového dôvodu.
- Rozpoznateľné vlastníctvo (recognisable ownership). Dáta a operácie, ktoré patria k jednej ucelenej funkcii.
- Obmedzené závislosti (limited dependencies). Zvládnuteľný počet volaní smerom dovnútra a von z navrhovanej hranice.
- Nezávislé overenie (independent verification). Testy, ktoré dokážu modul otestovať priamo cez jeho rozhranie.
Tieto signály sú oveľa spoľahlivejšie ako delenie kódu čisto podľa technických vrstiev. Priečinok pre používateľské rozhranie, priečinok pre služby a priečinok pre dáta môžu totiž všetky obsahovať časti tej istej funkcie.
Modulárny monolit verzus samostatné služby
Modulárny monolit využíva volania v rámci jedného procesu (in-process) a jedno spoločné nasadenie. Samostatné služby komunikujú cez sieť a môžu mať nezávislé releasy. To so sebou prináša dodatočné prevádzkové riziká a chybové stavy, vrátane timeoutov, čiastočnej dostupnosti a kompatibility kontraktov.
Vyčlenenie služby môže byť opodstatnené, keď určitá funkcia vyžaduje nezávislé škálovanie, nasadzovanie, technológiu alebo vlastníctvo. Dovtedy však môže interný modul poskytnúť rovnaké logické oddelenie s oveľa menším počtom pohyblivých častí. Ohraničený kontext môže usmerniť obe tieto formy, pretože definuje hranicu modelu, a nie samotný spôsob nasadenia.
Využitie ako fáza migrácie
Prvým krokom je zmapovanie súčasných závislostí a výber jednej hranice. Tím potom definuje rozhranie, presunie prístup zaň a využije testy aktuálneho správania (characterization test) na ochranu viditeľného fungovania aplikácie. Priame importy a zápisy dát sa odstránia hneď po tom, ako volajúci začnú používať rozhranie.
Tento postup môže ponechať modul vo vnútri aplikácie, alebo ho pripraviť na neskoršie vyčlenenie. Cieľom nie je dosiahnuť konkrétny počet služieb. Cieľom je kód, v ktorom je možné jasne určiť vlastníctvo, smerovanie závislostí a dopad zmien ešte pred tým, než začne ďalší krok refaktoringu.