sobota, 13 listopada 2021

Core Code & Infrastruktura

Najprościej rzecz ujmując - Core Code - jest to kod niezależny od infrastruktury i kontekstu wywołania. Wzorce służące do jego implementacji to: Command, Serwis, Repozytorium, Fabryka, Read Model, Write Model (Encja) czy Value Object. Z drugiej strony mamy tytułową Infrastrukturę do której należą np. implementacje interfejsów repozytoriów czy kontrolery HTTP/CLI. Stosując podział na Core Code i Infrastrukturę realizujemy ważny cel: odseparowanie kodu realizującego funkcjonalność od szczegółów technicznych. Istotnym elementem w poznaniu każdego z tych wzorców jest zrozumienie w jaki sposób działają ze sobą nawzajem, oraz na jakim etapie cyklu życia aplikacji występują. W tym wpisie nie skupie się na wspomnianych wzorcach, ale na istocie tytułowego podziału na Core Code i Infrstrukturę - czyli dlaczego jest on istotny i jakie korzyści przyniesie.  
 

Odseparowanie od Infrastruktury. Why even bother...?

    Odseparowanie od infrastruktury możemy zrealizować poprzez stosowania wyżej wymienionych wzorców. Jakby się na tym zastanowić to pilnując by kod dzielił się na Core Code i Infrastrukturę, realizujemy główne założenia OOP - wprowadzanie warstw abstrakcji. Biorąc za przykłąd Serwis Aplikacyjny - czytając jego kod, nie musimy zaprzątać sobie głowy szczegółami technicznymi zapisu danych do bazu ponieważ Serwis posiada zależność w postaci Interfejs Repozytorium, która skutecznie ukrywa złożoność tego zagadnienia. Jeżeli będziemy potrzebowali tej wiedzy, po prostu wyszukamy Implementacje Repozytorium ukrywającą techniczne aspekty zapisy. Dzięki temu nie jesteśmy zbyt wcześnie obciążeni więdzą, której na danym etapie w ogóle nie potrzebujemy. 

    Często wymienianą zaletą odseparowania Core Code od Infrastruktury jest wymienialność szczegółów implementacyjnych jak np. baza danych. Prawde mówiąc wymiana bazy na inną jest dość rzadko występująca czynność w czasie życia projektu, ale jeżeli bylibyśmy do niej zmuszeni np. z przyczyn wydajnościowych to przy wydzielonej Warstwie Infrastruktury jesteśmy do tego zdolni porównywalnie mniejszym nakładem pracy. Pojęcie szczegółu implementacyjnego nie ogranicza się jedynie do bazy danych, ale jesto to o wiele szersze spektrum, gdzie do Infrastruktury zaliczamy całe frameworki, biblioteki, połączenia z zewnętrznymu systemami. Od wszystkich tych zależności możemy się odgrodzić warstwą abstrakcji co w nieznanej przyszłości może się okazać nieocenioną zaletą. Jak to powiedział Michael Feathers:  

"Avoid littering direct calls to library classes in your code. You might think that you’ll never change them, but that can become a self-fulfilling prophecy"

    Można wywnioskować, że to co robimy to izolowanie się od technologii służącej do realizowania funkcjonalności, która nie definiuje funkcjonalności samej w sobie. Przez to, że mamy fizycznie rozdzielone miejsca obsługi zapisu do bazy danych konkretnej Encji i definicji samej Encji - utrzymanie projektu staje się prostrze. Na przykład chcąc podnieść wersje biblioteki obsługującej połączenie z bazą danych będziemy operowali jedynie na Warstwie Infrastruktury nie ruszając przy tym kwestii biznesowych, w wyniku czego ryzyko nieumyślnego wprowadzenia zmiany funkcjonalności biznesowej znacząco maleje.

Fizyczne rozdzielenie - czyli tworzenie oddzielnych klas domeny problemu oraz infrastrukturowych w osobnych Warstwach. Warstwy według standardowego modelu dzielą się na Aplikację, Domenę oraz Infrastrukturę - realizowane są przez namespace'y. Jest jasno określone, jaką wiedzę mogą posiadać klasy w danej warstwie o bytach (klasy, interfejsy, enum) z innej warstwy. Jako, że PHP natywnie nie ma zaimplementowanego mechanizumu enkapsulacji przestrzeni nazw, nic nie stoi na przeszkodzie by zaimplementować zły kierunek komunikacji np. Wastwa Aplikacji posiada odwołania do klas z Warstwy Infrastruktury. Jedynie dyscyplina developerów w przestrzeganiu zasad może pilnować tej poprawności (być może przy pomocy analizy statycznej).  

    Z wyizolowaną Warstwą Infrastruktury sprawiamy, że możemy stosować TDD bo klasy z Core Code są łatwe w testowaniu jednostkowym. Dodatkowy nakład pracy może prowadzić do powstania modelu domenowego wedle DDD. Stosowanie tych technik niewątpliwie przyczyni się do jakości wytwarzanego oprogramowania. Dodatkowo, tworzenie klas z myślą o odseparowanej infrastrukturze, naturalnie prowadzi do implementacji popularnego wzorca architektonicznego Porty i Adaptery.  

Wewnętrzna jakość oprogramowania

    Przez Jakość Wewnętrzną rozumie się szerokopojętą czytelność kodu, łatwość w utrzymaniu i rozwijaniu aplikacji. Wydaje się, że na jakości kodu zależy głównie programistom, gdyż dla ludzi biznesu jej istotność na pierwszy rzut oka nie jest taka oczywista. Jako że oprogramowanie tworzone jest w sposób iteracyjny, pisane klasy muszą realizować spójne funkcjonalności, tworzyć luźno powiązane większe struktury i być pokryte należytą ilością testów jednostkowych. Nigdy nie wiadomo jakie zmiany najdejdą w kolejnych iteracjach, więc musimy starać się tworzyć solidne elementy budulcowe (klasy/grupy klas/moduły) w całym projekcie. Utrzymując ścisłą dyscypline przy tworzeniu wysokiej jakości oprogramowania sprawiamy, że będziemy mogli modyfikować zachowanie oprogramowania w sposób przewidywalny i bezpieczny, minimalizując ryzyko, że zmiana będzie wymagała dużej przeróbki. 

    Oprogramowanie jest oczywiście tworzone do realizowania celów biznesowych, ale musi ono też służyć developerom. Moglibyśmy napisać kiepski kod spełniający w 100% wymagania klienta (o wysokiej jakości zewnętrznej), ale wprowadzanie zmian w oprogramowaniu niskiej jakości jest skomplikowane, nieprzewidywalne i ryzykowne - z czasem wymagające coraz większej uwagi developera i nakładu czasu. 

Podsumowując: 

  1. im łatwiej i pewniej wprowadzać zmiany w oprogramowaniu tym lepiej
  2. by łatwiej wprowadzać zmiany w oprogramowaniu trzeba pisać kod o wysokiej jakości wewnętrznej
  3. istnieje szereg technik do osiągnięcia wysokiej jakości kodu   

    Jak widać tematy te są pochodną wydzielania Core Code i wzajemnie się zazębiają. Stosując wzorce Rdzenia Aplikacji będziemy mogli łatwo przetestować jednostkowo tworzone obiekty, co dowodzi że kod jest modułowy oraz niezależny od kontekstu co jest równoznaczne z wysoką jakością.    

Wzorce Projektowe

    Wymienione na samym początku wzorce, pomagają w pisaniu kodu odseparowanego od infrastruktury. Opracowane były na podstawie sumy doświadczeń innych programistów w budowaniu aplikacji webowych. Są one dla programisty zestawem jasno zdefiniowanych i sprawdzonych elementów budulcowych. Stosowanie wzorców sprawia, że kod jest czytelny, modułowy i łatwy w testowaniu. Developer znający całą paletę wzorców może korzystać z niej jak przybornika z narzędziami - dobierając narzędzie najlepiej dopasowane do probelmu. Nie trzeba wtedy wymyślać koła na nowo i zastanawiać się nad tym czy zastosowane rozwiązanie jest dobre. Mając sprawdzony zestaw wzorców oraz posiadając praktyczną wiedzę ich stosowania jesteśmy w stanie modyfikować oprogramowanie szybciej i pewniej. 

    W rękach developera jest to czy poszerza on swoją wiedzę w dziedzinie wytwarzania oprogramowania wysokiej jakości. Zgłębianie informacji na temat wzorców projektowych sprawia że, zaczynamy widzieć więcej. Implementując kod możemy wybiec w przyszość, przewidując jakie będzie niósł ze sobą konsekwencje, przed jakimi potencjalnymi problemami nas chroni, w jaki sposób sprawia że jest wysokiej jakości. Korzystając z danego wzorca dziesiątki bądź setki razy, posługujemy się nim coraz lepiej, widzimy jakie warianty się najlepiej sprawdzają, a w jakich sytuacjach lepiej go nie stosować.

    Można powiedzieć, że przybornik z narzędziami developera uzupełniany jest o nowe elementy gdy poszerza on swoją wiedzę - teoretyczną i praktyczną. Warto szlifować swoje umiejętności posługiwania się tymi narzędziami gdyż bezpośrednio przekładają się na umiejętności tworzenia kodu wysokiej jakości, czyniąc nasza pracę prostszą.

Na sam koniec wrzucam ciekawy cytat Matthias'a Noback'a dający wiele do myślenia:   

 “Software always becomes a mess, even if you follow all the best practices for software design  but I’m convinced that if you follow these practices it will take more time to become a mess  and this is a huge competitive advantage”

Źródła 

Wpis jak i cała koncepcja została zaczerpnięta przede wszystkim z przemyśleń Matthias'a Noback'a (blog) zawartych w jego dwóch książkach:

📕 Advanced Web Application Architecture (2020)
📕 Object Design Style Guide (2019) 

Wzmianka o Wewnętrznej jakości oprogramowania została zaczerpnięta z książki:

📕 Growing Object-Oriented Software, Guided by Tests (2009)


Brak komentarzy:

Prześlij komentarz