Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose pre-state Version via TransactEx #263

Merged
merged 3 commits into from
Feb 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ The `Unreleased` section name is replaced by the expected version of next releas
- Remove separated `Backend` project from examples (support for architecturally separating all domain logic from Equinox and Domain Service logic from Concrete Stores remains)
- Revise semantics of Cart Sample Command handling
- Simplify `AsyncCacheCell` [#229](https://github.com/jet/equinox/pull/229)
- Replace `Decider.TransactAsyncEx` with `TransactEx`, providing an extended signature to expose the `ISyncContext` to `decide` [#263](https://github.com/jet/equinox/pull/263)

### Removed
### Fixed
Expand Down
14 changes: 7 additions & 7 deletions src/Equinox/Decider.fs
Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,41 @@ type Decider<'event, 'state>

let transact decide mapResult =
let resyncPolicy = defaultArg resyncPolicy (fun _log _attemptNumber resyncF -> async { return! resyncF })
let inline createDefaultAttemptsExhaustedException attempts : exn = MaxResyncsExhaustedException attempts :> exn
let createDefaultAttemptsExhaustedException attempts : exn = MaxResyncsExhaustedException attempts :> exn
let createAttemptsExhaustedException = defaultArg createAttemptsExhaustedException createDefaultAttemptsExhaustedException
Flow.transact (maxAttempts, resyncPolicy, createAttemptsExhaustedException) (stream, log) decide mapResult

/// 0. Invoke the supplied <c>interpret</c> function with the present state
/// 1a. (if events yielded) Attempt to sync the yielded events events to the stream
/// 1b. Tries up to <c>maxAttempts</c> times in the case of a conflict, throwing <c>MaxResyncsExhaustedException</c> to signal failure.
member __.Transact(interpret : 'state -> 'event list) : Async<unit> =
transact (fun state -> async { return (), interpret state }) (fun () _context -> ())
transact (fun context -> async { return (), interpret context.State }) (fun () _context -> ())

/// 0. Invoke the supplied <c>decide</c> function with the present state, holding the <c>'result</c>
/// 1a. (if events yielded) Attempt to sync the yielded events events to the stream
/// 1b. Tries up to <c>maxAttempts</c> times in the case of a conflict, throwing <c>MaxResyncsExhaustedException</c> to signal failure.
/// 2. Yield result
member __.Transact(decide : 'state -> 'result * 'event list) : Async<'result> =
transact (fun state -> async { return decide state }) (fun result _context -> result)
transact (fun context -> async { return decide context.State }) (fun result _context -> result)

/// 0. Invoke the supplied <c>_Async_</c> <c>decide</c> function with the present state, holding the <c>'result</c>
/// 1a. (if events yielded) Attempt to sync the yielded events events to the stream
/// 1b. Tries up to <c>maxAttempts</c> times in the case of a conflict, throwing <c>MaxResyncsExhaustedException</c> to signal failure.
/// 2. Yield result
member __.TransactAsync(decide : 'state -> Async<'result * 'event list>) : Async<'result> =
transact decide (fun result _context -> result)
transact (fun context -> decide context.State) (fun result _context -> result)

/// 0. Invoke the supplied <c>_Async_</c> <c>decide</c> function with the present state, holding the <c>'result</c>
/// 0. Invoke the supplied <c>_Async_</c> <c>decide</c> function with the present state (including extended context), holding the <c>'result</c>
/// 1a. (if events yielded) Attempt to sync the yielded events events to the stream
/// 1b. Tries up to <c>maxAttempts</c> times in the case of a conflict, throwing <c>MaxResyncsExhaustedException</c> to signal failure.
/// 2. Uses <c>mapResult</c> to render the final outcome from the <c>'result</c> and/or the final <c>ISyncContext</c>
/// 3. Yields the outcome
member __.TransactAsyncEx(decide : 'state -> Async<'result * 'event list>, mapResult : 'result -> ISyncContext<'state> -> 'result) : Async<'result> =
member __.TransactEx(decide : ISyncContext<'state> -> Async<'result * 'event list>, mapResult : 'result -> ISyncContext<'state> -> 'view) : Async<'view> =
transact decide mapResult

/// Project from the folded <c>'state</c>, without executing a decision flow as <c>Transact</c> does
member __.Query(projection : 'state -> 'view) : Async<'view> =
Flow.query (stream, log, fun syncState -> projection (syncState :> ISyncContext<'state>).State)
Flow.query (stream, log, fun context -> projection (context :> ISyncContext<'state>).State)

/// Project from the stream's <c>'state<c> (including extended context), without executing a decision flow as <c>Transact<c> does
member __.QueryEx(projection : ISyncContext<'state> -> 'view) : Async<'view> =
Expand Down
4 changes: 2 additions & 2 deletions src/Equinox/Flow.fs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ module internal Flow =
/// 2b. if conflicting changes, retry by recommencing at step 1 with the updated state
let run (log : ILogger) (maxSyncAttempts : int, resyncRetryPolicy, createMaxAttemptsExhaustedException)
(context : SyncContext<'event, 'state>)
(decide : 'state -> Async<'result * 'event list>)
(decide : ISyncContext<'state> -> Async<'result * 'event list>)
(mapResult : 'result -> SyncContext<'event, 'state> -> 'view)
: Async<'view> =

Expand All @@ -88,7 +88,7 @@ module internal Flow =
/// Run a decision cycle - decide what events should be appended given the presented state
let rec loop attempt : Async<'view> = async {
let log = if attempt = 1 then log else log.ForContext("syncAttempt", attempt)
let! result, events = decide ((context :> ISyncContext<'state>).State)
let! result, events = decide (context :> ISyncContext<'state>)
if List.isEmpty events then
log.Debug "No events generated"
return mapResult result context
Expand Down