All Projects → Dzoukr → Cosmostore

Dzoukr / Cosmostore

Licence: mit
F# Event store for Azure Cosmos DB, Table Storage, Postgres, LiteDB & ServiceStack

Programming Languages

fsharp
127 projects

Projects that are alternatives of or similar to Cosmostore

Eventstore
The stream database optimised for event sourcing
Stars: ✭ 4,395 (+2753.9%)
Mutual labels:  eventsourcing, eventstore
Eventflow
Async/await first CQRS+ES and DDD framework for .NET
Stars: ✭ 1,932 (+1154.55%)
Mutual labels:  eventsourcing, eventstore
Jes
Java Event Store implementation
Stars: ✭ 32 (-79.22%)
Mutual labels:  eventsourcing, eventstore
Equinox
.NET Event Sourcing library with CosmosDB, EventStoreDB, SqlStreamStore and integration test backends. Focused at stream level; see https://github.com/jet/propulsion for cross-stream projections/subscriptions/reactions
Stars: ✭ 260 (+68.83%)
Mutual labels:  eventstore, postgres
Marten
.NET Transactional Document DB and Event Store on PostgreSQL
Stars: ✭ 1,654 (+974.03%)
Mutual labels:  eventsourcing, postgres
Aggregates.net
.NET event sourced domain driven design model via NServiceBus and GetEventStore
Stars: ✭ 261 (+69.48%)
Mutual labels:  eventsourcing, eventstore
Event Store Client
PHP 7.4 Event Store Client Implementation
Stars: ✭ 93 (-39.61%)
Mutual labels:  eventsourcing, eventstore
rxeventstore
RxEventStore is a module for persisting and querying data using the Event Sourcing pattern and RxJs.
Stars: ✭ 26 (-83.12%)
Mutual labels:  eventstore, eventsourcing
Bifrost
This is the stable release of Dolittle till its out of alpha->beta stages
Stars: ✭ 111 (-27.92%)
Mutual labels:  eventsourcing, eventstore
Kitsvc
⚙ 一個基於 Golang、Consul、Prometheus、EventStore、Gin、Gorm、NSQ 的微服務起始結構。
Stars: ✭ 101 (-34.42%)
Mutual labels:  eventsourcing, eventstore
factcast
This project is archived. A friendly fork can be found at https://github.com/factcast/factcast/
Stars: ✭ 14 (-90.91%)
Mutual labels:  eventstore, eventsourcing
Eventflow.example
DDD+CQRS+Event-sourcing examples using EventFlow following CQRS-ES architecture. It is configured with RabbitMQ, MongoDB(Snapshot store), PostgreSQL(Read store), EventStore(GES). It's targeted to .Net Core 2.2 and include docker compose file.
Stars: ✭ 131 (-14.94%)
Mutual labels:  eventsourcing, eventstore
les
Go directly from an event storming to a working API: Event Markdown / Markup validation & NodeJS CQRS/ES application builder.
Stars: ✭ 48 (-68.83%)
Mutual labels:  eventstore, eventsourcing
Sequent
CQRS & event sourcing framework for Ruby
Stars: ✭ 400 (+159.74%)
Mutual labels:  eventsourcing, eventstore
eventsource-api
Provides an eventsourcing high level API.
Stars: ✭ 12 (-92.21%)
Mutual labels:  eventstore, eventsourcing
Eventstore
EventStore Haskell TCP Client
Stars: ✭ 77 (-50%)
Mutual labels:  eventsourcing, eventstore
event bus postgres
🐘 Postgres event store for event_bus
Stars: ✭ 49 (-68.18%)
Mutual labels:  postgres, eventstore
eventuous
Minimalistic Event Sourcing library for .NET
Stars: ✭ 236 (+53.25%)
Mutual labels:  eventstore, eventsourcing
Quiz
Example real time quiz application with .NET Core, React, DDD, Event Sourcing, Docker and built-in infrastructure for CI/CD with k8s, jenkins and helm
Stars: ✭ 100 (-35.06%)
Mutual labels:  eventsourcing, postgres
Eshoponcontainersddd
Fork of dotnet-architecture/eShopOnContainers in full DDD/CQRS design using my own patterns
Stars: ✭ 126 (-18.18%)
Mutual labels:  eventsourcing, eventstore

CosmoStore

F# Event Store library for various storage providers (Cosmos DB, Table Storage, Marten, InMemory and LiteDB)

Features

  • Storage agnostic F# API
  • Support for Azure Cosmos DB
  • Support for Azure Table Storage
  • Support for Marten
  • Support for In-memory
  • Support for LiteDB
  • Optimistic concurrency
  • ACID compliant
  • Simple Stream querying

Available storage providers

Storage Provider Payload type Package Version Author
none (API definition only) - CosmoStore NuGet @dzoukr
Azure Cosmos DB Newtonsoft.Json CosmoStore.CosmosDb NuGet @dzoukr
Azure Table Storage Newtonsoft.Json CosmoStore.TableStorage NuGet @dzoukr
InMemory Newtonsoft.Json CosmoStore.InMemory NuGet @kunjee
Marten Newtonsoft.Json CosmoStore.Marten NuGet @kunjee
LiteDB BsonValue / BsonDocument CosmoStore.LiteDb NuGet @kunjee
ServiceStack 'a CosmoStore.ServiceStack NuGet @kunjee

What is new in version 3

All previous version of CosmoStore were tightly connected with Newtonsoft.Json library and used its JToken as default payload for events. Since version 3.0 this does not apply anymore. Whole definition of EventStore was rewritten to be fully generic on payload and also on version level. Why? Some libraries not only use different payload than JToken, but possibly use different type for Version then int64 (default before version 3). Authors of libraries using CosmoStore API now can use any payload and any version type that fits best their storage mechanism.

Event store

Event store (defined as F# record) is by design storage agnostic which means that no matter if you use Cosmos DB or Table Storage, the API is the same.

type EventStore<'payload,'version> = {
    AppendEvent : StreamId -> ExpectedVersion<'version> -> EventWrite<'payload> -> Task<EventRead<'payload,'version>>
    AppendEvents : StreamId -> ExpectedVersion<'version> -> EventWrite<'payload> list -> Task<EventRead<'payload,'version> list>
    GetEvent : StreamId -> 'version -> Task<EventRead<'payload,'version>>
    GetEvents : StreamId -> EventsReadRange<'version> -> Task<EventRead<'payload,'version> list>
    GetEventsByCorrelationId : Guid -> Task<EventRead<'payload,'version> list>
    GetStreams : StreamsReadFilter -> Task<Stream<'version> list>
    GetStream : StreamId -> Task<Stream<'version>>
    EventAppended : IObservable<EventRead<'payload,'version>>
}

Each function on record is explained in separate chapter.

Initializing Event store for Azure Cosmos DB

Cosmos DB Event store has own configuration type that follows some specifics of database like Request units throughput.

type Configuration = {
    DatabaseName : string
    ContainerName : string
    ConnectionString : string
    Throughput : int
    InitializeContainer : bool
}

Note: If you don't know these terms check official documentation

Configuration can be created "manually" or use default setup (fixed collection with 400 RU/s)

open CosmoStore

let cosmosDbUrl = Uri "https://mycosmosdburl" // check Keys section on Azure portal
let cosmosAuthKey = "VeryPrivateKeyValue==" // check Keys section on Azure portal
let myConfig = CosmosDb.Configuration.CreateDefault cosmosDbUrl cosmosAuthKey

let eventStore = myConfig |> CosmosDb.EventStore.getEventStore

Initializing Event store for Azure Table Storage

Configuration for Table Storage is much easier since we need only account name and authentication key.

type StorageAccount =
    | Cloud of accountName:string * authKey:string
    | LocalEmulator

type Configuration = {
    DatabaseName : string
    Account : StorageAccount
}

As for Cosmos DB, you can easily create default configuration.

open CosmoStore

let storageAccountName = "myStoreageAccountName" // check Keys section on Azure portal
let storageAuthKey = "VeryPrivateKeyValue==" // check Keys section on Azure portal
let myConfig = TableStorage.Configuration.CreateDefault storageAccountName storageAuthKey

let eventStore = myConfig |> TableStorage.EventStore.getEventStore

Writing Events to Stream

Events are data structures you want to write (append) to some "shelf" also known as Stream. Event for writing is defined as this type:

type EventWrite<'payload> = {
    Id : Guid
    CorrelationId : Guid option
    CausationId : Guid option
    Name : string
    Data : 'payload
    Metadata : 'payload option
}

When writing Events to some Stream, you usually expect them to be written having some version hence you must specify optimistic concurrency strategy. For this purpose the type ExpectedVersion exists:

type ExpectedVersion<'version> =
    | Any
    | NoStream
    | Exact of 'version

There are two defined functions to write Event to Stream. AppendEvent for writing single Event and AppendEvents to write more Events.

let expected = ExpectedVersion.NoStream // we are expecting brand new stream
let eventToWrite = ... // get new event to be written
let streamId = "MyAmazingStream"

// writing first event
eventToWrite |> eventStore.AppendEvent streamId expected 

let moreEventsToWrite = ... // get list of another events
let newExpected = ExpectedVersion.Exact 2L // we are expecting next event to be in 2nd version

// writing another N events
moreEventsToWrite |> eventStore.AppendEvents streamId newExpected

If everything goes well, you will get back list (in Task) of written events (type EventRead - explained in next chapter).

Reading Events from Stream

When reading back Events from Stream, you'll a little bit more information than you wrote:

type EventRead<'payload,'version> = {
    Id : Guid
    CorrelationId : Guid option
    CausationId : Guid option
    StreamId : StreamId
    Version: 'version
    Name : string
    Data : 'payload
    Metadata : 'payload option
    CreatedUtc : DateTime
}

You have two options how to read back stored Events. You can read single Event by Version using GetEvent function:

// return 2nd Event from Stream
let singleEvent = 2L |> eventStore.GetEvent "MyAmazingStream"

Or read list of Events using GetEvents function. For such reading you need to specify the range:

// return 1st-2nd Event from Stream
let firstTwoEvents = EventsReadRange.VersionRange(1,2) |> eventStore.GetEvents "MyAmazingStream"

// return all events
let allEvents = EventsReadRange.AllEvents |> eventStore.GetEvents "MyAmazingStream"

To fully understand what are the possibilities have a look at EventsReadRange definition:

type EventsReadRange<'version> =
    | AllEvents
    | FromVersion of 'version
    | ToVersion of 'version
    | VersionRange of fromVersion:'version * toVersion:'version

If you are interested in Events based on stored CorrelationId, you can use function introduced in version 2 - GetEventsByCorrelationId

let myCorrelationId = ... // Guid value
let correlatedEvents = myCorrelationId |> eventStore.GetEventsByCorrelationId

Reading Streams from Event store

Each Stream has own metadata:

type Stream = {
    Id : string
    LastVersion : int64
    LastUpdatedUtc : DateTime
}

If you know exact value of Stream Id, you can use function GetStream. To query more Streams, use GetStreams function. The querying works similar way as filtering Events by range, but here you can query Streams by Id:

let allAmazingStream = StreamsReadFilter.StartsWith("MyAmazing") |> eventStore.GetStreams
let allStreams = StreamsReadFilter.AllStream |> eventStore.GetStreams

The complete possibilities are defined by StreamsReadFilter type:

type StreamsReadFilter =
    | AllStreams
    | StartsWith of string
    | EndsWith of string
    | Contains of string

Observing appended events

Since version 1.4.0 you can observe appended events by hooking to EventAppended property IObservable<EventRead>. Use of FSharp.Control.Reactive library is recommended, but not required.

Comparison with Jet's Equinox?

Coming from Jet's Equinox? Please see amazing comment describing conceptual differences between Equinox and CosmoStore written by @bartelink: https://github.com/Dzoukr/CosmoStore/issues/6#issuecomment-477481777

Known issues (Azure Table Storage only)

Azure Table Storage currently allows only 100 operations (appends) in one batch. CosmoStore reserves one operation per batch for Stream metadata, so if you want to append more than 99 events to single Stream, you will get InvalidOperationException.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].