sobota, 23 maja 2026

Skill analizy kolejna analiza

Po uruchomieniu wersji drugiej skilla i przejściu przez kontekst rejestracji jestem bardziej zadowolony niż na początku. Ale wynikł problem wybrałem tryb numer 1, dla przypomnienia, mam dostępne cztery:

## Tryb 1 — Agentowy (Claude orkiestruje)

Claude dzieli domenę na konteksty i przydziela je sub-agentom.

### Tryb 2 — Ekspercki (użytkownik jako ekspert domenowy) ← domyślny

Sub-agent zadaje pytania, użytkownik odpowiada.Claude śledzi spójność między odpowiedziami i sygnalizuje konflikty.

### Tryb 3 — Mixed (współpraca)

Część kontekstów trafia do sub-agentów, część użytkownik prowadzi sam.

### Tryb 4 — Użytkownik jako orkiestrator

Użytkownik decyduje co i kiedy badać, sub-agenci dostają konteksty od użytkownika.

Claude reaguje na pytania i pilnuje spójności na żądanie.

Agentowy z definicji miał działać sam, a ja miałem tylko dostać wynik, ale to claude zadawał pytania a ja odpowiadałem i tak dziesięć pytań pozwoliło pierwszy kontekst "rozpykać".

Dalsza część testów

Ściągnął sobie dane z cache i odkrył, że zrobiliśmy pierwszy etap, to idziemy dalej.

Odpowiedz prawidłowa, nie spodziewałem się.

Faza 1 — Big Picture: Generacja grup startowych
  
 Wznawiamy analizę. Kontekst z pamięci:
 - Tryb: Agentowy (Claude orkiestruje)
 - Zakres: Big Picture bazowy (bez read modeli)
 - Wejście z poprzedniej fazy: pivotal event "Opłacono uczestnictwo" + przypisany numer startowy + otwarty H1 (niedopłata)
Skończył w odpowiednim miejscu, wchodzimy w nowy kontekst zaczynający się od pivotal 

Będę wrzucał odpowiedzi Klaudiusza, co jakiś czas. Całość i tak pójdzie na repo.

 Dodatkowe pliki (linki na końcu) procesu Klaudiusza:

 - project_marathon_analiza.md — stan analizy: co ukończono, wnioski z Rejestracji, następny krok, otwarty hotspot H1
 - reference_skill_deepanalysis.md — gdzie jest skill i jak go ładować przy wznowieniu

Klaudiusz mówi:

 "Indeks tych plików jest w MEMORY.md w tym samym katalogu — to jest ładowane automatycznie przy każdym starcie sesji, dzięki czemu wiedziałem od razu gdzie
  skończyliśmy."

Spryciarz.

Katalog memory wrzucę w repo.

To jedziemy z kolejnym kontekstem.

Tym razem pytań było 11, ale dodał kilka zdarzeń których nie było i są sensowne.

Czy będę poprawiał wersje 2? nie, ta wersja jest tylko testowa nie do rozbudowy działam z tym co mam i sprawdzam kolejne kroki. 

A wcześniej jeszcze słów kilka o procesie tworzenia, tak właściwie to Klaudiusz na podstawie moich wytycznych tworzy tego skilla, teraz mam wersje trzecią ale ona wejdzie na testy gdy wyczerpie się wersja 2. Klaudiusz podpowiedział, że plik ze skillem nie może być większy niż 500 linii, jako że wersja 2 już dobija do tego. Wersja 3 będzie mocno zredukowana, tam pomysł mam zupełnie inny ale na razie testy wersji drugiej.

Zastanawiam się co wyjdzie z tego, pomysły ładuje już w rozwiązanie trzecie, ono jeszcze nie przetestowane, a już puchnie. Będzie do tego inne repo, osobne, dane wejściowe będą te same, chce to potem porównać. Ostatecznie trzeba będzie z tego kod wygenerować, ale do tego jeszcze dłuuuga droga. Jestem teraz na ponownym przejściu przez Big Picture i uzupełnieniu go, na trzecim kontekście, przed ostatnim z tego co pamiętam, już mniejszym od pozostałych.

Linki do tej wersji:




środa, 20 maja 2026

Maraton powrót

 W 2019 roku zakończyłem przygodę z maratonem rowerowym na wersji najbardziej zaawansowanej pod kątem ilości funkcji. W 2021 zrodził się pomysł stworzenia Event Stromingu w kontekście maratonu i taki powstał. Teraz w roku 2026 mam kolejną wizję, mam ES, mam jeszcze wiedzę na temat domeny, mam Klaudiusza, może czas zrobić nowy maraton? przy okazji poćwiczyć z LLM-em?

Nie lubię działać bez planu. Jaki jest plan?

  • Przygotować dane do wytworzenia systemu, który ma na celu obsługę maratonu rowerowego,
  • Przygotować skile które pozwolą w odpowiedni sposób przejść przez analizę,
  • Przygotować skille z odpowiednią architekturą i implementacją rozwiązania,
  • Pociąć ES na odpowiednie kawałki zdatne do przetrawienia przez Klaudiusza,
  • Implementować po kawałku, rozwiązania
Celem jest poprawienie mojego warsztatu w kierunku lepszego posługiwania się LLMem, odświeżenie informacji o Event Stormingu i o całym procesie wytwarzania w dobie generatywnej sztucznej inteligencji. Tak więc praca ze wsparciem.

Na początek 

Analiza z razem z kolegą, podrzucę Kladiuszowi kontekst w postaci event stormingu i opis "słowno-muzyczny", zobaczę co wygeneruje i jakie rozwiązania zaproponuje.

Będzie to też rozwinięcie poprzedniego posta, który wyznaczał bounded contexty i pivotal eventy.
No to lecimy od lewej do prawej.
  • Rejestracja,
  • Tworzenie grup,
  • Pomiar czasu,
  • Zakończenie maratonu
Wydzieliłbym te cztery główne konteksty

Rejestracja

Drogi (coraz droższy) Klaudiuszu działaj.

No to może by tak zainstalować na Kubuntu? Klaudiusz pomóż,

curl -fsSL https://claude.ai/install.sh | bash

To wystarczy bez roota

To teraz zaczynamy.

Napoczątek napisałem sobie skilla do analizy inspirowanego event stormingiem i skillem grille-me.


Spróbuje pogłębionej analizy tym skilem zobaczę co wyjdzie. Skill jest tak napisany żeby szedł od ogółu do szczegółu i posiłkował się dokumentacja czy też istniejącym event stromingiem, dopytywał się o szczegóły sądzę, że będzie trzeba go doszlifować ale na początek test numer 1.

Plan jednak się nie powiódł, dlaczego? Z powodu niedostatecznie dobrze zrobionego skilla do analizy i tym o to sposobem post o wytworzeniu aplikacji za pomocą LLM-a zmienił się na post o tematyce wytwarzania skilla do analizy.

Skill oprócz event stormingu, implementacji pewnych reguł z nie go, zawiera jeszcze inne opcje, które nie będą na razie rozwijane. Liczę na podejście ewolucyjne, plan jest taki że na bazie wsadu w postaci pocięte ES-a będę testował kolejne wersje skilla aż dostane satysfakcjonujące rozwiązanie.

Skill deep-analisis wersja 1

Krótki opis, bo sam skill znajduje się w repo, ale nim do tego dojdę to słowo wstępu moje testy poszczególnych wersji skilli będą odbywać się na danych wejściowych w postaci pociętego event stromingu process level i trochę design level. Chce dostać na wyjściu pliki z satysfakcjonującą mnie analizą która będzie podstawą dalszych kroków.

Opis Skilla - bardzo ogólny

Skill jest prosty w swej formie i wynik jak się okazało też nie był najlepszy, żadnych pytań, w pięć minut odpowiedz.


Test Skilla

Wykonał dwa kroki ES BP i PL na bazie tego co było w wsadzie,  ale wynik był mało satysfakcjonujący

Sam skill dane wejściowe oraz wynik będzie na repo w githubie.

Skill deep-analisis wersja 2


Przerobiłem trochę skilla, dodałem tam więcej rzeczy w pierwszym i drugim etapie czyli w big picrure i process level, reszta na razie nie jest istotna będzie dobry wynik na tych dwóch przejdę dalej.

Porównanie skilli jest tutaj -> porównanie 

Opis Skilla - ogólny

Tu jest bardziej rozbudowana ścieżka, w tym skillu Claude doprecyzowuje poprzez pytania, zbiera informacje. Są wbudowane tryby działania:
  • Tryb 1 — Agentowy (Claude orkiestruje)
  • Tryb 2 — Ekspercki (użytkownik jako ekspert domenowy)
  • Tryb 3 — Mixed (współpraca)
  • Tryb 4 — Użytkownik jako orkiestrator
Jest większa interakcja pomiędzy fazami

Test Skilla

Było więcej wstępnych pytań, wybrałem fazę 1 i musiałem odpowiedzieć na 10 pytań tylko z jednego kontekstu rejestracji co trwało dosyć długo, musiałem się zastanawiać na odpowiedziami LLM wykrył brakujące zdarzenia i wnioski które nie zostały nigdy opisane a mogły bo są istotne.

Wynik też jest obiecujący i ma znacznie większa wartość niż ten ze skilla wersji 1. Ale że jest już późno to na dziś koniec, jutro dalszy test, a na horyzoncie majaczy kolejna wersja Skilla jeszcze bardziej złożona. Cóż musi poczekać

Linki:












niedziela, 22 marca 2026

Jak wyjść z bagna?

Tytuł nie napawa optymizmem ale mógłbym dać taki jak wyjść z piekła? Więc to i tak duży przeskok.

Panie gdzie macie te bagno i jak wam pomóc?

WCF

O nie, to imię, którego się nie wypowiada bo przyjdzie zły pan i zabierze wam wasze dusze. Coś w tym jest, jak wchodzę do wcf-a to za każdym razem wychodzę kimś innym.

Jednak to część mojej pracy babrać się w otchłani, na szczęście na horyzoncie znajduje się latarnia która wskazuje kierunek wyjścia, tym razem to nie nadjeżdżający pociąg.

Bagno/Piekło

Czym jest w moim przypadku? No cóż to jest rozwiązanie które przez lata ewoluowało, jak zwierze poddane dobroczynnemu działaniu promieniowania, o jakie silne i jak wspaniale wykonuje swoją pracę, a po latach, dlaczego ma dwie głowy i pięć nóg i świeci?

Ewolucja kodu jest nieubłagana, to jak grządki w ogródku, nie dbasz to plonów coraz mniej. Tak i w przypadku kodu, był kiedyś gospodarz który zbudował te dzieło i pozostawił swoim następcom z uśmiechem na twarzy poszedł do innego gospodarstwa, lepszego, tylko zapomniał powiedzieć jak dbać o ogródek. Przyszedł kolejny, mniej doświadczony popatrzył jak było zrobione i zrobił "tak samo".
Potem skup powiedział że te plony to już nie takie dobre jak wcześniej po za tym dodaj kolejne warzywa i owoce żeby dywersyfikować źródła dochodu i tak kolejni gospodarze przychodzili i odchodzili pozostawiając coraz większy "porządek".

Tylko w którymś momencie przestanie się opłacać obsiewać kolejny raz, bo o plony trzeba coraz bardziej walczyć, ziemia coraz trudniejsza w uprawie, w nieskończoność działać nie będzie, to nie gra.

Co wtedy?

Dobre pytanie, jak mam odpowiedzieć na nie. Pomysł mój.

Mam wcf-a  a pod nim plątaninę różnych rozwiązań, kodu który nie da się utrzymać w dłuższej perspektywie, bazy, która kiedyś biegała z dinozaurami tylko że im się zachciało wyginąć a baza stwierdziła że ewoluuje.

Wiem że cześć endpontiów z wcf-a to proste crudy bez logiki, a wcf służy tylko do przepychania w tę z powrotem, to czemu by tego nie wykorzystać przeciw niemu?!

Proste api do crudów.

Tak sobie wymyśliłem że te api nie będzie tylko do CRUD-ów (nie wiem czemu ale kojarzy mi sie z bajką o jaskiniowcach), ale od czegoś trzeba zacząć, to na początek rzeczy proste.

Żeby wykiełkowała właściwa architektura rozwiązania i sposób tworzenia, to też powie czy podejście jest właściwe. Na prostych getach czy postach nie tracę nic, nie ma logiki można eksperymentować.

Jak już wyrobie sobie pewne rozwiązanie mogę się zainteresować czymś więcej, logiką. Ale nim do tego dojdę to dla mnie w moim rozwiązaniu jest istotny jeszcze fakt czy dany crrud jest potrzebny?

Tu warto zadać sobie pytanie czy teraz mając api i entity frameworka, model relacyjno obiektowy dalej mam zwracać to samo z endpointa? Podstawą zmiany jest żeby efekt był dalej ten sam ale pod spodem może się dziać zupełnie coś innego. Mając nowe api mogę się pokusić a wręcz powinienem o zmianę podejścia o próbę napisania tego samego po nowemu w łatwiejszy nie trudniejszy sposób.

Tak więc to jest jeden z moich postulatów, na szczęście nie będzie ich tyle ile miał Marcin Luter, które stoją u podstaw głębszego zrozumienia problemu nawet w czymś takim jak przepisanie CRUD-a.

Otchłań

Czym jest każdy wie, tam czai się największe zło, wejście wymaga stalowych nerwów i sznurka żeby wyjść, chyba,że ktoś ma na tyle siły żeby sobie wyrąbać drogę.

Tu jest pomysł taki, że owszem możemy spróbować przepisać logikę ale znowu, pytanie czy warto? Czy jest sens powtarzania kodu, który wcześniej był często rzeźnią dla umysłu? Nasuwa się pytanie jak sprawdzić czy to jeszcze działa? nie mamy testów ale czy jak przepiszemy to samo to dalej będziemy mogli sprawdzić czy działa?


Powrót do Tristram

Zastanawiałem się czy to jedyna droga takie wyciągniecie CRUD-a i wrzucenie do osobnego api - bytu, czy to nie jest przemeblowanie w piekle? Zamieniam stare na nowe, zmieniam opakowanie, a zawartość pozostaje. No właśnie może droga nie wiedzę tędy, czas wrócić do źródła zła.

Wejście do wcf-a należy zapieczętować, żeby nic już nowego z niego nie wypełzło, zatrzymać jego wpływ na otoczenie. To co działa tak pozostawić, ale nowe rzeczy wynieść do modułu, który powstanie w solucji. Moduł ten będzie "czyśćcem na zło", będzie przystankiem na drodze do "raju".

"Czyściec"

Czym jest w moim rozwiązaniu? Nie celem ale przystankiem, to moduł, który pozwoli utrzymać stary system i przy okazji będzie brał na siebie odpowiedzialność za kolejne nowe rzeczy w obrębie struktury czyli będzie warstwą DAL. Ten byt będzie mógł brać do siebie kolejne zapytani do bazy, będzie otoczony granicą przez którą zgnilizna nie wydostanie się na zewnątrz.

Taka koncepcja pozwoli nie rozpełznąć się złu z wcf-a, ale pozwoli na to żeby kolejne byty mogły udać w kierunku odkupienia, tworząc nowe lepsze zapytania, bardziej przemyślane koncepcje, optymalizacje i w końcu dzięki np zastosowaniu ORM-a będę mógł łatwiej i szybciej dowieźć zmiany. Wejście w ten moduł będzie łatwiejsze dla każdego nowego śmiałka. Dzięki temu mam też możliwość w łatwiejszy sposób dodać testy integracyjne, bo warstwa infrastruktury nie jest ukryta pod wcf-em ale dostępna za fasadą w nowym module.

Istotne w moim pomyśle jest to, że taki moduł jest ewolucją idei wyprowadzenia kontekstów po za monolit. Nie znam dokładnego procesu, nie znam reguł, ale wciąż trzeba utrzymywać te królestwo, trzeba dokładać tam nowe funkcje, poprawiać istniejące, jeżeli wcf zapieczętowany to wydaje mi się że to jest jedyna sensowna droga do tego żeby można było utrzymywać i rozwijać istniejący system.

Wcf to nie tylko crudy ale też logika, co z nią? 

To proste, jeżeli mam logikę to mogę w końcu ją też wydrzeć z pod wcf-a tworząc kolejny moduł, taki w którym mogę odseparować logikę od warstwy prezentacji i struktury ale to dalej nie cel ostateczny, a przystanek na drodze. Czyli koncepcja "czyśćca" to więcej niż jeden moduł a może i więcej niż dwa. To jest grunt, baza do dalszego rozwoju, ewolucji.

Tu też warto zwrócić uwagę na to, że ten stan jest przejściowy ale to czego nie wiem, to jak długo dane konteksty będą w "czyśćcu"? Okres trwania może być zależny od prac, które są do wykonania w tworze docelowym gdzie to wszystko trafi ale ten czas budowania "raju" będzie też zależny od złożoności procesu, kontekstu jaki ma docelowo powstać. 

Podsumowując tę część konceptu.

Mam tu do czynienia z tworem przejściowym ale z trudnym do określenia czasem przebywania tam kontekstów. Każdy z nich prędzej czy później trafi do miejsca docelowego ale będzie to zależne od procesu analizy. Dodatkowo taki podział pozwoli mi na rezygnacje z rozwijania wcf-a. Wstępną refaktoryzacje oraz wprowadzenie testów czy to jednostkowych czy integracyjnych.

"Raj"

Czym jest ta koncepcja u mnie? To odpowiedz na właściwy sposób podejścia do tworzenia, budowania, mikroserwisu, czy też wyprowadzenia części logiki, procesu poza monolit. Ten monolit, który został skażony przez wcf-a. Przez, trudną do rozwinięcia technologie i wielką plątaninę różnych koncepcji.

To jest właściwy cel, który będzie mógł zaistnieć dopiero wtedy gdy zostanie wykonany etap analizy, poznania procesu, zbadania reguł. 

Co będzie podstawą do tego tworu?

Żeby coś wyrosło na polu, to trzeba go nawieźć, a odpowiednim produktem będzie czyściec i wcf. Co tam dokładnie trafi, a co nie to będzie zależeć od procesu ale jeżeli konteksty będą się pokrywać, czyli w czyśćcu będzie to co do raju ma trafić to będzie musiało być rozwijane w dwóch miejscach ale z docelową lokalizacja w raju.

Nie będzie się dało inaczej tego załatwić. W dużej mierze konteksty będą na siebie nachodzić, ba nawet będą takie same ale, pośrednie moduły będą miały logikę bardziej pomieszaną, oczywiście w dwóch lub więcej modułach w obrębie monolitu. To pozwoli na faktyczne długofalowe pozbycie się zależności od WCF. 

Podsumowanie

W tym poście przedstawiłem dwie koncepcje, jedna, pierwsza została fizycznie rozpoczęta ale zawieszona z powodu tego, że wybór drogi to nie tylko moja decyzja, a wypracowanie konsensusu całego zespołu.

Druga koncepcja pojawiła się trochę później i jest na tę chwile teoretycznym konceptem.

Moje podejście też ewoluowało, myślałem, że ten pierwszy pomysł jest dobry ale później zdałem sobie sprawę, że są jeszcze inne koncepcje, które w dłuższej perspektywie będą lepsze.

Jednak oba podejścia zakładają zamrożenie wcf-a i wydzielenie kontekstów do nowego bytu ale za pomocą analizy procesu, event stormingu czy innego narzędzia.

Ta druga koncepcja wydaje mi się bardziej właściwa bo nie tworzymy kolejnego zewnętrznego tworu z którym będę się komunikował, a tylko dodatkowe moduły w obrębie rozwiązania - monolitu. Jednak każda z tych zmian niesie ze sobą ogromne ryzyko, że zostanie zmarnowana i przekształci się w "tego wcf-a"  od którego staram się uwolnić. 

W momencie wyboru jakiejkolwiek koncepcji spadnie na nas odpowiedzialność wprowadzenia ADR-ów, które jasno zdefiniują reguły architektury rozwiązania.

Jednak czas dotarcia do celu może być bardzo długi, to również należy wziąć pod uwagę, ale jest to cena, którą trzeba zapłacić.


poniedziałek, 10 listopada 2025

Domain Model cz4

 Kolejny raz bez kodu, kolejny raz z Cluade-m w roli analityka.

Głównym tematem dziś będą Pivotal Events i bounded contexty.

Na sesji Event Stormingu odkryliśmy trzy tzw. pivotal events. 

Czym jest pivotal event, to nie będzie definicja, bo jej nie znam, wiem tylko tyle, że takie zdarzenie określa, że system wchodzi w nowy kontekst, nowy stan, który jest nieodwracalny. Takie zdarzenie wyznacza granicę kontekstu. Zmienia słownictwo.

Wyjaśnię to zaraz na przykładzie pierwszego kontekstu i pierwszego takiego zdarzenia(pivotal event).

Poruszam się w kontekście maratonu rowerowego.

Pierwsze zdarzenie o którym mowa to 

Przypisano numer startowy

Te zdarzenie zamyka kontekst rejestracji zawodnika. 

W obrębie tego kontekstu każda osoba, która zgłosi chęć uczestnictwa w maratonie staje się jego uczestnikiem, czyli zostaje zarejestrowana w tym wydarzeniu. Oznacza to, że może wybrać dystans na którym pojedzie, musi podać wszystkie dane, które są obowiązkowe, może je dowolnie zmieniać, również dystans.

Jednak w chwili opłacenia startu, zostaje przypisany uczestnikowi numer startowy. Skutkiem czego uczestnik staje się zawodnikiem, czyli godzi się na warunki startu, wyraża chęć uczestnictwa poprzez wniesienie opłaty. W tym momencie zawodnik traci możliwość zmiany dystansu i zmiany danych, które wprowadził. Zyskuje jednak dostęp do nowego kontekstu, kontekstu związanego z generowaniem grup startowych.

Te zdarzenie jest nieodwracalne nie da się przełączyć zawodnika z powrotem na uczestnika. Zawodnik może zrezygnować ze startu,  rezygnacja zawodnika wiąże się z wykreśleniem go z puli zawodników, ale jego numer nie trafia już do wolnych numerów. Jeżeli zawodnik, który zrezygnował postanowi jednak się ponownie zarejestrować i opłacić udział otrzyma nowy numer.

Tak więc te zdarzenie jest pivotalne i wyznacza granicę kontekstu rejestracji.

Kolejne zdarzenie, które wyznacza granicę kontekstu to

Wystartowano zawodników

To zdarzenie kończy kontekst generowania i przygotowania zawodników - grup do startowych. Po tym zdarzeniu zawodnik może już tylko jechać do przodu, nie da się cofnąć startu. Zawodnik może zrezygnować ale wiąże się to z definitywnym zakończeniem zawodów i dyskwalifikacją. Zawodnik może nie ukończyć swojego dystansu, np jedzie na 400 ale przejedzie tylko 100, będzie inaczej traktowany niż ci, którzy jadą 100 km ale reguły tego traktowania są zapisane w tym nowym kontekście, który zaczyna się od zdarzenia Wystartowano zawodników.

Zastanawiałem się nad słownictwem w tym kontekście. W poprzednim uczestnik zmieniał się w zawodnika, tu nie następuje tak spektakularna zmiana. Mamy dalej zawodników ale np grupa nie ma już znaczenie żadnego, sam zawodnik jako osoba, też nie bardziej liczy się czas przejazdu i numer, dystans również. Zawodnik w tym kontekście otrzymuje nowe cechy, które go definiują czyli wspomniany wyżej czas przejazdu i pokonana odległość czyli dystans. Tyle że wcześniej ten dystans to był założeniem deklaracją teraz jest realizacją.

Kolejne ważne zdarzenia, to 

Zakończono przejazdy

Te zdarzenie kończy maraton, jest zamknięciem pomiaru czasu. Jest granicą kontekstu ponieważ po tym zdarzeniu pomiar czasu jest nie możliwy, a wszystkie zarejestrowane czasy stają się historią danej edycji maratonu. Tu należy nadmienić, że jest to maraton 24h. Każdy dystans ma swój limit czasowy, dla przykładu 600 km ma 24h, 300 km 12h kolejne dystanse krócej. Dystanse nie startowały razem, tzn. były wydzielone te, które stratowały w sobotę (maraton był z soboty na niedziele). W sobotę startowały najdłuższe dystanse czyli te, które miały ograniczenie do 24h, kolejne krótsze startowały w niedziele ale tak żeby wszyscy mogli kończyć jednocześnie.

Dzięki temu można było zakończyć maraton dla wszystkich o tej samej porze, o ustalonej godzinie. Czyli zdarzenie zakończono przejazdy też można wrzucić do worka zdarzeń pivotalnych, czyli jak się wydarzy nie można go cofnąć, jest granicą poprzedniego kontekstu, otwiera nowy w którym zawodnik jest rekordem w tabeli wyników a pod uwagę brany jest jego dystans i czas przejazdu i czy przejechał deklarowaną odległość.

Typy zdarzeń 

Claude zatrudniłem jako pomoc w obaleniu tych zdarzeń, to była bitwa na argumenty. Te trzy zdarzenia się obroniły ale okazało się, że mają nie co inny zasięg.

  • przypisano numer startowy,
  • wystartowano zawodników
Te dwa zdarzenia mają charakter bardziej lokalny, szczególnie pierwsze gdzie zmieniamy słownictwo, uczestnik zmienia się na zawodnika. Jest to bardziej widoczne niż w drugim kontekście gdzie dochodzi nam aspekt czasu. Ale te dwa zdarzenia mają charakter lokalny bo nie odnoszą się do wszystkich, zawodników, do całego systemu. Ich zaistnienie powoduje skutki w systemie ale nie tak spektakularne jak ostatni event czyli Zakończono przejazdy. 

Dlaczego charakter lokalny?

Przypisanie numeru następuje w chwili potwierdzenia wprowadzenia opłaty przez zawodnik, czyli nie każdy zawodnik w jednej chwili wprowadza opłatę. Tak samo nie wszystkie dystanse są wystartowane w o tym samym czasie. Póki nie wystartował dany dystans można się na nie go zapisać. W chwili startu następuje zamknięcie pewnych funkcji ale otwarcie nowych.

Zdarzenie globalne

Zakończono przejazdy - te zdarzenie jest inne jego uruchomienie ma charakter globalny, obejmuje wszystkich zawodników, powoduje naraz zmianę kontekstu dla całego systemu. To zdarzenie zatrzymuje pomiar czasu, ten zawodnik który nie dał rady dojechać do mety albo punktu kontrolnego będzie nie klasyfikowany na swoim dystansie lub zdyskwalifikowany. To będzie określone w tym nowym kontekście poprzez reguły służące do klasyfikacji zawodników.

Podsumowanie

Zastanawiałem się nad istotą tego posta, w poprzednim już nie powstał kod, ale tu tym bardziej kod nie powstanie, bo może wyznaczenie granic i tych ważnych zdarzeń jest dużo bardziej ważne od samego kodzenia. W dzisiejszych czasach można go zlecić sztucznej inteligencji, pytanie czy te same AI może właściwie wyznaczyć granicę kontekstów, odnaleźć takie pivotal events, zrozumieć biznes i domenę?

Po co mi te zdarzenia? Dlaczego są one tak istotne dla procesu?
  • oznaczają granicę jednego kontekstu i wyznaczenie drugiego
  • zmieniają reguły i słownictwo, poprzez zmianę kontekstu
  • są drogowskazami, które informują o nieodwracalności
  • zabierają możliwości ale dają inne
Linki:

środa, 5 listopada 2025

Domain model cz3

 Tym razem inne podejście.

Wracamy do agregatu z poprzedniej części. Tyczy się on zdarzenia Zmiana Grupy Startowej. Mamy tam opisane takie reguły jak:

  • Grupy startowe muszą być wygenerowane,
  • Nie przekroczono terminu zmian grupy,
  • Grupa docelowa jest z tego samego dystansu,
  • Zawodnik nie zmieniał wcześniej grupy,
  • Grupa docelowa musi mieć miejsce.
Określiłem reguły typu "must be" (musi być spełniona) czyli:
  • grupy startowe muszą być wygenerowane,
  • grupa docelowa musi być z tego samego dystansu
Te dwie reguły są niezmiennikami, po analizie wyszło nam, że nie da się ich pominąć ani zmienić.
Pozostałe reguły nie są już tak "niezmienne", istnieją przypadki o których wspomniałem w poprzedniej części mówiące że można nagiąć te reguły.

Reguły możliwe do pominięcia 

Wracając do sedna, reguła
  • grupa docelowa musi mieć miejsce
Według logiki nie można zmienić grupy na taką która nie ma miejsca ale kontekst nagięcia tej reguły jest taki.
Mamy start zawodników mamy dwie grupy:
  1. grupa numer1 na 500 km 15 osób
  2. grupa numer2 na 500 km 1 osoba
Start grup odbywa się co 15 minut (co jakiś ustalony w regulaminie czas, dla przykładu 15 minut, nie pamiętam ile było w rzeczywistości), w moim przykładzie mam dwie grupy jedna 15 osób druga 1 osoba, pytanie czy ta jedna osoba ma czekać dodatkowe 15 minut? Ze względów organizacyjnych można taką osobę puścić w grupie poprzedniej. Grupy jako takie nie mają wpływu na sam przebieg maratonu, czyli są tylko zbiorami na zawodników, służącymi temu żeby zachować porządek i spełnić tę regułę. Grupie też przypisuje się start o tej samej godzinie.
Tak więc nic nie stoi na przeszkodzie żeby tego jednego zawodnika puścić razem z poprzednią grupą. Oficjalnie będzie występował w swojej grupie ale w chwili startu dwie grupy będą puszczone w jednej chwili.
Tak więc można dojść do wniosku, że jednak nie zostanie naruszona ta reguła. Mało tego ta reguła stanie się niezmiennikiem.

Analiza

W dojściu do tego wniosku pomógł mi Claude który stał się moim "sparing partnerem" i zamiast klepacza kodu stał się "gumową kaczką", która mówi.

Wcześniejsze podejście (poprzedni post) sugerował, że można dodać do kodu złamanie reguły, tudzież politykę, która umożliwiałaby taką sytuacje. Byłem świecie przekonany, że zatrudnię Clauda, któremu wrzucę problem i "wyklepie" mi rozwiązanie, ale podczas dodatkowej analizy, zdałem sobie sprawę, że to nie jest naginanie niezmiennika. To jest zupełnie coś innego co może śmiało być po za systemem, po za domeną.

Dlaczego muszę zawsze dążyć do tego żeby stworzyć kod? 
Może lepiej nie tworzyć kodu, a rozwiązanie. 

W tym przykładzie mam tego jednego zawodnika w grupie i drugą pełną grupę to mógłbym ja jako sędzia trasy w porozumieniu z organizatorami wypuścić takiego człowieka razem z grupą poprzednią. 
Czym jest grupa zawodników dla systemu? Jest zbiorem zawodników i spełnieniem reguły, że wystartować możemy maksymalnie grupę składającą się z 15 osób. Mamy na listach, czy w intrenecie wydrukowaną informacje, że start grupy jest o danej godzinie, dla systemu pomiaru czasu nie ma to żadnego znaczenia. Więc sędzia trasy, który wypuszcza zawodników wykorzysta interfejs aplikacji żeby wypuścić w tym samym momencie dwie grupy jedną 15 osobową i jedną 1 osobową.
Czyli pozostanie to w sferze rozwiązania poza systemowego, a to oznacza, że niezmiennik został obroniony. A samo rozwiązanie jest za pomocą "interfejsu białkowego".

Podsumowanie

 Nie wprowadziłem dodatkowego kodu i komplikacji do rozwiązania. Dzięki szerszemu spojrzeniu i zatrudnieniu Clauda w roli jakby drugiego analityka, który może spojrzeć z boku na problem, udało się nie pisać kodu. Wprowadzić rozwiązanie wykorzystując istniejące  możliwości.


środa, 8 października 2025

Domain Model cz.2

Dotarłem do miejsca w którym powstał kod. Oczywiście jak to jest w tych czasach korzystałem z pomocy Copilota. 

Do rzeczy, na warsztat został wzięty agregat Zmiana Grupy Startowej przez Zawodnika.

Widoczny pod tym adresem:

Generowanie grupy startowej

Wyszedł prawie idealnie, powstał prosty agregat jeden command jeden event, kilka reguł. Tu należy się uwaga, ten ES powstał jakieś cztery lata temu, razem z kolegami szkoliliśmy się w event stormingu padło na maraton, akurat ten temat był mi dobrze znany.

Kod jest polsko angielski, cóż tak uznałem, że będzie wygodniej na tę chwilę.

Opis:

Do zmiany grupy dochodzi na pewien czas przed startem maratonu, z reguły było to około miesiąca przed dniem startu. Następuje wtedy wygenerowanie grup startowych, które składają się docelowo z 15 zawodników. Istotnym faktem, który nie jest poruszany w tym kontekście ale należy go nadmienić jest to, że zawodnik który rejestruje się w danej edycji maratonu nie jest nazywany zawodnikiem a uczestnikiem.

Dlaczego to jest istotne? Uczestnik staje się zawodnikiem w momencie opłacenia startu, wtedy też uczestnik otrzymuje numer startowy, który uprawnia go do startu w zawodach oraz dzięki nie mu przynależy do jakieś grupy. Czyli jedną z reguł jest to, że do grupy startowej należą tylko zawodnicy.

Dlaczego to nie jest istotne dla tego agregatu? Dlatego że ten agregat umiejscowiony jest po fakcie wygenerowania grup. Dla niego fakt ten nie jest istotny, a są istotne inne reguły ale sama informacja jest ważna dla całego kontekstu zarządzania grupami startowymi i zrozumienia całego procesu.

Reguły

  1. grupy startowe muszą być wygenerowane - ta reguła w tym kontekście to typ "must be", bez wygenerowanych grup nie może odbyć się zmiana grupy, czyli brak spełnienia tej reguły eliminuje całość, nie da się jej nagiąc,
  2. nie przekroczono terminu zmian grupy - tu sprawa nie jest oczywista, termin jest płynny w zależności od edycji maratonu ale nie zmienia to faktu, że tylko w tym terminie można zmienić grupę *.
  3. Grupa docelowa jest z tego samego dystansu - to jest ważna reguła, której nie da się ominąć, czyli mamy kolejne "must be", dlaczego? grupy na danym dystansie startują o wyznaczonych porach, zapomniałem nadmienić że to maraton 24H, ta reguła została stworzona z przyczyn organizacyjnych, 
  4. Zawodnik nie zmieniał wcześniej grupy - reguła, ograniczenie ilości zmian, czy zawsze musi być spełniona? no to już zależy ale bardziej od reguł dotyczących danej edycji zawodów *.
  5. Grupa docelowa musi mieć miejsce - to dość logiczna reguła, która musi być spełniona, gdyż grupy mają narzuconą ilość zawodników, która ma tam być ale czy nie ma odstępstw? *.


Kod dostępny jest w tym miejscu


 public class ZmianaGrupyApplicationService
 {
     private readonly IGrupaRepository _grupaRepo;
     private readonly IZawodnikRepository _zawodnikRepo;
    
     public ZmianaGrupyApplicationService(
     IGrupaRepository grupaRepo,
     IZawodnikRepository zawodnikRepo)
     {
         _grupaRepo = grupaRepo;
         _zawodnikRepo = zawodnikRepo;
     }

     public void ZmienGrupe(ZmianaGrupyCommand command)
     {
         // 1. Pobierz dane
         var zawodnik = _zawodnikRepo.GetById(command.ZawodnikId);
         var grupaWyjsciowa = _grupaRepo.GetById(command.GrupaWyjsciowaId);
         var grupaDocelowa = _grupaRepo.GetById(command.GrupaDocelowaId);

         // 2. Utwórz agregat domenowy
         var zmiana = new ZmianaGrupyStartowej(
             command.TerminGrupy,
             zawodnik,
             grupaWyjsciowa,
             grupaDocelowa
         );
         // 3. Wykonaj operację domenową
         try
         {
             zmiana.ZmienGrupe(); 
         }
         catch (Exception ex)
         {
             //save to log
             throw;
         }

         // 4. Zapisz zmiany
         _zawodnikRepo.Save(zawodnik);
         _grupaRepo.Save(grupaWyjsciowa);
         _grupaRepo.Save(grupaDocelowa);
     }
 }

Nie będę ukrywał, copilot przyszedł mi z pomocą w tworzeniu tego serwisu, mój prototyp był nie co mniej "ładny". 

Agregat sprawdza reguły

 public void ZmienGrupe()
 {
     ValidateTermin();
     ValidateDystanse();
     ValidateZawodnik();
     ValidateIloscMiejsc();

     // Wykonaj zmianę na encjach
     _zawodnik.ZmienGrupe(_grupaDocelowa);
     _grupaWyjsciowa.UsunZawodnika(_zawodnik);
     _grupaDocelowa.DodajZawodnika(_zawodnik);

 }
Sprawdzenie reguł:
  • Nie przekroczono terminu zmian grupy - mogła zaistnieć sytuacja, pojedyncze przypadki, że tuż przed startem jakaś osoba mogła być przeniesiona z jednej grupy do drugiej, tak więc ta reguła może zostać podważona, ale to zawsze musiała być zgoda sędziego zawodów. Ciekawe jak to przedstawić w kodzie? (mamy rolę sędziego, to może być ciekawe)
  •  Zawodnik nie zmieniał wcześniej grupy - ta reguła mogła by być nagięta w sytuacji gdy zawodnik przed startem zgłasza że zmienia dystans, rezygnuje z jednego i pojedzie na innym dystansie,  dzieje się to po wygenerowaniu i zatwierdzeniu grup, sądzę, że ten proces jest inny i zasługuje na osobny agregat, reguły będą musiały być zbadane w szerszym zakresie, albo przypadek taki że kierownik zawodów ma jakąś znajomą osobę która jednak chciał by zmienić grupę, bo zapisała się już po zmianie grup i co odmówisz?
  • Grupa docelowa musi mieć miejsce - ta reguła może został "złamana" w przypadku gdy na danym dystansie mamy grupę, która osiągnęła limit zawodników i kolejną grupę która ma jednego zawodnika. Takie rzeczy się zdarzały i robiło się wtedy tak, że puszczało się jednego zawodnika z grupą wcześniejszą i czas startu jego grupy pokrywał się z czasem startu grupy poprzedniej,
Podsumowanie

Nagle nam się warunki zaczynają zmieniać, to chyba czas na zmianę agregatu i ponowne jego rozpisanie, a na razie cieszmy się tym co mamy.
Reszta kodu w repo.

Jak wspominałem event storming z maratonu ma już swoje lata, patrząc teraz na ten agregat widzę, że część reguł można nagiąć, a to sugeruje że, trzeba przejrzeć proces i sprawdzić ten kontekst.  Będzie szansa na to, bo jest opcja zrobienia warsztatów w tym temacie na żywo (zdalnie wyszły słabo). 

Na koniec powrót do przeszłości Historia wersji maratonu

sobota, 27 września 2025

Błędna decyzja

Jest błąd.

Czas żeby się z nim zmierzyć, wygląda na prosty

Błąd, posłużyłem się czatem gpt:

System.Data.SqlTypes.SqlTypeException:
  Wartość 0001-01-01 00:00:00 jest poza zakresem dla typu SqlDateTime.
  Zakres prawidłowy to od 1753-01-01 00:00:00 do 9999-12-31 23:59:59.

Pole w bazie, którego błąd dotyczy jest  typu DateTime i jest nullowalne, wykorzystywane przy okazji różnych funkcjonalności z danego kontekstu. Pole nazywa  się data ważności  dajmy na to  DateValid. Czyli mamy jakąś funkcjonalność, która ma określoną datę ważności, (do spożycia). 

To jest kontekst problemu który, powinien nam wystarczyć do jego zrozumienia.

Co robić?

Na pierwszy rzut oka problem jest prosty do rozwiązania, mamy nazwę pola możemy przeszukać w kodzie gdzie jest pole używane i zmienić wpisywaną wartość na tę z zakresu. Czyli z zakresu, który łyknie ms sql.

Opcja numer 2

Możemy też wejść do bazy i zmienić typ tej kolumny z DateTime na DateTime2, przy okazji do nie dawna nie wiedziałem, że jest taki typ jest, myślałem, że to błąd EJ-a, ten typ jest wprowadzony od wersji 2008. Jest to typ zgodny z .Net-otowym typem czyli przyjmuje .netowe wartości minimalne i jest bardziej dokładny. Tak właściwie powinno się go używać zamiast standardowego DateTime (żyłem w kłamstwie).

Wracając do tematu, mamy już dwie opcje w miarę proste do ogarnięcia, tylko czy właściwe?
Mamy pole DateValid jest nullowalne czyli może być puste, ktoś wpisuje tam minimalną wartość daty, te dwie informacje budzą we mnie pewne wątpliwości:
  • dlaczego wpisywana jest wartość minimalna skoro można wpisać null?
  • nazwa pola wskazuje że powinna być tam wartość, a ktoś tam wpisuje datę z poza zakresu data czyli minimalną można przyjąć,
  • być może któraś z funkcjonalności ma źle zwalidowane dane,
  • albo co gorsza jest proces, który dopuszcza wartość pustą w polu którego nazwa sugeruje, że taka nie powinna nastąpić, czyli błąd logiczny
Jak żyć?

Zacząłbym od przeszukania kodu, żeby ustalić która dokładnie funkcjonalność powoduje ten błąd, bo pole wiemy jak się nazywa, wiemy nawet co powoduje te wrzucenie daty minimalnej, powoduje błąd wrzucany do loga.

Dobra cze go nie wiem?

  • Jakie skutki ma ten błąd, proces jest przerwany, albo nieskończony,
  • Dlaczego wpisywana jest wartość minimalna daty, zamiast null-a,
  • Jaki jest sens tego żeby tam była wartość minimalna albo null bo to właściwe w tym przypadku to samo
Kolejny krok trzeba porozmawiać z kimś kto wie, czyli byznes.

No to błąd prosty do rozwiązania czy nie?


Jak widać nie koniecznie bo rozwiązanie proste typu zmiana typu w bazie lub poprawienie na minimalną datę zgodną z ms sql nie rozwiązuje problemu, wręcz przeciwnie tworzy nowe.

Tak więc pozostanie rozmowa z kimś, kto wie jak to ma działać, jak powinno działać. I naprawienie procesu. Czyli za małego błędu robi się góra lodowa, problem kryje się w głębi.
Dzięki temu, że przeprowadziliśmy analizę zagrożenia, a nie zaczęliśmy zmieniać kod może "uratowaliśmy świat".

Linki:


Skill analizy kolejna analiza

Po uruchomieniu wersji drugiej skilla i przejściu przez kontekst rejestracji jestem bardziej zadowolony niż na początku. Ale wynikł problem...