System składkowy

Myślę, że jesteśmy w stanie ograniczyć się do imienia i nazwiska. @amadeusz sugerował też, że można hash’ować te dane. Ale wracamy tym samym do problemu poprawnego przetwarzania danych.

Te dane mogą się zmieniać w czasie. Nowe nazwisko, nowy bank, itp. itd.

Zgadzam się. Dlatego pokładam jakąś nadzieję w DDD, że pozwoli opanować tego typu zachowania. Bo oprócz standardowej składki, mamy ulgę studencką, albo ktoś płaci z góry na pół roku. A niektórzy dorzucają się więcej co miesiąc. No i teraz mamy okropną inflację, co jak się zmieni globalna wysokość składki?

Jakie wymagania funkcjonalne (bądź poza funkcjonalne jak audytowalność) będzie prościej osiągnąć z użyciem event sourcingu niż w mojej propozycji z Django?

Uważam, że nie ma znaczenia czy zostanie wykorzystane django czy cokolwiek innego. To jedynie webframework. Będzie jedynie sie gryźć się z modelami stworzonymi nie po jego myśli, jeśli zostanie zastosowany event sourcing.

Nawet rozważam, żeby napisać prototyp w django i zobaczyć jak się zachowuje. Bo moim celem nie jest implementowanie każdego widoku ręcznie.

Zależy mi jedynie na uchwycenie jak najlepiej encji, modeli, itd. tego systemu.

Chyba, że przez django chcesz powiedzieć “użyj CRUDa i zapomnij o reszcie”?

W zasadzie tak, proponuję zastosowanie najprostszego możliwego podejścia. Weź sprawdzony framework, zaimplementuj w nim wszystko to co da się w nim zaimplementować, bez jakiegoś specjalnego planowania, a na końcu zrób jakiś drobny refactoring jeżeli gdzieś pojawiły się jakieś hacki. Dla małych aplikacji monolity to najlepsze rozwiązanie bo są proste. Pytanie czy jest cokolwiek konkretnego z czym w takim podejściu będzie problem a z tym całym event sourcingiem nie?

To jest coś do czego mam lekkie obawy. Okay, widoki, parsowanie, notyfikacje, wprowadzanie danych, to jest proste do tworzenia i refactoringu.

Uważam jednak, że zła implementacja logu transakcji może utrudnić refaktoryzacje. Chociaż pewnie są techniki jak to robić sensownie. Z drugiej strony właśnie stosowanie event sourcing powinno umożliwiać wprowadzanie właśnie tych zmian. Mając cała historię akcji, można ją odtworzyć stosując nową logikę biznesową.

Poza tym nie wiemy co przyniesie przyszłość.

Jaki JSON? Dane produkowane przez system żyją dłużej niż sam system. Zamiast wymyślać coś nowego, można zastosować jakiś standard. Na przykład OFX ma już sporo napisanych adapterów z różnych formatów bankowych. Dodatkowo, sam system może zostać kiedyś zaorany i zastąpiony czymś innym. Z dużą szansą, że poprzednie interfejsy będą musiały być zachowane.

Pora zakasać rękawy. Na początek warto zbudować jakąś podstawową strukturę. Logowanie przez SSO, widoki użytkowników. Obsługa składek będzie najpewniej osobnym modułem w którym spróbuję zamknąć cały event sourcing. Ale najpierw muszę zrobić jakiś prototyp obok.

Ale w międzyczasie można coś specyfikować:

@yakub ogarniasz django? Jakie znaczenie ma wybór między 4, a 3.2?

1 polubienie

Mogę coś tam w Django ruszyć, robiłem w tym pracę inżynierską i mam z tym czasem styczność w pracy.
4 to jest dopiero release candidate i na razie bym się na to nie rzucał, nie wiem jakie są zmiany, ale wolę się nie zaskoczyć tym, że jakaś biblioteka np. do OAuth nie działa jeszcze z wersją 4.0.

Ogólnie można myśleć o hspsh/meta jako kodzie smugowym

Tutaj z znajdują się moje eksperymenty z event sourcingiem. Udało mi się zaimplementować składki.

Agregat Fee wylicza wysokość składki przez podanie do niego przedziału czasu. Pozwala też na zmianę składki i jej zmiana działa od ustalonego momentu.

@psuwala, możesz przejrzeć i ocenić jak się podoba

Nazywam się jak się nazywam, ale tez chcę dorzucić swoje 3 grosze:

  • nie używaj typów zmiennoprzecinkowych do reprezentowania pieniędzy. Ustał, ze każdą wartość mnożysz przez stałą i trzymaj w bazie liczby całkowite. Bez tego ryzykujesz gubienie groszy przez błędy zaokrągleń.
  • jeśli zdefiniowałeś zdarzenie credit, to sensowne byłoby tez zdarzenie debit wyzwalane 1 każdego miesiąca o 00:00. W ten sposób łatwo będzie ustalić, kto zalega ze składkami.
  • nie rozumiem zdarzenia Opened da Fee. Rozumiem dla Account, ale nie dla Fee. To byłoby coś na kształt obciążenia składką 1 każdego miesiąca? Ale to powinno być z kolei zdarzenie na koncie, nie na Fee.
  • get_dues na Fee powinno raczej być wołanie na Account. Do tego zdarzenie debit i wio! :wink:

Sorry, ze nie na GH, ale z aplikacji nie da się, chyba, komentować commitów.

W kwestii

Czy pythonowy typ Decimal nie daje już takich gwarantów?

Zastanawiam się czy mogę uniknąć wywoływania polecenia debit każdego miesiąca. Taki był mój pomysł na ten agregat Fee, że on wylicza należną składkę od np. otwarcia konta do teraz. Chciałbym uniknąć sytuacji, gdzie system w sposób automatyczny zmienia stan kont. Jedynym wyjątkiem powinno być wprowadzanie danych z przelewów.

To pierwsze zdarzenie które po prostu definiuje dany typ składki, oraz jej początkową wysokość.

W każdym razie dzięki za komentarze. Niby mając już te wszystkie zdarzenia nie powinienem się bać obciążać kont, bo zawsze mogę przywrócić stan poprzedni. Ale coś w automatycznym naliczaniu należności z poziomu Fee wydaje się bardziej eleganckie.

Skąd będziesz wiedział kto ile zalega?

Faktycznie, powinien.

Zdradź jeszcze jak to będzie w bazie zapisane? Bo z tego co pamietam, to przy event sourcingu źródłem prawdy powinny być zdarzenia. Zawołanie Account.credit() spowoduje zapisanie zdarzenia do bazy?

Przepraszam za pińcet komentarzy; piszę z telefonu.

Myślałem, że zrealizuje to w ten sposób:

ile_kto_zalega(czas_od_otwarcia_konta) == fee.get_dues(czas_od_otwarcia_konta) + acc.balance

Chyba będą tam jeszcze inne szególne przypadki. Ale właśnie próbuję je w ten sposób odkryć.

Biblioteka evensourcing ma event_store w którym zapisuje wszsystkie wydarzenia. I agregaty są budowane z tych wydarzeń przy zaciąganiu z bazy. Tam są jakieś nawet sensowne adaptery do baz danych w tej libce.

Zważ, że mając zdarzenie Debited wystarczy Ci znajomość stanu konta. IMHO to duże uproszczenie.

EDIT: pytanie pułapka: członek obniżył wpłacaną składkę z 200 do 100 złotych. Co się stanie z przedstawionym przez Ciebie wzorem?

Możliwe, że sensowniejszym byłby jakiś mechanizm wyliczający składkę w danym momencie, i jej aplikowanie przez wydarzenie Debited.

Jeśli zmniejszył wpłacaną, to zostanie naliczony dług. Jeśli ustali inną wartość składki, to będzie to uznane:

fee_id = app.create_fee(name="account_specific", amount=Decimal(100))

# collect fees for one year
assert app.get_dues(fee_id, datetime(2020,1,1), datetime(2020,12,30)) == Decimal(1200)

# collect fees for one year but double amount mid-year
app.set_amount(fee_id, Decimal(200), datetime(2020,7,1))
assert app.get_dues(fee_id, datetime(2020,1,1), datetime(2020,12,30)) == Decimal(1800)

Skąd będziesz wiedział kiedy się kwota zmieniła? Dodasz pola from i to Fee? Czy może jakieś inne jeszcze zdarzenie? IMHO należałoby całość zoptymalizować pod główną funkcjonalność, czyli ustalanie ile kto wpłacił i ile kto wisi.

Może faktycznie pozbędę się na ten moment tego Fee i wstawię coś na sztywno. Chociaż ten agregat działa całkiem sprawnie i bez problemu śledzi zmiany wysokości składki.

To co mnie ciekawi, to jak naliczać obciążenia bez crona. Aby było tylko jedno obciążenie na miesiąc. Możemy sobie wyobrazić jakiegoś crona który odpali się dwa razy w związku ze zmianą czasu na letni. Albo kilka triggerów z różnych źródeł nieświadomych, że nakazują naliczyć coś dwa razy. To znaczy, można mieć jakieś wyjątki które by do tego nie dopuściły, ale może jest sposób aby agregat sam o to dbał?

Skąd w takim razie wiesz, kiedy zmieniła się wysokość składki? Widzę, ze czegoś nie łapię mocno :stuck_out_tongue: