Jak rozplanować poszczególne części systemu, klasy, moduły, komponenty? Mnóstwo można by tu napisać, ale dziś tylko jedna zasad: oddzielaj to co się zmienia od tego, co się nie zmienia.
Każda nietrywialna aplikacja będzie się zmieniać. Będą zmieniać się wymagania, bo zmienia się wizja albo sytuacja klienta. Jak więc poukładać klocki tak, żeby zmiana wymagań nie wiązała sie z rozsypaniem całej budowli?
Zidentyfikować zawczasu, jaka część funkcjonalności będzie podatna na zmiany i oddelegować ją do osobnej klasy. Z projektu wyłonią się 3 obszary:
- Niezmiene serce systemu. Ten kod może się nie zmieniać całymi latami. Można raz napisać testy i one cały czas będą przechdzić, bo nic się nie zmienia.
- Polityki, używając terminologii DDD. Ten kod będzie sie zmieniał często. Klient wymyślił nowy sposób naliczania rabatu? Super, napiszmy nową klasę
RebatePolicy
, przetestujmy ją, a starą wyrzućmy. Wszystko inne działa, jak działało.
- Plumbing code. Niezbyt fascynujący kod, którego zadaniem jest sklejanie (1) i (2) w działający system.
Łatwo powiedzieć, trudniej zrobić. Cała sztuka w umiejętności zgadnięcia, co może się zmieniać i ukryciu tego pod niezmiennym interfejsem. Odwołując się do wspomnianego RebatePolicy
– co metody tej klasy powinny mieć na wejściu? Cenę, cały produkt, historię użytkownika, jakiś kod rabatowy? A na wyjściu? Nową cenę, kwotę rabatu, procent? Nie wystarczy nam wiedzieć, że algorytm rabatu będzie się zmieniał. Trzeba jeszcze tak zaprojektować moduł, żeby zmiana logiki nie wymuszała zmiany API. Cóż nam po wydzieleniu rabatów do osobnej klasy, jeżeli jej zmiana będzie wymagała zmian także w klasach, które jej używają? Tych, które miały być niezmienne!
Polityki mogą się zmieniać, ale ich interfejsy powinny być stabilne.