Oskar Dudycz

Pragmatic about programming

Generyczne mechanizmy workflowów, dynamiczne słowniki i jak to się ma do DDD

2020-01-30 oskar dudyczSztuka Programowania

cover

Cześć!

Kiedyś rozpocząłem cykl “What really grind my gears”, w którym napisałem jak bardzo mnie irytują źle napisane IFy. Cykl oczywiście skończył się na jednym odcinku. Ten mail, mógłby spokojnie być jego kolejną odsłoną. W zeszłym tygodniu napisałem na Twitter, że jeżeli zostałbym porwanym to moim “hasłem bezpieczeństwa” by było: “Zróbmy generyczny mechanizm worfklowów!”. Jeżeli zobaczysz tytuł wiadomości o takiej treści to od razu dzwoń na 997! Z góry dziękuję!

Są takie miejsca w każdym mieście, które wyglądają jak świetna lokalizacja, ale co by tam nie powstało to podziała chwilę i padnie. Żabka, fryzjer, sklep z butami, kawiarnia, biuro rachunkowe, burgerowania - nie ważne! Jak otworzysz to już włączaj stoper odliczający czas do plajty. Co chwile jakiś właściciel widząc te puste miejsce (zwolnione przez poprzedniego właściciela) myśli: “To jest mój moment!”. Otwiera swój biznes w tym miejscu i zamyka jak poprzedni. Taki to był właśnie jego moment: na zmarnowanie czasu i pieniędzy.

Podobnie w naszym zawodzie są takie miejsca:

  • silniki raportowania, z których klient będzie mógł sobie sam definiować raporty,
  • dynamiczne słowniki definiowane przez UI (włacznie z ich polami i typami),
  • formularze generujące się same na podstawie XMLa (czy też może teraz JSONa lub YAMLa)
  • generyczne mechanizmy worfklowów, które użytkownik może samemu defniować,
  • kod C# uruchamiany w przeglądarce.

Każdą z tych rzeczy nie raz widziałem i sam nawet próbowałem zrobić. Z jakim skutkiem? Takim jak sklep z zabawkami, następnie sportowy, biuro rachunkowe i Żabka na Podwalu we Wrocławiu.

Aktualnie pracuję nad wystawieniem starej funkcjonalności systemu on premise do modułów chmurowych. Jest to skarbnica “ciekawych” pomysłów:

  1. XMLe definiujące obiekty, ale też i metody, filtry itd. a nawet kod C# jako CData.
  2. Te XMLe podobnie jak i dynamiczne definicje słowników (z których mogą korzystać obiekty zdefiniowane w XMLu) zapisane są jako BLOBy w bazie relacyjnej.
  3. XMLe są zaczytywane przy starcie aplikacji i dokompliowywane w locie do assembly (włącznie ze skryptami C# w CData).
  4. Oprócz tego oczywiście pełno customowego kodu C# i XML, który również dodaje jakąś logikę do tego momentu.

Można się pośmiać, tylko że, zastanówmy się czy sami takich rozwiązań na mniejszą lub większą skalę nie napisaliśmy. Przecież nikt nie chce zrobić rozwiązania, które jest złe. Raczej nikt nie siada z myślą “no teraz to sobie wreszcie kawał ch… kodu napiszę!“.

Na początku dostajemy do napisania prosty workflow. Już mamy się zabierać, żeby go po prostu zakodować, ale dostajemy info “wiesz, bo tam w przyszłości pewnie jeszcze będzie kilka podobnych przepływów”. Myślimy, więc “hm, może jednak uogólniłbym to, to bym miał mniej roboty potem, zrobię sobie konfigurację w XML, wrzucę tam nazwy pól i z jakie mogą być statusy i już”. No i fajnie, pierwsze rozwiązanie działa, drugi przepływ, faktycznie podobny. Nieco inny, ale usprawniamy delikatnie strukturę XMLa, poprawiamy trochę parser i jedziemy dalej.

Potem pojawia się kolejny przepływ, który już nie jest tak podobny do tych dwóch poprzednich. Mamy jeszcze zapał racjonalizatorski, więc dajemy możliwość dopisania skryptów. Będzie przecież w ogóle dynamicznie. Wytłumaczy się klientowi, kilka podstaw i jeszcze będzie szczęśliwy. Zapisujemy to do bazy jako BLOB i YOLO. Wdrażamy! Nawet zadziałało, klient zadowolony, zaczynamy już inne zadania i moduł. No i padamy ofiarą swojego sukcesu, klient to używa, ale wpadają błędy, zgłoszenia. “Wiesz Oskar, to jest super, ale jakby w tym miejscu to działało inaczej”, “a jakbyś tutaj mi dodał te jedno pole”, “a tutaj jeden checkbox”, “tak wiem, że sam mogę to dodać, ale patrz co mi się tutaj zrobiło w windowsie… A jak chcę zapisać zmiany to mam kliknąć przycisk Zapisz? Nieeeee to za trudne, weź to zrób, dobrze?”.

Coraz mniej czasu, dokumentacji nie zrobiliśmy, inni zaczynają tam grzebać. Nie doceniają naszej inwencji twórczej i zaczynają robić obejścia. Poza tym nie są przywiązani tak jak my do naszego genialnego dzieła. Naszego dziecka, skarbu. Widzą, że ten cały mechanizm generyczny tak naprawdę powinien obsłużyć 5 przepływów, klient nigdy sam nic nie zdefiniował samemu. Generyczne słowniki, które też miał dodawać to my zawsze sami definiujemy i wczytujemy z Excela przy instalacji.

Dlatego tak właśnie lubię Event Sourcing, CQRS i DDD. Kładą nacisk na dobre modelowanie biznesu, na zrozumienie procesu oraz skupieniu się na nim. CQRS pozwala skupić się na tym jak obsłużyć komendę reprezentującą proces od A do Z oraz jak efektywnie pobrać dane przy pomocy Query. Event Sourcing z kolei wychodzi od wyodrębnienia zdarzeń, które zachodzą w trakcie procesu biznesowego i użycie ich jako checkpointów.

Wracając do mojego przykładu z roboty i odpowiadając na to jaki z DDD by miało związek? Jednym z najważniejszych jego wzorców jest podział naszego systemu na tzw. Bounded Contexty (niezależne moduły, autonomiczne jednostki, podsystemy). Przeprowadza się w tym procesie również analizę jak ten kontekst ma się do naszego biznesu. Ocenia się czy to jest faktycznie kluczowa rzecz dla nas, coś co jest jego motywem przewodnim i tym za co chcemy brać pieniądze. Jeśli np. taką analizę by zrobiono w moim systemie to pewnie doszłoby się do wniosku, że niekoniecznie generyczny mechanizm workflowów jest kluczowy do sukcesu systemu do zarządzania projektami budowlanymi. Pewnie ktoś by jednak pomyślał, że może jednak lepiej skupić się na czymć co będzie docelowo bardziej potrzebne klientom. A przede wszystkim, czy biorąc pod uwagę, że nie jest to funkcjonalność, o którą zabiegają klienci, a więc nie będzie naszym topowym priorytetem, to czy będziemy w stanie to utrzymać.

Jak powstaje spaghetti code? Dzień po dniu. Tak samo przekomplikowane rozwiązania. To wszystko nie musi zaczynać się od wielkich mechanizmów. To zaczyna się od podstaw. Od dobrze stawianych ifów. Od zastanowienia się czy na pewno musimy uogólniać nasze rozwiązanie. Od zastanowienia się przed dodaniem generycznego typu i klasy abstrakcyjnej (mój rekord to 14 parametrów generycznych, a Twój?). Dla mnie najlepsze rozwiązanie to takie, na które się spojrzy i powie “wow, jakie to proste”.

A jak to wygląda u Ciebie?

Jakie dziwne konstrukcje zdarzyło Ci się “przeinżynierować”?

Pozdrawiam serdecznie i życzę żeby Twoje rozwiązania nie były jak te martwe punkty usługowe w mieście!

P.S. Świetny artykuł na ten temat napisał też Dan Abramov, jego lektura też mnie zainspirowała do tego wpisu: Goodbye Clean Code.

  • © Oskar Dudycz 2019-2020