kw. 22, 2022

Greenfield efektywny kosztowo

Bytespack

Projektując architekturę, bardzo często zastanawiamy się w jaki sposób osiągnąć aplikację odporną na zmiany. W środowisku startupowym rynek i wymagania zmieniają się bardzo dynamicznie, co niesie za sobą drastyczne zmiany w kodzie. Jak obejść się bez sformułowania “To teraz musimy wszystko przepisać” we wczesnych fazach produktu, który jeszcze nie trafił na rynek?

CQRS - rozdzielenie odpowiedzialności zapisu i odczytu

Model ten choć z pozoru wydaje się być bardziej kosztowny niż klasyczny CRUD z jednym modelem, pozwala na bardzo łatwe modyfikacje encji zapisu lub odczytu, co umożliwia dużą elastyczność (. Mimo iż implementacja CQRS jest bardziej kosztowna niż CRUDa, inwestycja zwraca się w długim terminie.

DRY - wczesna replikacja nad wczesną abstrakcją

Często stawiając nowe projekty dochodzimy do miejsca w którym nasze struktury w kodzie przestają odpowiadać na nasze potrzeby. Pojawia się konieczność refaktoryzacji, która potrafi trwać miesiącami. Często obserwujemy złamanie zasady Liskov Substitution Principle, które dzieje się przypadkowo. Zbyt wczesna definicja abstrakcji, lub wczesne wprowadzanie domenowych typów generycznych może spowodować że zachowanie kilku encji o pozornie tej samej abstrakcji będzie się logicznie rozgałęziać w przeciwnych kierunkach. W obliczu uodpornienia się na te zmiany, na wczesnych etapach projektu warto dopuścić duplikację kodu - zdecydowanie mniejszym kosztem będzie usunięcie duplikacji kodu niż definiowanie modelu na nowo.

Kompozycja nad dziedziczenie

Dziedziczenie ma dwie podstawowe wady, które dają się we znaki w trakcie rozwijania nowych aplikacji. Zależności nie da się zmieniać w runtime aplikacji i każda nowo dodana zmiana powoduje problemy z zarządzaniem modelem, który w fluktuujących modelach biznesowych ostatecznie przeradza się w dependency hell. Z pomocą przychodzą delegacja i kompozycja. Małe atomowe moduły z dedykowaną odpowiedzialnością pozwalają nie tylko na łatwiejsze testowanie ale też na znaczną elastyczność w przypadku dużych zmian.

Używanie kompozycji powoduje że finałowy model może wykrystalizować się bardzo późno, bez jakichkolwiek złych skutków dla aplikacji, co znacznie obniża koszty modyfikacji nie przynoszących nowych funkcjonalności.

Open-closed principle

Jak radzić sobie ze zmianą jeśli kod ma być zamknięty na modyfikacje? Jeśli posiadamy przetestowane fragmenty aplikacji to nie powinniśmy modyfikować takiego fragmentu. Najpierw powinniśmy napisać testy, stworzyć nowy fragment, użyć go tam gdzie jest potrzebny a następnie sprawdzić czy stary jest jeszcze używany. To pozwala na szybki development oraz przycięcie wpływu zmian na obszarze całej aplikacji, co przekłada się korzystnie na koszt developmentu oraz kontrolę nad projektem.

Od atomowych operacji do atomowych feature’ów

Zdecydowanie lepiej przejść duży most na dwa kroki niż zdecydować że jeden krok będzie miał rozmiar mostu. Każda nowa akcja dostępna w aplikacji powinna być niepodzielna, nawet jeśli proces jest skomplikowany. Obsługa zmian w wymaganiach staje się dzięki temu jasna i przejrzysta. Posiadając atomowe funkcje w programie możemy łatwo przewidzieć scope zmiany, co zapobiega powstawaniu dużych refactorów.

Czy przestrzeganie tych zasad pomoże Ci uzyskać brak refactorów? Zdecydowanie nie. Pomoże Ci jednak projektować kod, ktory jest gotowy na bardzo duże zmiany relatywnie niewielkim kosztem, co przekłada się na wysoki komfort pracy i codehealth.