Skip to content

Commit

Permalink
Extract Store CompositionRoot to common project so Web and CLI can share
Browse files Browse the repository at this point in the history
  • Loading branch information
bartelink committed Nov 30, 2018
1 parent 4e17f0d commit e387296
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 50 deletions.
9 changes: 8 additions & 1 deletion Equinox.sln
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Equinox.Cosmos", "src\Equin
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Equinox.Cosmos.Integration", "tests\Equinox.Cosmos.Integration\Equinox.Cosmos.Integration.fsproj", "{DE0FEBF0-72DC-4D4A-BBA7-788D875D6B4B}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Web", "samples\Store\Web\Web.fsproj", "{1B0D4568-96FD-4083-8520-CD537C0B2FF0}"
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Web", "samples\Store\Web\Web.fsproj", "{1B0D4568-96FD-4083-8520-CD537C0B2FF0}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Infrastructure", "samples\Store\Infrastructure\Infrastructure.fsproj", "{ACE52D04-2FE3-4FD6-A066-9C81429C3997}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -110,6 +112,10 @@ Global
{1B0D4568-96FD-4083-8520-CD537C0B2FF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1B0D4568-96FD-4083-8520-CD537C0B2FF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1B0D4568-96FD-4083-8520-CD537C0B2FF0}.Release|Any CPU.Build.0 = Release|Any CPU
{ACE52D04-2FE3-4FD6-A066-9C81429C3997}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ACE52D04-2FE3-4FD6-A066-9C81429C3997}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ACE52D04-2FE3-4FD6-A066-9C81429C3997}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ACE52D04-2FE3-4FD6-A066-9C81429C3997}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -120,6 +126,7 @@ Global
{406A280E-0708-4B12-8443-8FD5660CD271} = {D67D5A5F-2E59-4514-A997-FEBDC467AAF6}
{0B2D5815-D6A5-4AAC-9B75-D57B165E2A92} = {D67D5A5F-2E59-4514-A997-FEBDC467AAF6}
{1B0D4568-96FD-4083-8520-CD537C0B2FF0} = {D67D5A5F-2E59-4514-A997-FEBDC467AAF6}
{ACE52D04-2FE3-4FD6-A066-9C81429C3997} = {D67D5A5F-2E59-4514-A997-FEBDC467AAF6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {177E1E7B-E275-4FC6-AE3C-2C651ECCF71E}
Expand Down
1 change: 1 addition & 0 deletions cli/Equinox.Cli/Equinox.Cli.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<ProjectReference Include="..\..\src\Equinox.MemoryStore\Equinox.MemoryStore.fsproj" PrivateAssets="all" />
<ProjectReference Include="..\..\samples\Store\Backend\Backend.fsproj" PrivateAssets="all" />
<ProjectReference Include="..\..\samples\Store\Domain\Domain.fsproj" PrivateAssets="all" />
<ProjectReference Include="..\..\samples\Store\Infrastructure\Infrastructure.fsproj" PrivateAssets="all" />
<ProjectReference Include="..\..\src\Equinox.EventStore\Equinox.EventStore.fsproj" />
</ItemGroup>

Expand Down
46 changes: 1 addition & 45 deletions cli/Equinox.Cli/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ open Equinox.EventStore
open Equinox.Cli.Infrastructure
open Serilog
open Serilog.Events
open Store.CompositionRoot
open System
open System.Threading

Expand Down Expand Up @@ -122,12 +123,6 @@ module Cosmos =
.Connect("equinox-cli", discovery)
let createGateway connection (maxItems,maxEvents) = EqxGateway(connection, EqxBatchingPolicy(defaultMaxItems=maxItems, maxEventsPerSlice=maxEvents))

[<RequireQualifiedAccess; NoEquality; NoComparison>]
type Store =
| Mem of Equinox.MemoryStore.VolatileStore
| Es of GesGateway
| Cosmos of EqxGateway * databaseId: string * collectionId: string

module Test =
let run log testsPerSecond duration errorCutoff reportingIntervals (clients : ClientId[]) runSingleTest =
let mutable idx = -1L
Expand All @@ -136,45 +131,6 @@ module Test =
clients.[clientIndex % clients.Length]
let selectClient = async { return async { return selectClient() } }
Local.runLoadTest log reportingIntervals testsPerSecond errorCutoff duration selectClient runSingleTest
let serializationSettings = Newtonsoft.Json.Converters.FSharp.Settings.CreateCorrect()
let genCodec<'Union when 'Union :> TypeShape.UnionContract.IUnionContract>() = Equinox.UnionCodec.JsonUtf8.Create<'Union>(serializationSettings)
type EsResolver(useCache) =
member val Cache =
if useCache then
let c = Equinox.EventStore.Caching.Cache("Cli", sizeMb = 50)
CachingStrategy.SlidingWindow (c, TimeSpan.FromMinutes 20.) |> Some
else None
member __.CreateAccessStrategy snapshot =
match snapshot with
| None -> None
| Some snapshot -> Equinox.EventStore.AccessStrategy.RollingSnapshots snapshot |> Some
type CosmosResolver(useCache) =
member val Cache =
if useCache then
let c = Equinox.Cosmos.Caching.Cache("Cli", sizeMb = 50)
Equinox.Cosmos.CachingStrategy.SlidingWindow (c, TimeSpan.FromMinutes 20.) |> Some
else None
member __.CreateAccessStrategy snapshot =
match snapshot with
| None -> None
| Some snapshot -> AccessStrategy.Snapshot snapshot |> Some
type Builder(store, useCache, useUnfolds) =
member __.ResolveStream
( codec : Equinox.UnionCodec.IUnionEncoder<'event,byte[]>,
fold: ('state -> 'event seq -> 'state),
initial: 'state,
snapshot: (('event -> bool) * ('state -> 'event))) =
let snapshot = if useUnfolds then Some snapshot else None
match store with
| Store.Mem store ->
Equinox.MemoryStore.MemoryStreamBuilder(store, fold, initial).Create
| Store.Es gateway ->
let resolver = EsResolver(useCache)
GesStreamBuilder<'event,'state>(gateway, codec, fold, initial, ?access = resolver.CreateAccessStrategy(snapshot), ?caching = resolver.Cache).Create
| Store.Cosmos (gateway, databaseId, connectionId) ->
let resolver = CosmosResolver(useCache)
let store = EqxStore(gateway, EqxCollections(databaseId, connectionId))
EqxStreamBuilder<'event,'state>(store, codec, fold, initial, ?access = resolver.CreateAccessStrategy snapshot, ?caching = resolver.Cache).Create

let createTest store test (cache,unfolds) log =
let builder = Builder(store, cache, unfolds)
Expand Down
59 changes: 59 additions & 0 deletions samples/Store/Infrastructure/CompositionRoot.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/// TL;DR There is no secret plan to make a package out of this;
/// it's infrastructure that makes sense solely in the context of this test rig
/// ...depending on 3 stores is not a goal or achievement
/// Yes, they live together here - that's because it's infrastructure that makes sense in the context of this test rig
/// It also proves the point that the Domain and backend are not bound to the Store, which is important too
/// IRL, it's unlikely you'd want to do storage switching in this manner
module Store.CompositionRoot

open System

let serializationSettings = Newtonsoft.Json.Converters.FSharp.Settings.CreateCorrect()
let inline genCodec<'Union when 'Union :> TypeShape.UnionContract.IUnionContract>() = Equinox.UnionCodec.JsonUtf8.Create<'Union>(serializationSettings)

type private EsResolver(useCache) =
member val Cache =
if useCache then
let c = Equinox.EventStore.Caching.Cache("Cli", sizeMb = 50)
Equinox.EventStore.CachingStrategy.SlidingWindow (c, TimeSpan.FromMinutes 20.) |> Some
else None
member __.CreateAccessStrategy snapshot =
match snapshot with
| None -> None
| Some snapshot -> Equinox.EventStore.AccessStrategy.RollingSnapshots snapshot |> Some
type private CosmosResolver(useCache) =
member val Cache =
if useCache then
let c = Equinox.Cosmos.Caching.Cache("Cli", sizeMb = 50)
Equinox.Cosmos.CachingStrategy.SlidingWindow (c, TimeSpan.FromMinutes 20.) |> Some
else None
member __.CreateAccessStrategy snapshot =
match snapshot with
| None -> None
| Some snapshot -> Equinox.Cosmos.AccessStrategy.Snapshot snapshot |> Some

[<RequireQualifiedAccess; NoEquality; NoComparison>]
type Store =
| Mem of Equinox.MemoryStore.VolatileStore
| Es of Equinox.EventStore.GesGateway
| Cosmos of Equinox.Cosmos.EqxGateway * databaseId: string * collectionId: string

type Builder(store, useCache, useUnfolds) =
member __.ResolveStream
( codec : Equinox.UnionCodec.IUnionEncoder<'event,byte[]>,
fold: ('state -> 'event seq -> 'state),
initial: 'state,
snapshot: (('event -> bool) * ('state -> 'event))) =
let snapshot = if useUnfolds then Some snapshot else None
match store with
| Store.Mem store ->
Equinox.MemoryStore.MemoryStreamBuilder(store, fold, initial).Create
| Store.Es gateway ->
let resolver = EsResolver(useCache)
Equinox.EventStore.GesStreamBuilder<'event,'state>(gateway, codec, fold, initial, ?access = resolver.CreateAccessStrategy(snapshot), ?caching = resolver.Cache).Create
| Store.Cosmos (gateway, databaseId, connectionId) ->
let resolver = CosmosResolver(useCache)
let store = Equinox.Cosmos.EqxStore(gateway, Equinox.Cosmos.EqxCollections(databaseId, connectionId))
Equinox.Cosmos.EqxStreamBuilder<'event,'state>(store, codec, fold, initial, ?access = resolver.CreateAccessStrategy snapshot, ?caching = resolver.Cache).Create
30 changes: 30 additions & 0 deletions samples/Store/Infrastructure/Infrastructure.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
<WarningLevel>5</WarningLevel>
<IsTestProject>false</IsTestProject>
<DisableImplicitFSharpCoreReference>true</DisableImplicitFSharpCoreReference>
<DisableImplicitSystemValueTupleReference>true</DisableImplicitSystemValueTupleReference>
</PropertyGroup>

<ItemGroup>
<Compile Include="CompositionRoot.fs" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\Equinox\Equinox.fsproj" />
<ProjectReference Include="..\..\..\src\Equinox.Cosmos\Equinox.Cosmos.fsproj" />
<ProjectReference Include="..\..\..\src\Equinox.EventStore\Equinox.EventStore.fsproj" />
<ProjectReference Include="..\..\..\src\Equinox.MemoryStore\Equinox.MemoryStore.fsproj" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
<!--Handle TypeShape-restriction; would otherwise use 3.1.2.5-->
<PackageReference Include="FSharp.Core" Version="4.0.0.1" Condition=" '$(TargetFramework)' == 'net461' " />
<PackageReference Include="FSharp.Core" Version="4.3.4" Condition=" '$(TargetFramework)' == 'netcoreapp2.1' " />
<PackageReference Include="Jet.JsonNet.Converters" Version="0.0.3-CI69140" />
<Reference Include="System.Runtime.Caching" Condition=" '$(TargetFramework)' != 'netstandard2.0' " />
</ItemGroup>

</Project>
21 changes: 17 additions & 4 deletions samples/Store/Web/Startup.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,31 @@ open Microsoft.Extensions.DependencyInjection
open Serilog
open System

type Store() =
member __.GetStream() = failwith "TODO"
open global.Store.CompositionRoot

type Startup(configuration: IConfiguration) =
type Startup(_configuration: IConfiguration) =
let configureMvc (s : IServiceCollection) =
s.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1) |> ignore
let configureApp (services : IServiceCollection) =
let regF (factory : IServiceProvider -> 'T) = services.AddSingleton<'T>(fun (sp: IServiceProvider) -> factory sp) |> ignore

let store = failwith "TODO parse cmdline"
services.AddSingleton<Store>() |> ignore

regF <| fun sp -> Backend.Favorites.Service(sp.GetService<ILogger>(), sp.GetService<Store>().GetStream())
let useCache, useUnfolds = false, false
services.AddSingleton(Builder(store, useCache, useUnfolds)) |> ignore

let mkFavorites (log: ILogger, builder: Builder) =
let fold, initial, snapshot = Domain.Favorites.Folds.fold, Domain.Favorites.Folds.initial, Domain.Favorites.Folds.snapshot
let codec = genCodec<Domain.Favorites.Events.Event>()
Backend.Favorites.Service(log, builder.ResolveStream(codec,fold,initial,snapshot))
regF <| fun sp -> mkFavorites (sp.GetService(), sp.GetService())

let mkSaves (log: ILogger, builder: Builder) =
let fold, initial, snapshot = Domain.SavedForLater.Folds.fold, Domain.SavedForLater.Folds.initial, Domain.SavedForLater.Folds.snapshot
let codec = genCodec<Domain.SavedForLater.Events.Event>()
Backend.SavedForLater.Service(log, builder.ResolveStream(codec,fold,initial,snapshot), maxSavedItems=50, maxAttempts=3)
regF <| fun sp -> mkSaves (sp.GetService(), sp.GetService())

//member val Configuration : IConfiguration = null with get, set

Expand Down
1 change: 1 addition & 0 deletions samples/Store/Web/Web.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<ProjectReference Include="..\..\..\src\Equinox.EventStore\Equinox.EventStore.fsproj" />
<ProjectReference Include="..\Backend\Backend.fsproj" />
<ProjectReference Include="..\Domain\Domain.fsproj" />
<ProjectReference Include="..\Infrastructure\Infrastructure.fsproj" />
</ItemGroup>

</Project>

0 comments on commit e387296

Please sign in to comment.