Model C4 zaproponowany przez Simona Browna, pomaga zobrazować kolejne abstrakcyjne poziomy systemu :
- System,
- Kontener,
- Komponent,
- Diagram klas.
Cztery powyższe pojęcia tworzą model C4, każdy z tych poziomów jest rozwinięciem poprzedniego. Rozwiązanie te nie jest zbiorem sztywnych reguł, mamy możliwość dodania, jeżeli jest potrzeba, kolejnych poziomów.
Wstęp
Dla przypomnienia grupa (wsparcia) w składzie:
Te zadanie zrobiłem na podstawie Event Stormingu Proces Level, gdzie wspólnie z kolegami ustaliliśmy Bonded Contexty. Czy ES jest wymagany do zrobienia C4?
Nie jest. Dla mnie zrozumienie domeny i przeniesienie na model C4 jest łatwiejsze z poziomu ES
Łatwiej zrobić je na podstawie ES, ponieważ mamy wyznaczone wszystkie części systemu (bounded contexty i subdomeny).
W kursie DNA poznaliśmy narzędzie structurizr. Na stronie tego narzędzia dostępne są przykłady pomagające wygenerować diagramy na poszczególne poziomy
Przykładem będzie oczywiście system “sklep z torfem”. Poszczególne poziomy rozdzieliłem na osobne posty. Niniejszy post będzie łączył pozostałe, zajmujące się tematem modelu C4.
Całości nie tworzyłem “od zera”, bazowałem na przykładach dostępnych na stronie z narzędziem structurizr. Są tam zamieszczone przykłady w różnych językach, kod został przeze mnie ubogacony i dostosowany do mojego systemu.
Opis rozwiązania
Do poprawnego działania przykładu należy założyć konto Structurizr, otrzymamy :
1 2 3 |
private const long WorkspaceId = "numer wygenerowany"; private const string ApiKey = "wartosc wygenerowana"; private const string ApiSecret = "wartosc wygenerowana"; |
Powyższą definicje stałych należy umieścić w kodzie aplikacji.
Zdefiniowanie tagów jest również pomocne, tagi ułatwiają ostylowanie. Podzieliłem je na grupy, jak przystało na „prawdziwego” programistę (pracującego w górnictwie) używam polsko-angielskiego dialektu.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
//external system, systemy zewnetrzne private static string Mail_Chimp_Tag = "System Obsługi Majli - Mail Chimp"; private static string Qiuck_Payments_Tag = "Szybkie płatności"; private static string Traditional_Bank_Tag = "Przelewy tradycyjne"; private static string External_System_Tag = "System zewnętrzny inwestora"; private static string Basket_System_Tag = "Zewnętrzny system koszykowy"; private static string Kurier_Sytem_Tag = "System kuriera"; private static string Account_Sytem_Tag = "System fakturowania"; //aktorzy, uzytkownicy private static string BOK_Person_Tag = "BOK"; private static string Torf_Person_Tag = "System Torf"; private static string Account_Person_Tag = "Księgowość"; private static string Client_Person_Tag = "Klient"; private static string Worker_Person_Tag = "Pracownik"; private static string Storekeeper_Person_Tag = "Magazynier"; private static string Admin_Person_Tag = "Administrator"; //kontenery private static string Container_DataBase = "DB container"; private static string Container_ExtenalSystem = "Systemy Container"; //kontenery - interfejsy aktorow private static string Bok_Interface = "Bok interfejs"; private static string Warehouse_Interface = "Magazyn interfejs"; private static string Account_Interface = "Ksiegowosc interfejs"; private static string WebTorf_Container = "Web Interfejs"; private static string MainSystem_Container = "Glowny system"; //komponent aplikacji pocztowej private static string Mail_Chimp_Component = "Mail Chimp"; |
Definicja workspace i modelu, opis dosyć istotny, w końcu chcemy wiedzieć czego dotyczy system .
1 2 |
Workspace workspace = new Workspace("Sklep_Torf", "Sklep sprzedający Torf"); Model model = workspace.Model; |
Kolejny krok dodanie aktorów, tu jeszcze na uwagę zasługuje parametr „Location”, który służy do określenie, który z aktorów ma być wewnątrz organizacji (tego prostokąta w linie przerywane ;-), a który na zewnątrz.
Opcje do wyboru :
- Location.External
- Location.Internal
- Location.Unspecified
Tu także można dodać wcześniej zdefiniowane tagi do aktorów.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Person user = model.AddPerson(Location.External, "Klient", "Chce zarejestrować się w systemie aby zakupić towar"); user.AddTags(Client_Person_Tag); Person worker = model.AddPerson(Location.Unspecified, "Pracownik", "Osoba zatrudniona w sklepie"); worker.AddTags(Worker_Person_Tag); Person storeKeeper = model.AddPerson(Location.Internal, "Magazynier", "Dział magazynu"); storeKeeper.AddTags(Storekeeper_Person_Tag); Person accountant = model.AddPerson(Location.External, "Księgowa", "Dział księgowości"); accountant.AddTags(Account_Person_Tag); Person admin = model.AddPerson(Location.Internal, "Admin", "Pracownik administrujący systemem"); admin.AddTags(Admin_Person_Tag); Person bok = model.AddPerson(Location.Internal, "Bok", "Biuro obsługi klienta"); bok.AddTags(BOK_Person_Tag); Person torfSys = model.AddPerson(Location.Internal, "System TORF", "System torf"); torfSys.AddTags(Torf_Person_Tag); |
Poziomy
Po tym, jakże ciekawym, wstępie uznałem, że podzielę poszczególne poziomy na osobne posty (z powodu dużej ilości kodu). Poszczególne poziomy (u mnie w kodzie) nie co się przeplatają ale każdy z postów będzie dedykowany do jednego z poziomów.
- Poziom C1,
- Poziom C2,
- Poziom C3,
- Poziom 4 – diagram klas, nie ma kodu nie ma poziomu.
Style pozostaną częścią wspólną, poniżej kod.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
Styles styles = views.Configuration.Styles; styles.Add(new ElementStyle(Tags.SoftwareSystem) { Background = "#1168bd", Color = "#ffffff" }); styles.Add(new ElementStyle(BOK_Person_Tag) { Background = "#08427b", Color = "#ffffff", Shape = Shape.Person }); styles.Add(new ElementStyle(Torf_Person_Tag) { Background = "#08427b", Color = "#ffffff", Shape = Shape.Person }); styles.Add(new ElementStyle(Account_Person_Tag) { Background = "#08427b", Color = "#ffffff", Shape = Shape.Person }); styles.Add(new ElementStyle(Client_Person_Tag) { Background = "#08427b", Color = "#ffffff", Shape = Shape.Person }); styles.Add(new ElementStyle(Worker_Person_Tag) { Background = "#08427b", Color = "#ffffff", Shape = Shape.Person }); styles.Add(new ElementStyle(Storekeeper_Person_Tag) { Background = "#08427b", Color = "#ffffff", Shape = Shape.Person }); styles.Add(new ElementStyle(Admin_Person_Tag) { Background = "#08427b", Color = "#ffffff", Shape = Shape.Person }); styles.Add(new ElementStyle(Mail_Chimp_Tag) { Background = "#B5B5B5", Color = "#ffffff", Shape = Shape.Box }); styles.Add(new ElementStyle(Qiuck_Payments_Tag) { Background = "#B5B5B5", Color = "#ffffff", Shape = Shape.Box }); styles.Add(new ElementStyle(Traditional_Bank_Tag) { Background = "#B5B5B5", Color = "#ffffff", Shape = Shape.Box }); styles.Add(new ElementStyle(External_System_Tag) { Background = "#B5B5B5", Color = "#ffffff", Shape = Shape.Box }); styles.Add(new ElementStyle(Basket_System_Tag) { Background = "#B5B5B5", Color = "#ffffff", Shape = Shape.Box }); styles.Add(new ElementStyle(Kurier_Sytem_Tag) { Background = "#B5B5B5", Color = "#ffffff", Shape = Shape.Box }); styles.Add(new ElementStyle(Account_Sytem_Tag) { Background = "#B5B5B5", Color = "#ffffff", Shape = Shape.Box }); styles.Add(new ElementStyle(Tags.Component) { Background = "#1168Bd", Color = "#ffffff" }); styles.Add(new ElementStyle(Tags.Container) { Background = "#B5B5B5", Color = "#ffffff" }); styles.Add(new ElementStyle(MainSystem_Container) { Background = "#0066FF", Color = "#ffffff", Shape = Shape.Box }); styles.Add(new ElementStyle(WebTorf_Container) { Background = "#0066FF", Color = "#ffffff", Shape = Shape.WebBrowser }); styles.Add(new ElementStyle(Container_DataBase) { Background = "#009900", Color = "#ffffff", Shape = Shape.Cylinder }); styles.Add(new ElementStyle(Container_ExtenalSystem) { Background = "#B5B5B5", Color = "#ffffff", Shape = Shape.Box }); styles.Add(new ElementStyle(Bok_Interface) { Background = "#0066FF", Color = "#ffffff", Shape = Shape.WebBrowser }); styles.Add(new ElementStyle(Warehouse_Interface) { Background = "#0066FF", Color = "#ffffff", Shape = Shape.MobileDevicePortrait }); styles.Add(new ElementStyle(Account_Interface) { Background = "#0066FF", Color = "#ffffff", Shape = Shape.WebBrowser }); styles.Add(new ElementStyle(Mail_Chimp_Component) { Background = "#08427b", Color = "#ffffff", Shape = Shape.Box }); UploadWorkspaceToStructurizr(workspace); |
Metoda „UploadWorkspaceToStructurizr” kończy zabawę z modelem C4, generuje kod i wysyła moje „wypociny” do mojego konta na structurizr.
Dokumentacja
Można również dodać dokumentacje do poszczególnych poziomów
1 2 3 4 5 6 7 8 9 10 11 12 13 |
StructurizrDocumentationTemplate template = new StructurizrDocumentationTemplate(workspace); template.AddContextSection(sklepSystem, Format.Markdown, "Sklep kontekst\n" + "\n" + ""); template.AddContainersSection(sklepSystem, Format.Markdown, "Kontenery\n" + "\n" + ""); template.AddComponentsSection(webTorf, Format.Markdown, "Komponenty\n" + "\n" + ""); |
Powyższy wpis o dokumentacji nie jest wymagany do działania całości, jest dodatkiem, ja nie korzystam z tego kodu.
Pomocne linki:
- Structurizr for .NET
- Structurizr for .NET quickstart (.NET Framework version) – na podstawie tego jest stworzony niniejszy przykład.
Dokładny opis Modelu C4 jest w kursie DNA.