Teoria
Po raz pierwszy o modelu C4 usłyszałem podczas prezentacji Sławka Sobótki na Confiturze w 2016 roku (prezentacja dostępna jest tutaj).
Opracowany przez Simona Browna model zakłada odejście od wpajanego nam od zawsze UMLa, na rzecz lżejszego, mniej formalnego sposobu wizualizacji architektury. Model C4 zakłada przedstawienie architektury z czterech poziomów zaczynając od ‚big picture’ schodząc aż na poziom klas.
Wyróżniamy następujące poziomy:
- System Context diagram – spojrzenie na system z lotu ptaka. Celem jest pokazanie otoczenia, w jakim działa nasz system.
- Container diagram – na tym poziomie schodzimy już na poziom omawianego systemu. Pokazujemy kontenery wchodzące w skład systemu. Pojedynczy kontener to osobno wdrażana i autonomiczna jednostka.
- Component diagram – schodząc niżej na poziom każdego kontenera wizualizujemy komponenty wchodzące w skład danego kontenera.
- Code – na koniec tworzony jest diagram reprezentujący kod. Może być to znany z UMLa diagram klas.
Poziom czwarty uznawany jest za opcjonalny i ze względu na znaczą zmienność często pomijany.
Tyle teorii z mojej strony. Po więcej informacji sięgnij do źródła, czyli na stronę https://c4model.com.
Praktyka
Mimo że model C4 poznałem ponad 3 lata temu, pierwszą próbę jego implementacji podjąłem zainspirowany kursem Droga nowoczesnego architekta.
Szczerze przyznam się, że nigdy podczas mojej trzynastoletniej kariery zawodowej, nie narysowałem chyba żadnego diagramu zgodnego w pełni z UML. Każda moja próba wizualizacji architektury była jakąś hybrydą miksującą ze sobą poziomy opisane przez Simona Browna.
Podobnie wyglądały moje pierwsze próby implementacji C4. Już podczas implementacji diagramu C1 wkradały się tam elementy specyficzne dla bardziej szczegółowych diagramów. Potrzebowałem zdefiniować sam sobie wytyczne, które postanowiłem Wam zaprezentować.
Struktura projektu
Kluczowe dla dalszej analizy będzie zapoznanie się ze strukturą projektu, który postanowiłem zwizualizować. Jest to autentyczny system działający produkcyjnie. Oczywiście do celów tego posta pozbyłem się z diagramów oraz struktury projektu wszystkiego, co mogło by zostać uznane za informację poufną.
C1 – System Context diagram
System FMP wchodzi w skład grupy aplikacji realizujących funkcje biznesowe przedsiębiorstwa o zasięgu europejskim. Jego diagram C1 wygląda następująco:
Założenia, jakie przyjąłem przy diagramie C1 są następujące:
- system FMP mimo swojej złożoności jest reprezentowany przez jeden prostokąt
- celem jest zaprezentowanie ekosystemu, w którym działa aplikacja
- pokazuję wszystkie systemy, z którymi FMP wchodzi w interakcję zarówno będąc bezpośrednim klientem, jak również poprzez systemy pośredniczące
C2 – Container diagram
Biorąc na tapet diagram C2 miałem następujące wątpliwości:
- Co w moim systemie jest kontenerem
- Czy na tym diagramie powinienem również zaznaczyć wszystkie systemy zewnętrzne, które znalazły się wcześniej na diagramie C1
Założenia jakie przyjąłem przy diagramie C2 są następujące:
- Kontenerem będzie baza danych
- Kontenerami będą również artefakty typu WAR czyli samodzielnie deploy’owalne aplikacje webowe
- Na diagramie pozostaną wyłącznie te systemy zewnętrzne, z którymi bezpośrednio łączą się kontenery
- Systemy do których odwołujemy się pośrednio nie są niezbędne na diagramie C2
Wracając do struktury projektu, kontenerami są artefakty następujących modułów:
- fmp-admin
- fmp-core
- fmp-cs
- fmp-rs-middleware
- fmp-web
oraz następujące aplikacje nie wchodzące w skład projektu lecz wspólne dla kilku naszych projektów:
- translation-tool
Sam diagram C2 wygląda następująco
C3 – Component diagram
Przy diagramie C3 pytania były następujące:
- Czy każdy z kontenerów powinien być rozrysowany na osobnym diagramie czy na wspólnym?
- Jak reprezentować sytuację, gdy komponent aktualnie analizowanego kontenera wchodzi w interakcję z komponentem innego kontenera? Czy zewnętrzny komponent powinien być również na diagramie czy może wystarczy, że zależność będzie do całego zewnętrznego kontenera?
- Co miałoby być pojedynczym komponentem?
Po głębszej analizie doszedłem do następujących konkluzji:
- Każdy z kontenerów będę rozrysowywał na osobnym diagramie. Dla celów tego artykułu zaprezentowany zostanie kontener fmp-web.
- Na diagramie nie umieszczam komponentów innych kontenerów. Jeśli zachodzi interakcja konkretnego komponentu to strzałka rysowana jest do/z całego zewnętrznego kontenera bez uwzględnienia, z którym zewnętrznym komponentem następuje komunikacja.
- Za komponenty uznaję:
- jary będące artefaktami procesu budowania pozostałych modułów, a które są w zależnościach modułu fmp-web tj: fmp-common-web oraz fmp-core-api
- jary związane z zewnętrznymi systemami, w szczególności jary pomagające w implementacji integracji z tymi systemami tj: translation-backend-api, sso-api
- ponieważ moduł fmp-web implementowany jest w klasycznej architekturze warstwowej to za komponent uznaję każdą warstwę tj:
- MCV Controllers
- Service layer
- moduły konfiguracyjny i security również uznane zostały za osobne komponenty, ze względu na specyficzny zakres odpowiedzialności, niezwiązany z konkretną warstwą.
Diagram C3 prezentuje się następująco: