TheAngryByrd / Marten.FSharp

A set of FSharp wrappers around Marten
MIT License
74 stars 10 forks source link

Marten Discriminated Union as Event Type or Each Discriminated Union Case as Event Type? #29

Open natalie-o-perret opened 5 years ago

natalie-o-perret commented 5 years ago

I recently found out that discrimated unions does not work well with aggregation in Marten: https://stackoverflow.com/questions/56479902/f-marten-does-not-load-the-aggregate

For a given discriminated union:

type AccountCreation = {
    Owner: string
    AccountId: Guid
    CreatedAt: DateTimeOffset
    StartingBalance: decimal
}

type Transaction = {
    To: Guid
    From: Guid
    Description: string
    Time: DateTimeOffset
    Amount: decimal
}

type AccountEvent =
    | AccountCreated of AccountCreation
    | AccountCredited of Transaction
    | AccountDebited of Transaction

and the aggregate:

type Account() =
    member val Id = Unchecked.defaultof<Guid> with get,set
    member val Owner = Unchecked.defaultof<string> with get,set
    member val Balance = Unchecked.defaultof<decimal> with get,set
    member val CreatedAt = Unchecked.defaultof<DateTimeOffset> with get,set
    member val UpdatedAt = Unchecked.defaultof<DateTimeOffset> with get,set

    member this.Apply(accountCreation: AccountCreation) =
        this.Id <- accountCreation.AccountId
        this.Owner <- accountCreation.Owner
        this.Balance <- accountCreation.StartingBalance
        this.CreatedAt <- accountCreation.CreatedAt
        this.UpdatedAt <- accountCreation.CreatedAt     

I am not sure what experienced F# folks recommend:

options.Events.AddEventType(typeof<AccountEvent>)

or

options.Events.AddEventType(typeof<AccountCreation>)
options.Events.AddEventType(typeof<Transaction>)
TheAngryByrd commented 5 years ago

I've never worked with Marten's event sourcing so most of my input will be uninformed. I'll definitely be seeking your help to dig at the actions below.

options.Events.AddEventType(typeof) pros: can use pattern matching for the kind of event I have (when fetching the stream of events), hence AccountCredited and AccountDebited can be discriminated against each other even if they have the same type. cons: loosing the possibility to have the projections saved in DB (performance if the projection is going through a lot of data), session.LoadAsync(id) will return null .

So i does not correctly call Apply to your given Account type? If possible, digging into Marten proper to figure out why would be a good start because maybe we can just fix it there.

options.Events.AddEventType(typeof) options.Events.AddEventType(typeof) pros: the possibility to have the projections saved in DB: session.LoadAsync(id) will return the right value. cons: loosing the possibility to discriminate AccountCredited and AccountDebited against each other when fetching the stream if they have the same type, it forces to have a different record type definition for each.

I dislike this option because of the cons you have outlined. If digging into Marten to figure out whats going on doesnt pan out, maybe looking into Akka.net fsharp stuff to see how they handle this similar type of thing works and maybe we can mimic it here.

natalie-o-perret commented 5 years ago

I've never worked with Marten's event sourcing so most of my input will be uninformed. I'll definitely be seeking your help to dig at the actions below.

options.Events.AddEventType(typeof) pros: can use pattern matching for the kind of event I have (when fetching the stream of events), hence AccountCredited and AccountDebited can be discriminated against each other even if they have the same type. cons: loosing the possibility to have the projections saved in DB (performance if the projection is going through a lot of data), session.LoadAsync(id) will return null .

So i does not correctly call Apply to your given Account type? If possible, digging into Marten proper to figure out why would be a good start because maybe we can just fix it there.

Yea the Apply method is not called Exactly

options.Events.AddEventType(typeof) options.Events.AddEventType(typeof) pros: the possibility to have the projections saved in DB: session.LoadAsync(id) will return the right value. cons: loosing the possibility to discriminate AccountCredited and AccountDebited against each other when fetching the stream if they have the same type, it forces to have a different record type definition for each.

I dislike this option because of the cons you have outlined. If digging into Marten to figure out whats going on doesnt pan out, maybe looking into Akka.net fsharp stuff to see how they handle this similar type of thing works and maybe we can mimic it here.

Good idea, will check this out

natalie-o-perret commented 5 years ago

Update: https://github.com/JasperFx/marten/issues/1283 I opened an issue on the official repository and added my investigations

nkosi23 commented 11 months ago

Quick question: how do you query / filter based on the value of a discriminated union property using Marten.FSharp?