Skip to content

Commit

Permalink
Replace Target with FsCodec.StreamName (#189)
Browse files Browse the repository at this point in the history
  • Loading branch information
bartelink authored Jan 30, 2020
1 parent 5effb29 commit b46c9f5
Show file tree
Hide file tree
Showing 38 changed files with 100 additions and 101 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The `Unreleased` section name is replaced by the expected version of next releas
### Changed

- `SqlStreamStore`.*: Target `SqlStreamStore` v `1.2.0-beta.8`
- Target `FsCodec` v `2.0.0-rc1`
- Target `FsCodec` v `2.0.0-rc2`
- Target `Microsoft.SourceLink.GitHub`, `Microsoft.NETFramework.ReferenceAssemblies` v `1.0.0`
- Samples etc target `Argu` v `6.0.0`
- `eqx dump`'s `-J` switch now turns off JSON rendering
Expand All @@ -29,6 +29,7 @@ The `Unreleased` section name is replaced by the expected version of next releas
### Removed

- `Accumulator` [#184](https://github.com/jet/equinox/pull/184)
- `Target` (now uses `FsCodec.StreamName`) [#189](https://github.com/jet/equinox/pull/189)

### Fixed

Expand Down
4 changes: 2 additions & 2 deletions DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ The following example is a minimal version of [the Favorites model](samples/Stor
```fsharp
(* Event stream naming + schemas *)
let (|ForClientId|) (id: ClientId) = Equinox.AggregateId("Favorites", ClientId.toStringN id)
let (|ForClientId|) (id: ClientId) = FsCodec.StreamName.create "Favorites" (ClientId.toString id)
type Item = { id: int; name: string; added: DateTimeOffset }
type Event =
Expand Down Expand Up @@ -372,7 +372,7 @@ See [the TodoBackend.com sample](README.md#TodoBackend) for reference info regar
#### `Event`s

```fsharp
let (|ForClientId|) (id : string) = Equinox.AggregateId("Todos", id)
let (|ForClientId|) (id : string) = FsCodec.StreamName.create "Todos" id
type Todo = { id: int; order: int; title: string; completed: bool }
type Event =
Expand Down
1 change: 0 additions & 1 deletion Equinox.sln
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".project", ".project", "{7E
Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets
DOCUMENTATION.md = DOCUMENTATION.md
global.json = global.json
LICENSE = LICENSE
README.md = README.md
SECURITY.md = SECURITY.md
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ The components within this repository are delivered as multi-targeted Nuget pack

### Core library

- `Equinox` [![NuGet](https://img.shields.io/nuget/v/Equinox.svg)](https://www.nuget.org/packages/Equinox/): Store-agnostic decision flow runner that manages the optimistic concurrency protocol. ([depends](https://www.fuget.org/packages/Equinox) on `Serilog` (but no specific Serilog sinks, i.e. you configure to emit to `NLog` etc))
- `Equinox` [![NuGet](https://img.shields.io/nuget/v/Equinox.svg)](https://www.nuget.org/packages/Equinox/): Store-agnostic decision flow runner that manages the optimistic concurrency protocol. ([depends](https://www.fuget.org/packages/Equinox) on `FsCodec` (for the `StreamName` type-contract), `Serilog` (but no specific Serilog sinks, i.e. you configure to emit to `NLog` etc))

### Serialization support

Expand Down
4 changes: 2 additions & 2 deletions samples/Store/Domain.Tests/Infrastructure.fs
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ module IdTypes =
let x = Guid.NewGuid() in let xs, xn = x.ToString(), x.ToString "N"
let (x1 : CartId, x2 : CartId) = %x, %x
test <@ x1 = x2
&& xn = CartId.toStringN x2
&& xn = CartId.toString x2
&& string x1 = xs @>

[<Fact>]
let ``ClientId has structural equality and Guid rendering semantics`` () =
let x = Guid.NewGuid() in let xs, xn = x.ToString(), x.ToString "N"
let (x1 : ClientId, x2 : ClientId) = %x, %x
test <@ x1 = x2
&& xn = ClientId.toStringN x2
&& xn = ClientId.toString x2
&& string x1 = xs @>

[<Fact>]
Expand Down
4 changes: 3 additions & 1 deletion samples/Store/Domain/Cart.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

// NOTE - these types and the union case names reflect the actual storage formats and hence need to be versioned with care
module Events =

let (|ForCartId|) (id: CartId) = FsCodec.StreamName.create "Cart" (CartId.toString id)

type ContextInfo = { time: System.DateTime; requestId: RequestId }

type ItemInfo = { context: ContextInfo; item: ItemInfo }
Expand All @@ -22,7 +25,6 @@ module Events =
| ItemWaiveReturnsChanged of ItemWaiveReturnsInfo
interface TypeShape.UnionContract.IUnionContract
let codec = FsCodec.NewtonsoftJson.Codec.Create<Event>()
let (|ForCartId|) (id: CartId) = Equinox.AggregateId ("Cart", CartId.toStringN id)

module Fold =
type ItemInfo = { skuId: SkuId; quantity: int; returnsWaived: bool }
Expand Down
5 changes: 4 additions & 1 deletion samples/Store/Domain/ContactPreferences.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ type Id = Id of email: string

// NOTE - these types and the union case names reflect the actual storage formats and hence need to be versioned with care
module Events =

let (|ForClientId|) (email: string) = FsCodec.StreamName.create "ContactPreferences" email // TODO hash >> base64

type Preferences = { manyPromotions : bool; littlePromotions : bool; productReview : bool; quickSurveys : bool }
type Value = { email : string; preferences : Preferences }

type Event =
| [<System.Runtime.Serialization.DataMember(Name = "contactPreferencesChanged")>]Updated of Value
interface TypeShape.UnionContract.IUnionContract
let codec = FsCodec.NewtonsoftJson.Codec.Create<Event>()
let (|ForClientId|) (email: string) = Equinox.AggregateId ("ContactPreferences", email) // TODO hash >> base64

module Fold =

type State = Events.Preferences

let initial : State = { manyPromotions = false; littlePromotions = false; productReview = false; quickSurveys = false }
Expand Down
2 changes: 1 addition & 1 deletion samples/Store/Domain/Domain.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<PackageReference Include="FSharp.Core" Version="3.1.2.5" Condition=" '$(TargetFramework)' == 'net461' " />
<PackageReference Include="FSharp.Core" Version="4.3.4" Condition=" '$(TargetFramework)' == 'netstandard2.0' " />

<PackageReference Include="FsCodec.NewtonsoftJson" Version="2.0.0-rc1" />
<PackageReference Include="FsCodec.NewtonsoftJson" Version="2.0.0-rc2" />
<PackageReference Include="FSharp.UMX" Version="1.0.0" />
</ItemGroup>

Expand Down
5 changes: 4 additions & 1 deletion samples/Store/Domain/Favorites.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

// NOTE - these types and the union case names reflect the actual storage formats and hence need to be versioned with care
module Events =

let (|ForClientId|) (id: ClientId) = FsCodec.StreamName.create "Favorites" (ClientId.toString id)

type Favorited = { date: System.DateTimeOffset; skuId: SkuId }
type Unfavorited = { skuId: SkuId }
type Snapshotted = { net: Favorited[] }
Expand All @@ -12,9 +15,9 @@ module Events =
| Unfavorited of Unfavorited
interface TypeShape.UnionContract.IUnionContract
let codec = FsCodec.NewtonsoftJson.Codec.Create<Event>()
let (|ForClientId|) (id: ClientId) = Equinox.AggregateId("Favorites", ClientId.toStringN id)

module Fold =

type State = Events.Favorited []

type private InternalState(input: State) =
Expand Down
6 changes: 3 additions & 3 deletions samples/Store/Domain/Infrastructure.fs
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@ module RequestId =
/// CartId strongly typed id; represented internally as a Guid; not used for storage so rendering is not significant
type CartId = Guid<cartId>
and [<Measure>] cartId
module CartId = let toStringN (value : CartId) : string = Guid.toStringN %value
module CartId = let toString (value : CartId) : string = Guid.toStringN %value

/// ClientId strongly typed id; represented internally as a Guid; not used for storage so rendering is not significant
type ClientId = Guid<clientId>
and [<Measure>] clientId
module ClientId = let toStringN (value : ClientId) : string = Guid.toStringN %value
module ClientId = let toString (value : ClientId) : string = Guid.toStringN %value

/// InventoryItemId strongly typed id
type InventoryItemId = Guid<inventoryItemId>
and [<Measure>] inventoryItemId
module InventoryItemId = let toStringN (value : InventoryItemId) : string = Guid.toStringN %value
module InventoryItemId = let toString (value : InventoryItemId) : string = Guid.toStringN %value
4 changes: 3 additions & 1 deletion samples/Store/Domain/InventoryItem.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ open System

// NOTE - these types and the union case names reflect the actual storage formats and hence need to be versioned with care
module Events =

let (|ForInventoryItemId|) (id : InventoryItemId) = FsCodec.StreamName.create "InventoryItem" (InventoryItemId.toString id)

type Event =
| Created of name: string
| Deactivated
| Renamed of newName: string
| Removed of count: int
| CheckedIn of count: int
interface TypeShape.UnionContract.IUnionContract
let (|ForInventoryItemId|) (id : InventoryItemId) = Equinox.AggregateId ("InventoryItem", InventoryItemId.toStringN id)

module Fold =
type State = { active : bool; name: string; quantity: int }
Expand Down
4 changes: 3 additions & 1 deletion samples/Store/Domain/SavedForLater.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ open System.Collections.Generic

// NOTE - these types and the union case names reflect the actual storage formats and hence need to be versioned with care
module Events =

let (|ForClientId|) (id: ClientId) = FsCodec.StreamName.create "SavedForLater" (ClientId.toString id)

type Item = { skuId : SkuId; dateSaved : DateTimeOffset }

type Added = { skus : SkuId []; dateSaved : DateTimeOffset }
Expand All @@ -27,7 +30,6 @@ module Events =
| Added of Added
interface TypeShape.UnionContract.IUnionContract
let codec = FsCodec.NewtonsoftJson.Codec.Create<Event>()
let (|ForClientId|) (id: ClientId) = Equinox.AggregateId("SavedForLater", ClientId.toStringN id)

module Fold =
open Events
Expand Down
39 changes: 20 additions & 19 deletions samples/TodoBackend/Todo.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
open Domain

// NOTE - these types and the union case names reflect the actual storage formats and hence need to be versioned with care
[<AutoOpen>]
module Events =

// The TodoBackend spec does not dictate having multiple lists, tenants or clients
// Here, we implement such a discriminator in order to allow each virtual client to maintain independent state
let (|ForClientId|) (id : ClientId) = FsCodec.StreamName.create "Todos" (ClientId.toString id)

type Todo = { id: int; order: int; title: string; completed: bool }
type Deleted = { id: int }
type Snapshotted = { items: Todo[] }
Expand All @@ -16,37 +20,34 @@ module Events =
| Snapshotted of Snapshotted
interface TypeShape.UnionContract.IUnionContract
let codec = FsCodec.NewtonsoftJson.Codec.Create<Event>()
// The TodoBackend spec does not dictate having multiple lists, tenants or clients
// Here, we implement such a discriminator in order to allow each virtual client to maintain independent state
let (|ForClientId|) (id : ClientId) = Equinox.AggregateId("Todos", ClientId.toStringN id)

module Fold =
type State = { items : Todo list; nextId : int }
type State = { items : Events.Todo list; nextId : int }
let initial = { items = []; nextId = 0 }
let evolve s e =
match e with
| Added item -> { s with items = item :: s.items; nextId = s.nextId + 1 }
| Updated value -> { s with items = s.items |> List.map (function { id = id } when id = value.id -> value | item -> item) }
| Deleted { id=id } -> { s with items = s.items |> List.filter (fun x -> x.id <> id) }
| Cleared -> { s with items = [] }
| Snapshotted { items = items } -> { s with items = List.ofArray items }
| Events.Added item -> { s with items = item :: s.items; nextId = s.nextId + 1 }
| Events.Updated value -> { s with items = s.items |> List.map (function { id = id } when id = value.id -> value | item -> item) }
| Events.Deleted { id=id } -> { s with items = s.items |> List.filter (fun x -> x.id <> id) }
| Events.Cleared -> { s with items = [] }
| Events.Snapshotted { items = items } -> { s with items = List.ofArray items }
let fold : State -> Events.Event seq -> State = Seq.fold evolve
let isOrigin = function Events.Cleared | Events.Snapshotted _ -> true | _ -> false
let snapshot state = Snapshotted { items = Array.ofList state.items }
let snapshot state = Events.Snapshotted { items = Array.ofList state.items }

type Command = Add of Todo | Update of Todo | Delete of id: int | Clear
type Command = Add of Events.Todo | Update of Events.Todo | Delete of id: int | Clear

module Commands =

let interpret c (state : Fold.State) =
match c with
| Add value -> [Added { value with id = state.nextId }]
| Add value -> [Events.Added { value with id = state.nextId }]
| Update value ->
match state.items |> List.tryFind (function { id = id } -> id = value.id) with
| Some current when current <> value -> [Updated value]
| Some current when current <> value -> [Events.Updated value]
| _ -> []
| Delete id -> if state.items |> List.exists (fun x -> x.id = id) then [Deleted {id=id}] else []
| Clear -> if state.items |> List.isEmpty then [] else [Cleared]
| Delete id -> if state.items |> List.exists (fun x -> x.id = id) then [Events.Deleted {id=id}] else []
| Clear -> if state.items |> List.isEmpty then [] else [Events.Cleared]

type Service(log, resolve, ?maxAttempts) =

Expand All @@ -65,7 +66,7 @@ type Service(log, resolve, ?maxAttempts) =
let state' = Fold.fold state events
state'.items,events)

member __.List(clientId) : Async<Todo seq> =
member __.List(clientId) : Async<Events.Todo seq> =
query clientId (fun s -> s.items |> Seq.ofList)

member __.TryGet(clientId, id) =
Expand All @@ -74,10 +75,10 @@ type Service(log, resolve, ?maxAttempts) =
member __.Execute(clientId, command) : Async<unit> =
execute clientId command

member __.Create(clientId, template: Todo) : Async<Todo> = async {
member __.Create(clientId, template: Events.Todo) : Async<Events.Todo> = async {
let! state' = handle clientId (Command.Add template)
return List.head state' }

member __.Patch(clientId, item: Todo) : Async<Todo> = async {
member __.Patch(clientId, item: Events.Todo) : Async<Events.Todo> = async {
let! state' = handle clientId (Command.Update item)
return List.find (fun x -> x.id = item.id) state' }
3 changes: 2 additions & 1 deletion samples/Tutorial/AsAt.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ open System

module Events =

let (|ForClientId|) clientId = FsCodec.StreamName.create "Account" clientId

type Delta = { count : int }
type SnapshotInfo = { balanceLog : int[] }
type Contract =
Expand All @@ -57,7 +59,6 @@ module Events =
let down (_index,e) : Contract * _ option * DateTimeOffset option =
e,None,None
FsCodec.NewtonsoftJson.Codec.Create(up,down)
let (|ForClientId|) clientId = Equinox.AggregateId("Account", clientId)

module Fold =

Expand Down
2 changes: 1 addition & 1 deletion samples/Tutorial/Cosmos.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ open System

module Favorites =

let (|ForClientId|) clientId = Equinox.AggregateId("Favorites", clientId)
let (|ForClientId|) clientId = FsCodec.StreamName.create "Favorites" clientId

type Item = { sku : string }
type Event =
Expand Down
2 changes: 1 addition & 1 deletion samples/Tutorial/Counter.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type Event =
| Cleared of Cleared
interface TypeShape.UnionContract.IUnionContract
(* Kind of DDD aggregate ID *)
let (|ForCounterId|) (id : string) = Equinox.AggregateId("Counter", id)
let (|ForCounterId|) (id : string) = FsCodec.StreamName.create "Counter" id

type State = State of int
let initial : State = State 0
Expand Down
6 changes: 4 additions & 2 deletions samples/Tutorial/FulfilmentCenter.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ module FulfilmentCenter =

module Events =

let (|ForFcId|) id = FsCodec.StreamName.create "FulfilmentCenter" id

type AddressData = { address : Address }
type ContactInformationData = { contact : ContactInformation }
type FcData = { details : FcDetails }
Expand All @@ -53,7 +55,6 @@ module FulfilmentCenter =
| FcRenamed of FcName
interface TypeShape.UnionContract.IUnionContract
let codec = FsCodec.NewtonsoftJson.Codec.Create<Event>()
let (|ForFcId|) id = Equinox.AggregateId("FulfilmentCenter", id)

module Fold =

Expand Down Expand Up @@ -146,13 +147,14 @@ Log.dumpMetrics ()
/// Manages ingestion of summary events tagged with the version emitted from FulmentCenter.Service.QueryWithVersion
module FulfilmentCenterSummary =

let (|ForFcId|) id = FsCodec.StreamName.create "FulfilmentCenterSummary" id

module Events =
type UpdatedData = { version : int64; state : Summary }
type Event =
| Updated of UpdatedData
interface TypeShape.UnionContract.IUnionContract
let codec = FsCodec.NewtonsoftJson.Codec.Create<Event>()
let (|ForFcId|) id = Equinox.AggregateId("FulfilmentCenterSummary", id)

type State = { version : int64; state : Types.Summary }
let initial = None
Expand Down
2 changes: 1 addition & 1 deletion samples/Tutorial/Gapless.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ open System
module Events =

let [<Literal>] categoryId = "Gapless"
let (|ForSequenceId|) id = Equinox.AggregateId(categoryId, SequenceId.toString id)
let (|ForSequenceId|) id = FsCodec.StreamName.create categoryId (SequenceId.toString id)

type Item = { id : int64 }
type Snapshotted = { reservations : int64[]; nextId : int64 }
Expand Down
2 changes: 1 addition & 1 deletion samples/Tutorial/Index.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Index
module Events =

let [<Literal>] categoryId = "Index"
let (|ForIndexId|) indexId = Equinox.AggregateId(categoryId, IndexId.toString indexId)
let (|ForIndexId|) indexId = FsCodec.StreamName.create categoryId (IndexId.toString indexId)

type ItemIds = { items : string[] }
type Items<'v> = { items : Map<string,'v> }
Expand Down
2 changes: 1 addition & 1 deletion samples/Tutorial/Sequence.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ open System
module Events =

let [<Literal>] categoryId = "Sequence"
let (|ForSequenceId|) id = Equinox.AggregateId(categoryId, SequenceId.toString id)
let (|ForSequenceId|) id = FsCodec.StreamName.create categoryId (SequenceId.toString id)

type Reserved = { next : int64 }
type Event =
Expand Down
2 changes: 1 addition & 1 deletion samples/Tutorial/Set.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Set
module Events =

let [<Literal>] categoryId = "Set"
let (|ForSetId|) id = Equinox.AggregateId(categoryId, SetId.toString id)
let (|ForSetId|) id = FsCodec.StreamName.create categoryId (SetId.toString id)

type Items = { items : string[] }
type Event =
Expand Down
Loading

0 comments on commit b46c9f5

Please sign in to comment.