jet / equinox

.NET event sourcing library with CosmosDB, DynamoDB, EventStoreDB, message-db, SqlStreamStore and integration test backends. Focused at stream level; see https://github.com/jet/propulsion for cross-stream projections/subscriptions/reactions
https://github.com/jet/dotnet-templates
Apache License 2.0
472 stars 68 forks source link

Add Transact*Task api's to the F# decider abstraction #375

Closed nordfjord closed 1 year ago

nordfjord commented 1 year ago

We're moving towards using Tasks more and more and this API would be helpful in situations such as calling deciders from propulsion as propulsion handlers are now Task based while deciders are still Async.

bartelink commented 1 year ago

The Task vs Async naming feels a bit wonky on first look, even though it's following the logic of the naming in general. Other questions spring to mind:

I do appreciate the overall idea of removing technical noise from business logic of course, but perhaps having some helpers in a ubiquitous lower level shims lib might address that more generally e.g. https://github.com/fsprojects/FSharp.Control.TaskSeq/issues/142

bartelink commented 1 year ago

With the latest changes, I wonder whether doing a member val Core = inner would not give you pretty much the same experience? or you could call it Task or Internal. Either way, providing that would give an escape hatch for concocting arbitrary variants by calling TransactExAsync on that?

Are you using something like IcedTasks for CancellationToken -> Task<'T>? If not, would skipping passing the CT match the real world usage in your app? Obviously one would not expose that in the real API, but having customised helpers live as type augmentations in a local helper lib might be a route to customising that (and hiding threading of cancellation)

bartelink commented 1 year ago

We're moving towards using Tasks more and more and this API would be helpful in situations such as calling deciders from propulsion as propulsion handlers are now Task based while deciders are still Async.

The next RC of Propulsion will provide a lower level API based on Tasks, Funcs and struct tuples However the default API (without Async suffices in Factory) will continue to use Async, obj tuples, and FSharpFuncs Having _ct and/or Async.AwaitTask in prod code can wait until a profiler tells you which ones need it

For this release, I'm going to expose a val Core : DeciderCore so you can further prototype to cater for such needs by having type augmentations on Decider in a global namespace within an app.

As discussed out of band, the key issue with the APIs in DeciderCore from a F# perspective is that they operate on Event seq rather than Event list (that and the struct tuples)

I'm hopeful that taking a bit longer before baking something noisy in will ultimately yield a better overall result, even if it's not entirely clear to either of us what that might actually become.