Jak nie dopuścić do legacy ?

Kod legacy to problem, który wielu z nas zna aż zbyt dobrze. Kilka ustępstw, brak dyscypliny i lądujemy w bagnie. Jak do tego nie dopuścić i wyczuć magiczny moment, w którym to się dzieje? Niedawno na Warszawskim JUG-u opowiadaliśmy jak użyć do tego Domain Driven Design (wideo poniżej) . Wzięliśmy na warsztat domenę usług serwisowych i pokazaliśmy kod z różnych faz projektu.  Zapraszamy do przeglądania kodu na githubie w wersjach C# oraz Java (in progress). Żeby ułatwić poruszanie się po kodzie zamieszczamy streszczenie głównych tematów wraz z odnośnikami do konkretnych fragmentów w repo.

 

Domena

Musiała być możliwa do ogarnięcia, a jednocześnie na tyle skomplikowana, żeby dało się pokazać to, co najciekawsze. Wybraliśmy wycenę serwisu klimatyzatorów i 3 koncepcje z nią związane:
  • „Zwykłą” wycenę serwisu na podstawie przepracowanego czasu i użytych części zamiennych
  • Gwarancje –  klient nie płaci za przegląd i naprawy, bo jest to część gwarancji
  • Umowy serwisowe, czyli abonament na usługi – pojedyncza usługa może być płatna lub nie w zależności od tego, czy wykorzystany czas i części mieszczą się w limitach zawartych w umowie.

Z If-em do celu

Na początek kontrprzykład, żeby było widać, co poprawiamy. Klasyk gatunku, czyli efekt if-owej jazdy bez trzymanki 😃. Można go znaleźć w repo tutaj w wersji C#.
Pewnie wielu z Was widziało coś takiego – kilka pięter if-ów, zapisywanie stanu w różnych miejscach… Kod jest bardzo szybko nieczytelny i ciężko wychwycić w nim główne koncepcje domeny.

Technicznie (Clean-code, Clean Architecture, SOLID etc)

Jaki jest pierwszy odruch, gdy widzimy, że powstaje takie spaghetti? Ogarnąć i uporządkować (oby!). Może rozbić na mniejsze funkcje, wrzucić w dobrą architekturę, zastosować wzorce. I są to bardzo ważne rzeczy. Bo z komplikującym się kodem walczy się wprowadzając abstrakcje. Ale czy na przykład tego typu abstrakcje rzeczywiście dużo pomogą? Brakuje mechanizmu, który pozwalałby rozróżniać dobre abstrakcje od nietrafionych.

Jeśli skupimy się tylko na tym efekt nie jest spektakularny. Przykład tego możecie znaleźć tutaj (na razie C#).

DDD way

Co proponuje Domain Driven Design? Użycie realnego świata do określania, które abstrakcje są dobre, a które nie! Oraz do stawiania hipotezy, czyli obstawiania, że użycie konkretnego zestawu abstrakcji najlepiej oddaje dzisiejsze wykorzystanie systemu. Ten zestaw abstrakcji to właśnie MODEL – najważniejsza koncepcja w DDD.

Słowo dzisiejsze jest bardzo istotne, bo w DDD nie modelujemy całej rzeczywistości dokładnie taką jaka jest (to często spotykany mit)! Wciąż wymyślamy pewne abstrakcje. Różnicą jest to, że odzwierciedlają one dokładnie sposób myślenia ekspertów domenowych o tym, jakie zadania powinien spełniać system. Jeśli interesuje Was bardziej kompleksowy opis DDD to znajdziecie w tym artykule (polecamy!).

Jaki powstał MODEL?

MODEL tworzymy w procesie, który często przedstawia się w postaci wiru, tzw WIRU MODELOWANIA ( modeling whirlpool ) Dlaczego akurat wir ? Bo ma zasysać wiedzę domenową do MODELU i docelowo do kodu.

Nasz model ( podobnie jak każdy inny) jest hipotezą – tzn zakładamy, że eksperci domenowi tłumaczyli rzeczywistość w taki a nie inny sposób. W szczególności zakładamy, że usłyszeliśmy od nich następujące stwierdzenia:
  •  Serwisant jedzie na INTERWENCJĘ i wykonuje podczas niej CZYNNOŚCI SERWISOWE
  • Zwykle w ramach INTERWENCJI jest realizowana 1 CZYNNOŚĆ SERWISOWA, ale czasem może ich być więcej
  • Na razie są 4 czynności serwisowe:
    • PRZEGLĄD
    • NAPRAWA
    • PRZEGLĄD GWARANCYJNY
    • NAPRAWA GWARANCYJNA
  • Każda z CZYNNOŚCI SERWISOWYCH jest wyceniana osobno.
  • WYCENA i aktualizacja 
UMOWY SERWISOWEJ są niezależne
  • Aha! I zasady wyceny będziemy zmieniać.
Pokażemy Wam 3 iteracje jakie mógł przeprowadzić hipotetyczny zespół, żeby dojść do kodu uwzględniającego wszystkie 3 koncepcje z domeny wymienione powyżej  (‚zwykłe naprawy’, gwarancje i umowy serwisowe)

Iteracja 1 – Naprawy

W tej iteracji została wzięta pod uwagę większość stwierdzeń eksperta domenowego. Mamy agregat interwencji (kolor pomarańczowy) na którym wołamy metodę finish. Do jej wywołania potrzebna jest polityka wyceny, która będzie aplikowana do czynności serwisowych wewnątrz interwencji. Żeby tę politykę uzyskać sięgamy do fabryki, która zamyka (‚enkapsuluje’) proces wyceny (który może się zmieniać).

To fabryka jest odpowiedzialna za znalezienie odpowiedniego „przepisu” na liczenie ceny. W tym najprostszym przypadku będzie to najzwyklejsze sumowanie czasu pracy przemnożonego przez stawkę i ceny części zamiennych.

Kod pierwszej iteracji możemy znaleźć tutaj w wersji C#  oraz tutaj w wersji Java.

Iteracja 2 – Gwarancje

Dodanie wyliczania ceny dla gwarancji jest przy takim modelu trywialne. Możemy dodać politykę „darmowej” wyceny która jest stosowana w momencie, gdy któraś z czynności serwisowych zostanie rozpoznana jako gwarancyjna.

Kod drugiej iteracji można znaleźć tutaj w wersji C# oraz tutaj w wersji Java.

Iteracja 3 – Umowy Serwisowe

Sytuacja z umowami serwisowymi jest odrobinę bardziej skomplikowana. Pojawił się serwis domenowy (kolor czarny) oraz dodatkowy agregat Umowy Serwisowej ( CONTRACT – kolor pomarańczowy ). Teraz to serwis odczytuje stan INTERWENCJI oraz UMOWY SERWISOWEJ ( limity do wykorzystania ). On jest odpowiedzialny za uzyskanie polityki wyceny z fabryki oraz stworzenie obiektu PRICING (wycena). Na PRICING składają się polityki wyceny oraz wykorzystanie limitów. Wobec tego może on zostać użyty zarówno do zakończenia INTERWENCJI jak i poinformowaniu UMOWY SERWISOWEJ o wykorzystanych limitach.

Kod trzeciej iteracji można znaleźć tutaj w wersji C# oraz tutaj w wersji Java.

Jaki dokładnie powstaje kod? 

I tutaj nie ma idealnej odpowiedzi 😃 To w jaki sposób najlepiej odzwierciedlić swój model  w kodzie zależy od całego zestawu tzw. driverów architektonicznych. Na jeden z nich warto zwrócić uwagę – to, jaki masz zespół i jakich ludzi będziesz mieć w zespole w przyszłości!
Jeśli kochacie programowanie funkcyjne możecie wyrzucić Dependency Injection oraz odnajdywać w modelu monady. Jeśli najlepiej idzie Wam modelowanie obiektowe i używanie ORM’a nie ma sensu od tego uciekać. Oczywiście istotne są też inne driver’y – takie jak skalowalność danej części aplikacji, które mogą wpływać na te decyzje. Niemniej aspekt ludzki jest tutaj zaskakująco często pomijany.
Zadaniem zespołu (włączając w to biznes, architektów itp) jest znalezienie swojego ‚stylu’ – trochę przypominającego styl prozy literackiej. Oczywiście warto używać narzędzi takich jak building blocki, wzorce itp. Ale  odnalezienie konkretnego sposobu ich wykorzystania to już kreatywne zadanie stojące przed zespołem
My, jako inspirację, proponujemy Wam mieszankę kodu funkcyjnego i obiektowego, którą zarówno Java jak i C# bardzo dobrze wspierają.

Przykład z kodu

Tutaj jako przykład pokazujemy Wam przebieg procesu dla najbardziej skomplikowanego przypadku – czyli kodu uwzględniającego zarówno gwarancje jak i umowy serwisowe:

Widać wyraźnie jakie agregaty ulegają zmianie (dla zachowania prostoty zapisujemy je w jednej transakcji). Poza tym sam proces obliczania ceny jest zamknięty w serwisie domenowym o następującej zawartości:

Po zajrzeniu do kodu serwisu widzimy, że cena jest obliczana na podstawie jakiejś polityki ? Jakiej ? Tego nie specyfikujemy na tym poziomie abstrakcji, ponieważ ekspert domenowy zaznaczył, że sposób liczenia ceny może ulegać zmianie.  Dokładny sposób liczenia ceny dla różnych przypadków odnajdziemy w fabryce:

Podsumowanie

Pokazaliśmy jak biznes w obrębie pewnego kontekstu może oddziaływać na to, jakich abstrakcji użyjemy w naszym kodzie. Pokazaliśmy czym jest Model – kluczowy koncept w DDD. To oczywiście tylko jeden z aspektów stosowania DDD. Nie poruszyliśmy tutaj kwestii tego, czym dokładnie są Bounded Context-y, jak je odnajdywać i integrować ze sobą. Ciekawskich uprzedzam, że pracujemy nad serią publikacji, w której te tematy poruszymy. Zachęcamy do obserwowania nas na facebook’u i twitterze.
Kategorie: DDD

1 Komentarz

Fan DDD · Luty 13, 2018 o 19:33

Fantastyczny artykuł!
Kiedy kolejna konferencja?

Dodaj komentarz

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