forked from jet/equinox
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFavorites.fs
More file actions
82 lines (64 loc) · 3.45 KB
/
Favorites.fs
File metadata and controls
82 lines (64 loc) · 3.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
module Domain.Favorites
let [<Literal>] Category = "Favorites"
let streamId = Equinox.StreamId.gen ClientId.toString
// NOTE - these types and the union case names reflect the actual storage formats and hence need to be versioned with care
module Events =
type Favorited = { date: System.DateTimeOffset; skuId: SkuId }
type Unfavorited = { skuId: SkuId }
type Snapshotted = { net: Favorited[] }
type Event =
| Snapshotted of Snapshotted
| Favorited of Favorited
| Unfavorited of Unfavorited
interface TypeShape.UnionContract.IUnionContract
let codec = EventCodec.gen<Event>
let codecJe = EventCodec.genJsonElement<Event>
module Fold =
type State = Events.Favorited []
type private InternalState(input: State) =
let dict = System.Collections.Generic.Dictionary<SkuId, Events.Favorited>()
let favorite (e: Events.Favorited) = dict.[e.skuId] <- e
let favoriteAll (xs: Events.Favorited seq) = for x in xs do favorite x
do favoriteAll input
member _.ReplaceAllWith xs = dict.Clear(); favoriteAll xs
member _.Favorite(e: Events.Favorited) = favorite e
member _.Unfavorite id = dict.Remove id |> ignore
member _.AsState() = Seq.toArray dict.Values
let initial: State = [||]
let private evolve (s: InternalState) = function
| Events.Snapshotted { net = net } -> s.ReplaceAllWith net
| Events.Favorited e -> s.Favorite e
| Events.Unfavorited { skuId = id } -> s.Unfavorite id
let fold (state: State) (events: seq<Events.Event>): State =
let s = InternalState state
for e in events do evolve s e
s.AsState()
let isOrigin = function Events.Snapshotted _ -> true | _ -> false
let snapshot state = Events.Snapshotted { net = state }
let doesntHave skuId (state: Fold.State) = state |> Array.exists (fun x -> x.skuId = skuId) |> not
let decideFavorite date skuIds state =
[ for skuId in Seq.distinct skuIds do
if state |> doesntHave skuId then
yield Events.Favorited { date = date; skuId = skuId } ]
let decideUnfavorite skuId state =
if state |> doesntHave skuId then [] else
[ Events.Unfavorited { skuId = skuId } ]
type Service internal (resolve: ClientId -> Equinox.Decider<Events.Event, Fold.State>) =
member _.Favorite(clientId, skus, ?at) =
let decider = resolve clientId
decider.Transact(decideFavorite (defaultArg at System.DateTimeOffset.Now) skus)
member _.Unfavorite(clientId, sku) =
let decider = resolve clientId
decider.Transact(decideUnfavorite sku)
member _.List clientId: Async<Events.Favorited []> =
let decider = resolve clientId
decider.Query(id)
member _.ListWithVersion clientId: Async<int64 * Events.Favorited []> =
let decider = resolve clientId
decider.QueryEx(fun ctx -> ctx.Version, ctx.State)
// NOTE not a real world example - used for an integration test; TODO get a better example where it's actually relevant
member _.UnfavoriteWithPostVersion(clientId, sku) =
let decider = resolve clientId
decider.TransactEx((fun c -> (), decideUnfavorite sku c.State), fun () c -> c.Version)
let create resolve =
Service(streamId >> resolve Category)