Które podejście do obsługi eventów wolisz?

Które podejście do obsługi eventów wolisz?
Opcja 1 - implemenacja interfejsu
59%
59% [10]
Opcja 2 - konwencja nazewnicza
29%
29% [5]
Opcja 3 - inne (przykład w poście)
12%
12% [2]
Aventus
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:UK
  • Postów:2235
3

Piszę bibliotekę do event sourcingu i mam dylemat jakie podejście zastosować. Sprawa jest więc prosta- proszę abyście zaznaczyli które podejście jako konsument biblioteki preferujecie, i w miarę możliwości uargumentowali jakoś swój wybór.

Chodzi o odbudowywanie stanu z eventów. Umyślnie nie podaję kodu wywołującego te stany (obiekty), ponieważ zależy mi na Waszej opinii z perspektywy konsumenta biblioteki.

Opcja 1
Wymóg aby każdy stan implementował poniższy interfejs. Oczekuje się że metoda Rehydrate zwróci nową instancję stanu, ale oczywiście nie da się tego zagwarantować i implementacja zależy od klienta.

Kopiuj
public interface IAggregateState
{
    IAggregateState Rehydrate(IEvent @event);
}

Oraz sama klasa stanowa:

Kopiuj
public record OrderState : IAggregateState
{
    public string OrderId { get; init; } = string.Empty;
    public OrderStatus Status { get; init; } = OrderStatus.InProgress;

    public IAggregateState Rehydrate(IEvent @event) =>
        @event switch
        {
            OrderPlaced e => this with { OrderId = e.OrderId, Status = OrderStatus.Placed },
            OrderDispatched e => this with { Status = OrderStatus.Dispatched },
            OrderDelivered e => this with { Status = OrderStatus.Delivered },
            _ => this
        };
}

Zauważcie, że używam w przykładzie C# 10, stąd mogę napisać bardziej zwięzły kod. Klient korzystający z biblioteki może nie mieć takiej możliwości, więc wtedy trzeba by to napisać inaczej.

Opcja 2
Wymóg aby dla każdego eventu klasa stanowa posiadała publiczną metodę Apply, ale brak wymogu implementacji interfejsu. Wywołanie Apply odbywa się "magicznie", coś jak w przypadku ConfigureServices czy Configure w ASP Core:

Kopiuj
public record OrderState
{
    public string OrderId { get; private set; } = string.Empty;
    public OrderStatus Status { get; private set; } = OrderStatus.InProgress;

    public void Apply(OrderPlaced @event)
    {
        OrderId = @event.OrderId;
        Status = OrderStatus.Placed;
    }

    public void Apply(OrderDispatched @event)
    {
        Status = OrderStatus.Dispatched;
    }

    public void Apply(OrderDelivered @event)
    {
        Status = OrderStatus.Delivered;
    }
}

Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
SA
  • Rejestracja:około 12 lat
  • Ostatnio:2 minuty
  • Postów:1428
2

Jakis argument za tym, że stringly-typed mialoby byc lepsze od strongly-typed? W sumie to jak ktoś może dać argument za tym w kontekście Startup z ASP .NET Core inny niż możliwość pisania metod per środowisko typu ConfigureProduction?

somekind
Syazysta miał zrobić interfejs, no ale dzień wcześniej były urodziny kolegi, no i mamy, co mamy.
SA
Mogło tak być, https://stackoverflow.com/questions/53254735/why-is-asp-net-cores-startup-class-not-an-interface-or-abstract-class niby są jakieś argumenty, ale wg mnie słabe. Tzn. zgadzam się, że obecny design z tym, że Startup może mieć różne metody z różnymi argumentami nie pozwala na zrobienie eleganckiego interfejsu, ale może taki design nie jest optymalny.
somekind
Szczerze, to już wolę teorię o skacowanym juniorze. Argumenty z SO są z czapy jak dla mnie.
Aventus
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:UK
  • Postów:2235
1

@Saalin: dzięki. Sam nie mam w zasadzie żadnego solidnego argumentu. Osobiście nie mam preferencji do żadnego z tych podejść, dla tego chciałem zaciągnąć opinii programistów.

Jeśli miałbym zagrać adwokata diabła to może podałbym takie powody przemawiające za opcją 2 (choć zdaję sobie sprawę że to wszystko w większości subiektywne):

  • Łatwiejsze rozdzielenie obsługi konkretnych eventów
  • Większa czytelność, szczególnie jeśli każda obsługa eventu to aktualizacja minimum kilku properties (Wiem, bardzo subiektywne. I funkcję w opcji 1 można podzielić na mniejsze funkcje)
  • W przypadku starszych wersji języka- bardziej zwięzły kod, ponieważ opcję 1 raczej trudno napisać zwięźle bez recordów i switch exressions
  • Brak niejasności czy mamy do czynienia z mutowalnym obiektem czy też nie- w opcji 1 konsument nadal może zwracać mutowalny stan, choć nie musi. Opcja 2 nie pozostawia wątpliwości- stan jest mutowalny i tyle

Co do ASP Core i pisania metod "per środowisko" to nie kojarzę żeby taki był powód przemawiający za taką a nie inną implementacją przyjętą przez MS. Możesz napisać coś więcej na ten temat?

Oczywiście kwestia środowisk nie ma znaczenia jeśli chodzi o temat tego wątku.


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
Aventus
@Saalin: już wiem o co chodzi że środowiskami dzięki wcześniejszemu linkowi który podesłałeś. Nawet nie wiedziałem o tej opcji!
SA
@Aventus: to kolejny argument przeciw. Nie wiedziałeś, bo nie wynika to z żadnego interfejsu. To sterowana stringami magia.
SA
U Ciebie dokładnie wiadomo jak ma wyglądać interfejs, jest jedna metoda, dokładnie znane argumenty. W Startup tak nie ma, więc nie ma interfejsu. To z mutowalnym stanem to szkoda, że nie da się tego ograć na poziomie kontraktów/typów, ale taki mamy już język, że niektórych rzeczy nie da się prosto wyrazić. Ja bym takie informacje (tzn. że nie mutujemy stanu tylko zwracamy nowy obiekt) opisał w dokumentacji i jak ktoś się nie stosuje jego problem.
Aventus
Dzięki za opinię!
maszrum
  • Rejestracja:około 5 lat
  • Ostatnio:12 miesięcy
  • Lokalizacja:Kraków
  • Postów:219
1

Dlaczego brak opcji generyczny interfejs IAggregateState<TEvent>? Nie, żebym uważał że tak będzie najlepiej, ale widzę takie rozwiązanie jako potencjalnie możliwe.

Aventus
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:UK
  • Postów:2235
0

@maszrum: dobre pytanie i taką opcję również rozważałem. Pojawia się tylko problem z obsługą tego na poziomie frameworka. Ogólnie to u mnie wygląda (w dużym uproszczeniu) tak że agregaty implementują po generycznym roocie:

Kopiuj
public abstract class AggregateRoot<TState> 
{
  public TState State { get; private set; } = new();
  
  // Dużo kodu pominięte dla czytelności

  public void Rehydrate(IEvent @event)
  {
    // ...
  }

Instancje IEvent to zassane z event store eventy implementujące interfejs IEvent. Dla obydwu przedstawionych opcji obsługa tych eventów i przekazanie do obiektu stanowego wygląda różnie. W przypadku stanu implementującego IAggregateState jest to oczywiście najprostsze bo wiemy na poziomie typów że mamy metodę Rehydrate którą stan implementuje. Ale jak wywołasz implementację IAggregateState<TEvent>, np. IAggregateState<OrderPlaced>, wiedząc jedynie że masz instancję IEvent? Być może ja nie jestem w stanie dojść do alternatywnego rozwiązania, ale nie wydaje mi się że cokolwiek się zyska generycznym IAggregateState<TEvent> w tym przypadku.


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
edytowany 3x, ostatnio: Aventus
obscurity
  • Rejestracja:około 6 lat
  • Ostatnio:około 4 godziny
1

tzn. nie widzę tutaj całości pomysłu ani problemu, ale mi się osobiście oba podejścia nie podobają i wydają over-engineered. Jeśli celem jest "odbudowywanie stanu z eventów" to w ogóle bym tego nie robił na eventach tylko zastosował https://en.wikipedia.org/wiki/Command_pattern
wtedy komendy się zajmują obsługą stanu, klasa nie musi nic implementować i wygląda tak:

Kopiuj
record OrderState(Guid OrderId, OrderStatus Status = OrderStatus.InProgress);

zaś w komendach jest zawarta logika:

Kopiuj
enum OrderStatus
{
    Placed,
    Dispatched,
    Delivered,
    InProgress
};

public interface ICommand<T>
{
    T Execute(T input);
}

class OrderPlaced : ICommand<OrderState>
{
    private readonly Guid orderId;

    public OrderPlaced(Guid orderId)
    {
        this.orderId = orderId;
    }

    public OrderState Execute(OrderState state) => state with { OrderId = orderId, Status = OrderStatus.Placed };
}

class OrderDispatched : ICommand<OrderState>
{
    public OrderState Execute(OrderState state) => state with { Status = OrderStatus.Dispatched };
}

class OrderDelivered : ICommand<OrderState>
{
    public OrderState Execute(OrderState state) => state with { Status = OrderStatus.Delivered };
}

i tyle. Jak chcesz odtworzyć stan obiektu to wystarczy że wywołasz komendy w kolejności

Kopiuj
var commands = new ICommand<OrderState>[]
{
    new OrderPlaced(Guid.NewGuid()),
    new OrderDispatched()
};

var order = new OrderState(Guid.Empty);
order = commands.Aggregate(order, (state, command) => command.Execute(state));

Console.WriteLine(order);

Bardzo łatwo to też rozwinąć o "undo".


"A car won't take your job, another horse driving a car will." - Horse influencer, 1910
Aventus
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:UK
  • Postów:2235
0

@obscurity: chodzi o event sourcing z wykorzystaniem agregatów (zapożyczonych z DDD). Jest również miejsce dla commands ale jako element wejściowy do agregatu. Efektem logiki jest event. W Twoim przykładzie masz record w czasie przeszłym (event), który implementuje ICommand (polecenie), który ma generyczny parametr dla typu stanu. Doceniam chęci ale proponowane podejście wydaje mi się bardzo pokręcone, i właśnie over-engineered.

Idea jest prosta, obsługa całego przepływu danych wygląda tak:

Command (polecenie wykonania czegoś, element wejściowy) -> agregat (logika biznesowa) -> event (informacja o tym co się stało, element wyjściowy)

Za każdym razem kiedy tworzona jest instancja agregatu dla nowego command, zostaje również odtworzony stan agregatu poprzez przypuszczenie przez niego dotychczasowych eventów.

Jeśli celem jest "odbudowywanie stanu z eventów" to w ogóle bym tego nie robił na eventach
Event sourcing z definicji polega na odtwarzaniu stanu z eventów. Ja nie pytam o propozycje alternatyw dla event sourcingu, tylko na preferencje co do odtwarzania stanu właśnie w oparciu o event sourcing. Niemniej dziękuję za chęć pomocy.


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
edytowany 6x, ostatnio: Aventus
obscurity
  • Rejestracja:około 6 lat
  • Ostatnio:około 4 godziny
0
Aventus napisał(a):

W Twoim przykładzie masz record w czasie przeszłym (event), który implementuje ICommand (polecenie), który ma generyczny parametr dla typu stanu.

Nie za bardzo rozumiem, albo źle przeczytałeś kod. Nazwy komend zaczerpnąłem z pierwszego posta żeby pokazać odpowiedniki, oczywiście nazwy komend nie powinny być w tej konwencji. Zauważ że to ten sam kod co w pierwszym Twoim przykładzie tylko logika jest wyizolowana na zewnętrz (do eventów tworząc z nich commandy) i nie łamie zasady podstawienia Liskov. Tak czy inaczej nie podoba mi się żeby obiekt stanowy zajmował się obsługą eventów. Powinien się tym zajmować jakiś event handler. Szczerze nie widzę przeszkód żeby eventem był command, choć jakiś purysta może się przyczepić. Można też mapować eventy na commandy, ale to sztuka dla sztuki albo zrobić zewnętrzny event handler - wtedy wszyscy są zadowoleni. Trzeba też rozdzielić pojęcie "command" z CQRS (polecenie na poziomie logiki biznesowej) od zwykłego command jako opakowaniem akcji z command pattern - trzeba też zauważyć że w większości przypadków w ogóle istnienie "command" nie ma sensu i wystarczy lambda, ale tej nie da się tak łatwo serializować i zapisywać w bazie / przesyłać.

Spójrz zresztą na dowolne schematy po wpisaniu "event sourcing" w googla - https://www.google.com/search?q=event+sourcing&tbm=isch
Na żadnym event handling nie jest częścią stanu aplikacji tylko zewnętrznym elementem. Trzymanie stanu i zarządzanie nim łamie zasadę jednej odpowiedzialności.
Oba podejścia wymagają zmiany kodu jeśli dojdzie nowy event (czyli łamie też zasadę open-closed) i w obu podejściach miałbym wtf jakbym zobaczył w kodzie i musiał rozgryzać o co w tym chodzi.
Tak więc Twoje kody łamią aż 3 z 5 zasad SOLID. Chyba że uważasz że CQRS się z SOLID wyklucza


"A car won't take your job, another horse driving a car will." - Horse influencer, 1910
edytowany 5x, ostatnio: obscurity
Zobacz pozostałe 2 komentarze
SO
Np. https://microservices.io/patterns/data/event-sourcing.html Gdzie w przykładach masz agregat z wieloma metodami apply.
Aventus
Ponieważ jest to jak najbardziej normalne podejście. WTF może być wywołany bo event sourcing to całkowicie inne podejście, ja też miałem wtf ucząc się programowania funkcyjnego. Nie twierdzę jednak że jest niewłaściwe czy wręcz łamie jakieś zasady, bo np. nie operuje na modelach domenowych tylko ma "anemiczne modele". Zresztą nawet w świecie OOP modele anemiczne i "bogate" (ang. rich domain model) to nie powinna być kwestia poprawności lub jej braku, a decyzji projektowej.
Aventus
Co ciekawe, tutaj również biblioteka chyba do event sourcingu, i również oddzielny stan z obsługą eventów (przed chwilą znalezione w google): https://eventuous.dev/docs/domain/aggregate/
obscurity
@some_ONE: hmm, no tutaj to trochę lepiej wygląda bo eventy tworzone są w tym samym miejscu w którym są obsługiwane, więc się to nie powinno rozjechać. Najgorzej jak dodanie nowej funkcji wymaga zmian w wielu miejscach z czego jakieś nie jest oczywiste i nie powoduje błędów kompilacji. @Aventus: w takim razie po co nowa biblioteka do tego? Jaki problem rozwiązujesz?
Aventus
@obscurity: odpowiem kiedy zasypiesz autorów bibliotek IoC pytaniami po co nowa biblioteka, tak jakby jedna nie wystarczyła. Albo bibliotek od unit testów. Albo... chyba łapiesz do czego zmierzam.
Aventus
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:UK
  • Postów:2235
1

Tu nie chodzi o obsługę eventów na zasadzie przetwarzania logiki biznesowej, tylko ich przetworzenie aby odbudować stan, ang. rehydration. Należy odróżnić te dwie kwestie. To co może być dla Ciebie mylące to fakt, że w moim przykładzie stan agregatu jest wydzielony to oddzielnej klasy- to jest po prostu decyzja projektowa. Gdyby usunąć ten stan jako oddzielną klasę, to wyglądało by to mniej więcej tak:

Kopiuj
// Agregat, encja, you name it
class Order
{
    public string OrderId { get; private set; } = string.Empty;
    public OrderStatus Status { get; private set; } = OrderStatus.InProgress;

    // Nadal trzeba zrobić tutaj rehydrate, tylko tym razem w tej klasie
    public Rehydrate(IEvent @event)
    {
        // ...
    }

    // Logika biznesowa
    public void Handle(PlaceOrder order)
    {
      // ...
      // Na końcu
      Apply(new OrderPlaced(...));
    }
}

To co Ty proponujesz, to rozdzielnie wszystkiego na command handlery. CQRS w takim zastosowaniu to całkowicie inne podejście niż to. To nie jest kwestia dobrego i złego rozwiązania, tylko różnych rozwiązań. Notabene, jeśli wyszukał byś moje posty to znajdziesz co najmniej kilka gdzie promuję CQRS w oparciu o wzorzec mediator, a więc de facto wzorzec command-handler. Nie jest to dla mnie to nic nowego, i jest nawet czymś co aktywnie stosuję np. w obecnej pracy. Rzecz w tym że to nie ma nic do rzeczy, bo mowa o dwóch różnych podejściach. Rzucanie tym że łamane są zasady SOLID (z czego SRP jest zasadą wybitnie podatną na interpretację) nic tu nie wnosi.

Co do tego jak odbudować stan to można to zrobić na wiele sposobów. Może Ci się to nie podobać, i szanuję to, natomiast się z tym nie zgadzam. Także nie będę ciągnął więcej tego tematu.

Proponuję bardziej zapoznać się z ideą event sourcingu żeby dobrze zrozumieć o co chodzi. Swoją drogą jesteś pewny że nie pomyliłeś event-sourcingu z event-driven?

Kilka materiałów z którymi warto się zapoznać żeby lepiej zrozumieć skąd moje podejście:

https://www.eventstore.com/event-sourcing (tutaj warto zwrócić uwagę na zastosowanie CQRS ale na całkowicie innym poziomie, mianowicie rozdzielenie writes od reads, a więc aggregates od projections/materialized views).
https://martinfowler.com/bliki/DDD_Aggregate.html
https://docs.microsoft.com/en-us/azure/architecture/patterns/event-sourcing


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
obscurity
  • Rejestracja:około 6 lat
  • Ostatnio:około 4 godziny
0
Aventus napisał(a):

To co Ty proponujesz, to rozdzielnie wszystkiego na command handlery. CQRS w takim zastosowaniu to całkowicie inne podejście niż to. To nie jest kwestia dobrego i złego rozwiązania, tylko różnych rozwiązań.

Tzn. nie uważam że to różne podejścia. Uważam że to podejścia które można ze sobą połączyć tworząc bardziej elastyczny kod. Jeśli operujemy na obiektach mutable i chcemy zachować enkapsulację to obsługa musi być wewnątrz, ale jeśli mamy obiekty immutable to można z tego skorzystać.
W Twoim drugim podejściu niejako implementujesz wzorzec wizytatora https://en.wikipedia.org/wiki/Visitor_pattern , nie widzę przeszkód żeby łączyć ze sobą wzorce architektoniczne z projektowymi. Ja po prostu zaproponowałem użycie innego wzorca operacyjnego, bo moim zdaniem rozwiązuje tu więcej problemów. Event sourcing to moim rozumieniu po prostu rzetelnie prowadzony audyt, na tyle że na podstawie wpisów audytu / logów ("eventów") można odtworzyć stan z dowolnego momentu, nie ma tu większej logiki ani religii.


"A car won't take your job, another horse driving a car will." - Horse influencer, 1910
edytowany 4x, ostatnio: obscurity
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 12 godzin
  • Lokalizacja:Laska, z Polski
  • Postów:10055
2

Nie widzę powodu czemu miałbym użyć tej biblioteki.

Zobacz pozostałe 12 komentarzy
_13th_Dragon
Dobrze wystarczy, wróć do pisania logiki biznesowej w bazie, @some_ONE twoja logika jest metr poniżej mułu, jak ktoś nie widzi sensu w zadanym pytaniu to znaczy że nie ma pojęcia o event sourcingu.
SO
W ogólności nie znaczy, ale w twoim przypadku na 100% to oznacza.
_13th_Dragon
@some_ONE, w ogólności podobne wypowiedzi nie oznaczają braku użycia mózgu na co dzień, ale w niektórych przypadkach na 100% oznaczają.
SO
Obaj wiemy, że mam rację, więc nie ciągnij tego dalej.
_13th_Dragon
Czy ktoś kto nie używa logiki może mieć racje? Więc nie ciągnij tego dalej.
WeiXiao
  • Rejestracja:około 9 lat
  • Ostatnio:około 11 godzin
  • Postów:5108
3

Bardziej podoba mi się podejście nr. 1 ze względu na interfejs i brak magii, aczkolwiek nie podoba mi się brak compile time safety, że gdy dochodzi nowy Event i ktoś zapomni go obsłużyć, to dowiemy się o tym dopiero w runtime. Można byłoby to rozwiązać wyżej wspomnianym odwiedzaczem, ale to średnie rozwiązanie, imo.

Ja osobiście bardziej zastanawiałem się nad tym w kontekście Enumów, aby np. switch statement sprawdzał czy wszystkie enumy są pokryte podczas kompilacji, bo aktualnie tak to nie działa.

Jako trochę rozszerzenie opcji 1 postanowiłem napisać jako POCkę analyzer, który jeżeli wszystko dobrze rozumiem byłby dołączany do biblioteki i działał nie tylko w Visual Studio, a również np. z CLI.

Wygląda to tak:

screenshot-20220723115619.png

screenshot-20220723115646.png

screenshot-20220723115713.png

co sądzicie architekci o angażowaniu analyzerów, a nie tylko poleganiu na języku / OOP w celu zapewnienia compile-time safety?

edytowany 4x, ostatnio: WeiXiao
obscurity
StyleCop i wiele innych linterów już to robi, nie trzeba pisać kolejnego analizatora
WeiXiao
@obscurity: mógłbyś linka podrzucić? bo nie widzę
obscurity
tzn. sprawdzają czy switch obsługuje wszystkie przypadki enuma, nie metoda "Rehydrate"
Aventus
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:UK
  • Postów:2235
0

@1a2b3c4d5e: dzięki. Akurat compile time safety o jakim piszesz nie da się wyegzekwować z bardzo prostego powodu- z czasem, w systemie mogą istnieć eventy które kiedyś były używane, ale później już nie są.

Myślę że kwestia tego, czy event jest obsługiwany kiedy powinien, leży w gestii unit testów.


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
Riddle
Administrator
  • Rejestracja:prawie 15 lat
  • Ostatnio:około 12 godzin
  • Lokalizacja:Laska, z Polski
  • Postów:10055
1

Bibliotek się nie piszę "a napiszę sobie bibliotekę, tylko muszę wymyślić jaką i jak". Tylko wychodzisz od powtarzającego się problemu, który chcesz rozwiązać. Np zaczynasz reużywać tych samych klas pomiędzy projektami, więc chcesz je udostępnić jako całość. Ewentulnie zauważasz że zmierzasz się z podobym problemem w wielu projekatch, i znajdujesz się w sytuacji w której jego rozwiązanie jest powtarzalne - wtedy tez możesz to wydzielić i upublicznić.

Może gdybyś podał przykład użycia tej biblioteki, albo pokazał repo/kod źrodłowy, etc. to możnaby sie jej lepiej przyjrzeć i ją lepiej ocenić.

Edit:

  • Dobrze, to wypowiem się w inny sposób. Zadałeś pytanie:
Aventus napisał(a):

Chodzi o odbudowywanie stanu z eventów. Umyślnie nie podaję kodu wywołującego te stany (obiekty), ponieważ zależy mi na Waszej opinii z perspektywy konsumenta biblioteki.

I odpowiedziałem zgodnie ze swoim sumieniem - nie wiem po co miałbym użyć tej biblioteki, co bym nią zyskał, jaki test miałbym napisać, który wykazałby potrzebę użycia jej. Kiedy rozwiązuję jakiś problem, to próbuję znaleźć rozwiązanie, i po prostu nie widzę gdzie wchodzi Twoja biblioteka, z jakimś rozwiązaniem.

Rozumiem że pytasz o interfejs Twojej biblioteki, ale nie widzę co ten interfejs sobą reprezentuje.

Jak na razie z tego co rozumiem, to to co ta biblioteka robi to serializuje obiekty (które nazywasz eventami), i pytasz o interfejs jaki serializowany obiekt miałby przyjąć. Nie wiem czy dobrze rozumiem?

edytowany 5x, ostatnio: Riddle
WeiXiao
Może gdybyś podał przykład sam tego nigdy nie robisz :P
Aventus
On akurat jest mistrzem w pytaniu o coś na forum, a później rozpętanie gównoburzy wytykaniem każdemu kto się z nim nie zgadza że się nie zna. Co za przypadek... Dziwić się że co jakiś czas przez forum przewija się fala odejść i dyskusje o atmosferę na forum...
Aventus
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:UK
  • Postów:2235
10

@Riddle: jeśli będę chciał to napisze sobie bibliotekę tylko dla tego że mam takie "widzi mi się", i nic nikomu do tego. Mogę sobie ją napisać dla własnego użytku, dla znajomych, dla współpracowników, albo dla wszystkich i wystawić ja publicznie. Mogę ją zacząć pisać i nie skończyć. Mogę ją pisać "dla tego że", a mogę również pisać również "pomimo że". Znów- nic nikomu do tego. Celem tego wątku nie jest pytanie o opinię czy powinienem taka bibliotekę pisać. Jesteś moderatorem a uprawiasz offtop, pomimo tego że zaraportowałem Twój poprzedni post, co oczywiście musiałeś widzieć.

Może gdybyś podał przykład użycia tej biblioteki, albo pokazał repo/kod źrodłowy, etc. to możnaby sie jej lepiej przyjrzeć i ją lepiej ocenić.

Miałem taki zamiar, ale ze względu na przypadki takie jak Ty raczej tego nie zrobię na tym forum. Co najwyżej podeślę link z pytaniem o opinię do kilku użytkowników którzy nie wykazują się pasywnie agresywnym zachowaniem, oraz tych którzy na tym forum wielokrotnie pytali o pomoc w kwestiach związanych z event sourcingiem.

Także proszę, wykaż się rozsądkiem i usuń swoje posty, jak i ten post który właśnie napisałem, żeby usunąć offtop z wątku. Ja i tak będę raportował do skutku.


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
Riddle
Edytowałem post wyżej z wyjaśnieniem.
Kubuś Puchatek
  • Rejestracja:ponad 7 lat
  • Ostatnio:4 miesiące
  • Postów:235
1

My w projekcie używamy coś podobnego do opcji 1 tyle że zamiast interface mamy void, A tam mamy switcha I się sprawdza więc jestem za 1 :)


Lubię miodek :)
Aventus
Czyli nie bawicie się w niemutowalnosc tylko stan po prostu przyjmuje event i aktualizuje swoje właściwości, dobrze rozumiem?
Kubuś Puchatek
Tak, jest metoda load którą przyjmuje kolekcję eventow I aktualizuje propercje w agregacie.
Aventus
Dzięki
SZ
  • Rejestracja:prawie 11 lat
  • Ostatnio:17 minut
  • Postów:1473
0

@Aventus

Co nam daje taki event sourcing?
W jakiego rodzaju aplikacjach się to stosuje?

Ja klepie CRUDy i jakoś nie widzę potrzeby stosowania takich mechanizmów ale może się myle.

Zobacz pozostałe 11 komentarzy
maszrum
@Aventus zaczął lekko tomriddlować :D
Aventus
@Inclouds: ponieważ eventual consistency jest właśnie pochodną projekcji. Ale nawet projekcje same w sobie niekoniecznie muszą oznaczać eventual consistency. ES można zaimplementować na wiele sposobów. Sama idea jest prosta i polega na odbudowywaniu stanu z eventów. Jeśli zaszła by taka potrzeba, to można ten sam stan używać do widoków, lub zapisywać projekcje w tej samej transakcji co eventy, a więc mając pełną spójność. Po prostu zazwyczaj się tego nie robi, i nie poleca się tego, bo powoduje to więcej problemów niż przynosi zysków.
Inclouds
@Aventus: eventual consistency wynika z laga w odświeżeniu widoków, natomiast może wynikać też z innych powodów, np. w przypadku storage'a, z samego sposób zapisu i odczytu danych. Podejrzewam, że gdybyś odświeżał widok w tej samej transakcji co zapisywał eventy, to prawdopodobnie złamałbyś zasadę jednej bazy per jeden mikroserwis. Powiedziałbym, że założenie jest proste - odbudować stan z listy eventow, natomiast konsekwencje tego założenia już proste nie są.
Aventus
Teraz to już wkraczamy w strefę dorabiania teorii do argumentu :p Jeśli storage powoduje eventual consistency to również "problem" może występować tam gdzie nie mamy ES. Weźmy taki Raven DB, tam z samego projektu tej bazy odczyty są eventually consistent, bez znaczenia jaka architekturę stosuje programista. No i na sam koniec- samo eventual consistency właśnie nie jest typowe tylko dla ES. Natomiast wcześniej poruszyłeś ciekawą kwestie, mianowicie skomplikowanie kodu. To jest coś z czym się zgadzam i jednocześnie nie, i znów- to jest coś co występuje w różnych...
Aventus
... architekturach, i zależy od tego jak programiści coś zaimplementują. Niestety z mojego doświadczenia wynika że często ludzie są zadowoleni samym faktem że coś działa, i nie podejmują kolejnego kroku żeby wprowadzić jakąś spójność kodu i ułatwić sobie życie w przyszłości. Ale to oddzielny temat na który można by dyskutować w innym wątku.
SO
  • Rejestracja:ponad 10 lat
  • Ostatnio:12 miesięcy
0

Proszę trzymać się tematu i nie off-topować. Inaczej wyciągniętę zostaną poważne konsekwencje.
Pytanie zadanie przez @Aventus jest proste i nie trzeba się zachowywać jak na elektrodzie. Jak ktoś nie chce się angażować to może też nie off-topować.

pms_enable_synaptics
@some_ONE: Inaczej wyciągniętę zostaną poważne konsekwencje. - https://www.youtube.com/watch?v=-bheKBeG_xs
pms_enable_synaptics
  • Rejestracja:prawie 3 lata
  • Ostatnio:ponad 2 lata
  • Postów:152
1
Aventus napisał(a):

Piszę bibliotekę do event sourcingu i mam dylemat jakie podejście zastosować. Sprawa jest więc prosta- proszę abyście zaznaczyli które podejście jako konsument biblioteki preferujecie, i w miarę możliwości uargumentowali jakoś swój wybór.

Wybieram opcję nr 3 - inne.

A dokładniej zamiast uprawiania NIH, zrobienie researchu istniejących rozwiązań i wybór odpowiedniej biblioteki, których już jest multum.

Zobacz pozostałe 5 komentarzy
pms_enable_synaptics
@iksde: znowu nope, ja nie myślę na jednym poziomie :) Przyjmuje się, że programując dobrze jest myśleć z uwzględnieniem dodatkowo 2-3 warstw poniżej tego, czym się zajmujemy.
Aventus
@pms_enable_synaptics: nope, OP pyta o nasze preferencje Pytałem wyraźnie o wybór między opcją 1 a 2, lub przedstawienie alternatywego sposobu (kodu). Także pytałem o preferencje, ale tylko w tym ograniczonym kontekście. Ty natomiast ewidentnie masz problemy z czytaniem ze zrozumieniem. Gdybym pytał o preferencje na które prawidłowa odpowiedzią jest to co napisałeś, to pytanie brzmiało by: wolicie opcje 1, 2 czy w ogóle powinienem nie pisać takiej biblioteki?
pms_enable_synaptics
@Aventus: kek, nope. Z całym szacunkiem do Ciebie, Ty nie jesteś od oceniania mojej zdolności czytania ze zrozumieniem, ani od ustalania, czy moja odpowiedź jest prawidłowa :) Przypomina mi to trochę sytuację powtarzającą się na studiach, gdy wykładowcy robili testy wielokrotnego wyboru myśląc, że co najmniej jedna odpowiedź jest prawidłowa. A ja udowodniałem, że żadna nie jest i udzielałem swojej xD
Aventus
Aha, ale Ty za to jesteś od tego żeby wiedzieć lepiej ode mnie, o co ja pytałem. Logic :D
pms_enable_synaptics
@Aventus: Nie, ja jestem od tego, żeby mieć własne zdanie.
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)