Enterprise SOLID dla architekta czystych kodów – grupowanie

utworzone przez | 23/02/2020 | Architektura, SOLID w czystej architekturze

Zaawansowana część cyklu o zasadach SOLID. To nie będą już pojedyncze klasy. To prawdziwa architektura z pełnią mocy i odpowiedzialności. Poznasz świat komponentów. Nauczysz się jak, zgodnie z zasadami czystej architektury, organizować je i składać z właściwych klas.

Zakładając, że dobrze wykonasz pracę i zastosujesz się do zasad pojedynczej odpowiedzialności (SRP) i segregacji interfejsu (ISP), uzyskasz optymalny zestaw klas. Będą one hermetycznie zamykały swój codebase, jednocześnie pozostając otwarte na rozbudowę (OCP). Dzięki zasadzie podstawień (LSP) i odwróconych zależności (DIP) bez problemu połączysz je ze sobą.

Czujesz, że coś cię ominęło i nie znasz powyższych zasad? Przeczytaj koniecznie:

Czy te same zasady SOLID możesz zastosować do większych jednostek – komponentów? Oczywiście że tak, ale nie wprost. Pora na zaczerpnięcie odrobiny wiedzy u źródła.

Teoria SOLID-nych komponentów

Robert C. Martin bazując na SOLID wprowadził 6 zasad odnoszących się do komponentów. Obiecuję, że to jedyny tak czysto teoretyczny kawałek tego artykułu 🤓.

Teoria grupowania klas w komponentach

Trzy pierwsze zasady dotyczą spójności komponentów, zasad grupowania i organizacji klas w tych komponentach.

  • Reuse/Release Equivalence Principle (REP) – Podstawą ponownego użycia komponentu jest jego numer wydania. Ziarnistość ponownego wykorzystania i wersji musi współgrać.
  • Common Closure Principle (CCP) – W ramach komponentu zgromadź te klasy, które zmieniają się z tego samego powodu i w tym samym czasie.
  • Common Reuse Principle (CRP) – Nie zmuszaj użytkowników komponentu do zależności od rzeczy, których nie potrzebują.

Teoria łączenia komponentów

Ostatnia trójka to recepturki jak łączyć i uzależniać od siebie komponenty. 

  • Acyclic Dependencies Principle (ADP) – Nie dopuść do powstawania grafów cyklicznych w diagramie zależności komponentów.
  • Stable Dependencies Principle (SDP) – Zależność kieruj w stronę elementu stabilnego.
  • Stable Abstraction Principle (SAP) – Relacja pomiędzy stabilnością a abstrakcyjnością. Komponent powinien być tak abstrakcyjny, jak jest stabilny. 

I to tyle definicji. Wyczuwasz nosem mało przydatną teorię i niewiele ci to mówi? Nie przejmuj się. Dokładnie tyle wyniosłem z pierwszej lektury Czystej Architektury R.C. Martina. Potraktuj tę część jako słowniczek, do którego będziesz mógł wrócić w przyszłości.

Tymczasem lecimy dalej z REP, CCP, CRP i… stop! A czym tak właściwie jest mityczny komponent? 

Poszukiwanie definicji komponentu

Pierwszy strzał do Google dla frazy “komponent informatyka”

Komponent – niezależnie wytworzony, skompilowany (z ukrytymi szczegółami implementacyjnymi) moduł programowy, udostępniający swą funkcjonalność za pomocą jednoznacznie zdefiniowanego interfejsu, zdolny do współdziałania z większą całością (systemem) oraz innymi komponentami.

Zródło: https://pl.wikipedia.org/wiki/Komponent_(informatyka)

Kompilowana klasyka i skomplikowana skryptówka

W świecie języków kompilowanych, DLL–ek, JAR–ów odpowiedź na pytanie czym jest komponent wydaje się być prosta. To niezależna jednostka wdrożeniowa. Pakujesz kod do takiej jednostki, kompilujesz i dostarczasz bez dotykania pozostałych bibliotek aplikacji. 

PHP, Python, JavaScript – języki skryptowe są już wyzwaniem. Czy komponent to po prostu katalog? Może odgałęzienie przestrzeni nazw na N–tym poziomie? Brakuje jednoznaczności.

Dokumentacja frameworka Symfony (PHP) tak definiuje komponent:

Symfony Components are a set of decoupled and reusable PHP libraries. (…) You can use any of these components in your own applications independently from the Symfony Framework.

Idąc tym tropem natrafimy na pojęcie pakietu. Czyli dotrzemy do PHP–owego composera i jego odpowiedników w Python (pip) i JavaScript (npm). Całkiem nieźle 😎, ale to za mało.

Kod własny aplikacji zazwyczaj trafia w jednym kawałku do repo. Niepodzielony, bez wyraźnie oznaczonych jednostek wdrożeniowych. To z kolei negatywnie wpływa na efektywność pracy, zwłaszcza gdy kod jest na tyle duży, że musi być współdzielony przez kilka zespołów.

Jak zatem powinny wyglądać komponenty własne aplikacji?

Komponenty w praktyce

W poprzednim artykule (Premium SOLID dla praktykujących czystą architekturę), pojawił się przypadek startupu Mozartify, który został zaimplementowany przez zespół Janka.

Janek Koderek to doświadczony programista i świeżo upieczony Tech Lead. Naczytał się dużo o czystym kodzie i popełnił go wiele w praktyce. Z zamiłowania jest architektem oprogramowania i fanem metodyk zwinnych.

W dalszej części tego artykułu zobaczysz, jak Janek poradził sobie z czystą architekturą i wydzieleniem core’owego komponentu subskrypcji w aplikacji Mozartify. A zrobił to zgodnie z trzema pierwszymi zasadami czystej architektury komponentów: REP, CCP, CRP.

Komponent Mozartify\Subscription

Popatrz na jedną z pierwszych wersji komponentu subskrypcji. Jest to idealny przykład samoopisującego się kodu.

Komponent Mozartify\Subscription
Komponent Mozartify\Subscription

Komponent mówi:

Jestem modułem subskrypcji. Tu (Domain) jest opis moich możliwości – domena. Posiadam persystencję(FileStorage) i potrafię komunikować się z systemem marketing automation (Pardot).

Janek przygotował dobrą strukturę plików i katalogów. Ale wiedział, że aby skutecznie wdrożyć czystą architekturę potrzeba czegoś jeszcze. Systemowego podejścia w definiowaniu zależności.

Reuse/Release Equivalence Principle (REP)

Wersjonuj komponenty, deklaruj kontakty i je spełniaj. Czy uzależniłbyś się od komponentu, który jest dostarczany jako paczka ZIP bez metadanych, określających jego wersję i zależności od innych pakietów? Pewnie nie. 

Managery pakietów

Dlatego tak chętnie korzystasz z managerów pakietów (composerpipnpm). Również z tego powodu w komponencie poczynionym przez zespół Janka, pojawił się plik composer.json.

Dzięki temu mógł precyzyjnie związać elementy aplikacji. Nie musi się przejmować tym, że update wciągnie coś, co wystrzeli apkę na Marsa…. to nie projekt dla NASA 🚀.

Na przykład komponent Mozartify\Web zależy od dwóch core’owych pakietów: Subscription i Player. Widać to jak na dłoni.

Komponent Mozartify\Web to framework i warstwa kontrolerów odpowiedzialny za warstwę interakcji z userem: Symfony, Laravel w PHP, pythonowy Flask lub Django albo js-owa pochodna Expressa.

Wersjonowanie semantyczne

Ważne, że zespół pracujący nad Mozartify\Web wie, kiedy musi wykonać znaczną pracę. Dokładnie wtedy, gdy któryś z pakietów core’owych podbije pierwszą cyferkę, czyli zmieni publiczny kontrakt. Wiemy to, dzięki konwencji SemVer.

No tak. Ale mówimy o językach skryptowych. W nich nie ma jarów i innych dll-owych czarów. Wystarczy zmienić plik i poczekać na odświeżenie pamięci podręcznej serwera, albo wykonać reload serwera http.

Wartość podziału na komponenty

Czy w takim razie jest sens dzielić kod na komponenty? Przecież masz testy, ciągłą integrację. Wszystko na jednej gałęzi w repozytorium… I ciężko zarządzalny monolit za jeden kodo–rok.

Grupując klasy w komponenty, już na samym początku projektu, chronisz się przed monolitem. Jednak zanim usiądziesz do dzielenia, przeczytaj artykuł do końca. Musisz uwzględnić jeszcze dwie zasady konstruowania pakietów: wspólnego domknięcia i ponownego użycia.

Drugą istotną kwestią jest topologia zespołów. Codebase projektu rośnie, zespół się skaluje. Część ekipy jest na miejscu w biurze, inna na drugim krańcu kabla ethernetowego i jeszcze z przesunięciem strefy czasowej 😈. Współpraca nad monolitycznym workiem ficzerów stanie się niemożliwa. Ale mając komponenty, zwiększasz szanse, że git merge będzie przyjemnością.

Równorzędność ponownego wykorzystania i wydania

Kilka faktów dotyczących komponentów:

  • Im większe komponenty, tym wydanie jest łatwiejsze w przygotowaniu.
  • Ale im większe komponenty, tym trudniejsze jest zapewnienie ponownego wykorzystania.
  • Z drugiej strony im mniejsze komponenty, tym łatwiejsze jest zapewnienie ponownego wykorzystania.
  • Niemniej jednak, im mniejsze komponenty, tym wydanie jest trudniejsze w przygotowaniu.

Niemożliwe jest, aby w 100% spełnić te przeciwstawne wymagania. Ważne jest zachowanie równowagi między releasem a reużyciem. Co nie oznacza, że pożądanym stanem jest spełnienie wymagań pół na pół. 

W dalszej części poznasz dwie zasady, dwa różne punkty widzenia, które pozwolą ci na dokonanie właściwego wyboru.

Common Closure Principle (CCP)

Pamiętasz zasadę pojedynczej odpowiedzialności (SRP) w SOLID? Zasada wspólnego domknięcia  jest czymś analogicznym. Chodzi o to, abyś w ramach komponentu zgrupował klasy, które zmieniają się z tego samego powodu.

Zachłannie grupuj

Przyjrzyj się komponentowi Subscription. Składa się z dwóch klas opisujących domenę (Domain, DomainException), klasy odpowiedzialnej za persystencję (FileStorage) i adaptera do systemu e-commerce (Pardot). Z jakich powodów będą się zmieniały klasy w tym komponencie? Z dokładnie jednego powodu. Modyfikacja kodu będzie efektem zmian w zasadach subskrypcji usługi Mozartify.

Komponent to jednostka wdrożeniowa. Pomyśl o nim jak o mikroserwisie, którym opiekuje się pojedynczy zespół developerski. Niech taki komponent ma wszystkie klasy niezbędne do działania. 

Nie dziel za wcześnie

Janek wiedział, że nietrafionym pomysłem będzie zbyt wczesny podział aplikacji na wiele komponentów. Chciał uniknąć tych niepotrzebnych, które później są jak kula u nogi. Zbytnie rozdrobnienie skutkuje tym, że wprowadzenie nowej funkcji oznacza zmianę kilku takich jednostek. Pojedynczy komponent przestaje być wtedy niezależnym blokiem wdrożeniowym.

Scalenie komponentów oczywiście jest możliwe, ale to żmudny i błędogenny proces 🐞. Zmiana przestrzeni nazw, testy, integracja, czasem konieczność komunikacji z innymi zespołami.

Identyfikuj czynności 

Zauważyłeś, że domena komponentu subskrypcji to jedna klasa?

Janek wiedział, że naszym naturalnym odruchem jest próba przypisywania nazw – rzeczowników. Czyli usilne poszukiwanie encji.

Zamiast tego zidentyfikował i zgrupował zachowania – czasowniki. Operacje, za które komponent będzie odpowiedzialny. Tak powstała jedna klasa (fasada operacji domenowych), zgodna z zasadą SRP. Klasa ma jeden powód do zmiany, którym jest modyfikacja reguł biznesowych procesu subskrypcji.

W kolejnej iteracji Janek zauważył pewną nadmiarową odpowiedzialność komponentu i dokonał niezbędnej korekty.

Common Reuse Principle (CRP)

Zbędną odpowiedzialnością Mozartify\Subscription była metoda przygotowująca ofertę dla istniejącego i nowego subskrybenta (prepareCommercialOffer).

Do wszystkiego czyli do niczego

Od czego może zależeć oferta? Od cennika i zasad rabatowania w dziale handlowym, od programu lojalnościowego i od innych dziwnych kompilacji pomysłów działu marketingu i e-commerce. To wszystko będzie miało wpływ na komponent Mozartify\Subscribe. Mało stabilna perspektywa.

Janek nie chciał, aby komponent subskrypcji stał się nieprzydatny. Tak by się stało, jeśli pozostałby zależny od niestabilności, związanych z programami lojalnościowymi. Kto chciałby używać tak dynamicznie zmieniającego się komponentu?

Segregacja interfejsów 

Dlatego również w przypadku komponentów niezbędna jest segregacja interfejsów, czyli takie wysokopoziomowe ISP.

Nowy komponent Mozartify\Marketplace
Nowy komponent Mozartify\Marketplace

Janek był zadowolony. Od teraz jeśli będzie miał potrzebę uzależnić jakiś komponent od Mozartify\Subscibe, to zrobi to z chęcią. Nie zwiąże się ze zbędnymi zależnościami. One są już gdzie indziej – w Mozartify\Marketplace.

Nie przeginaj z podziałem

Teoretycznie im mniejsze komponenty tym większe szanse łatwego ponownego użycia. Pozornie. Wyobraź sobie sytuację, w której wprowadzenie zmiany wymaga podbicia wersji pięciu komponentów składowych. To już generuje narzut.

Dołóż do tego zabawny fakt, że trzy komponenty są pod opieką twojego zespołu. Ale dwa kolejne trafiły do innych zespołów. Niezbędna będzie skuteczna komunikacja. O wciągnięciu zmiany wszędzie “na raz” możesz zapomnieć.  Aha i ten komponent w twoim zespole, był przez kilka lat utrzymywany przez jednego developera, który trzy miesiące temu złożył wypowiedzenie i już nie pracuje.

Taka całkiem realna kompilacja przypadków techniczno–społecznych. A wiadomo, że jak coś może pójść nie tak, to…

Czyli miej, w przysłowiowym tyle głowy, konieczność zachowania równorzędności ponownego wykorzystania oraz wydania.

Podsumowanie

Po lekturze artykułu, możesz śmiało odhaczyć na swojej todo–liście umiejętność grupowania klas w komponenty. Zostało coś jeszcze do omówienia.

Na poziomie klas SOLID zwykle jest jednoznaczny. Klasa nie może być częściowo abstrakcyjna, ani częściowo stabilna. Zależności, stabilności, abstrakcje i relacje. Płynnie zmierzamy do zasad łączenia komponentów, o których w kolejnym odcinku.

O autorze

O autorze

Michał Cisz

Programistycznie od kilkunastu lat związany z ekosystemem PHP/JS/PostgreSQL. Fan czystej do bólu architektury, metodyk agile i programowania ekstremalnego, dbający o przestrzeganie zasad SOLID.

Czysta architektura okiem ekipy migawka.it

Jakie problemy napotkaliśmy na początku? W czym nam pomogła? Kiedy na pewno z niej nie skorzystamy?

Nie przeszkadzaj mi! Jak nie dać się wyrwać z flow

Ile razy ostatnio zostałeś brutalnie oderwany od swojej pracy? Zaszywasz się w biurze i nagle pojawiają się oni – nieoczekiwani goście i ich pytania, które nie niosą ze sobą grozy awarii albo powagi hotfixa. Ale są to pytania, które teraz, dokładnie w tym momencie muszą paść i oderwać cię od bieżącej pracy. Jak sobie z nimi poradzić?

Precyzyjne twoje wiadomości być muszą

Dostajesz wiadomość e-mail i zastanawiasz się, czy to na pewno do ciebie i co właściwie masz zrobić. Wysyłasz pilną informację na komunikatorze i czekasz tydzień na odpowiedź. Dlaczego? Przecież wszystko było jasne. Najwyraźniej jednak nie było. Chwila moment i dowiesz się wszystkiego.

Testowanie wielu pól

Ponoć kiedy kod jest już napisany nie da się stwierdzić, czy był pisany w modelu TDD. Jest jednak kilka śladów zbrodni, które pozostają. Na przykład testy sprawdzające zbyt wiele rzeczy na raz i zakładające, że czytelnik posiada wiedzę tajemną.

Te bezsensowne interakcje przerywające pracę

Ile razy byłeś poirytowanie faktem, że musisz w pięciu mailach wyjaśniać o co chodzi? Ile razy miałeś wrażenie, że banalne tematy ciągną się w nieskończoność, a ty czułeś się jak niezastąpiona jednostka centralna, bez której nic w zespole się nie zadzieje? Oczywiście wszystko kosztem twojej efektywności i nieustannego przerywania własnej pracy. Posłuchaj pewnej historii i naucz się prostej techniki, która wyeliminuje zbędne interakcje.

Praca zdalna – komunikacja na czacie

Jak przenieść codzienne rozmowy z biura do świata online? Co z bezcennymi dyskusjami w kuchni? Najczęściej wrzucamy całą firmę na jakiś komunikator i liczymy, że zadzieje się magia. Nic bardziej mylnego i dziś opowiem ci jak sobie z tym poradzić.

Funkcja trackback/Funkcja pingback

  1. Premium SOLID dla praktykujących czystą architekturę - migawka.it - […] Enterprise SOLID: połówka pierwsza (grupowanie) […]
  2. Zasady SOLID w czystej architekturze - migawka.it - […] Klik! […]
  3. Enterprise SOLID dla architekta czystych kodów – łączenie - migawka.it - […] poprzedniej części wprowadziliśmy odrobinę teorii czym są zasady SOLID w odniesieniu do komponentów. Następnie skupiliśmy, się jak projektować komponenty, czyli w skrócie zapewniać…

Prześlij komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Pozostań w kontakcie