SkillAgentSearch skills...

Equinox

.NET event sourcing library with CosmosDB, DynamoDB, EventStoreDB, message-db, SqlStreamStore and integration test backends. Focused at stream level; see https://github.com/jet/propulsion for cross-stream projections/subscriptions/reactions

Install / Use

/learn @jet/Equinox

README

Equinox Build Status release NuGet license code size docs status Discord

Equinox is a set of low dependency libraries that allow for event-sourced processing against stream-based stores handling:

Not a framework; you compose the libraries into an architecture that fits your apps' evolving needs.

It does not and will not handle projections and subscriptions. See Propulsion for that.

Table of Contents

Getting Started

Design Motivation

Equinox's design is informed by discussions, talks and countless hours of hard and thoughtful work invested into many previous systems, frameworks, samples, forks of samples, the outstanding continuous work of the EventStore founders and team and the wider DDD-CQRS-ES community. It would be unfair to single out even a small number of people despite the immense credit that is due. Some aspects of the implementation are distilled from Jet.com systems dating all the way back to 2013.

An event sourcing system usually needs to address the following concerns:

  1. Storing events with good performance and debugging capabilities
  2. Transaction processing
    • Optimistic concurrency (handle loading conflicting events and retrying if another transaction overlaps on the same stream)
    • Folding events into a State, updating as new events are added
  3. Decoding events using codecs and formats
  4. Framework and application integration
  5. Projections and Reactions

Designing something that supports all of these as a single integrated solution results in an inflexible and difficult to use framework. Thus, Equinox focuses on two central aspects of event sourcing: items 1 and 2 on the list above.

Of course, the other concerns can't be ignored; thus, they are supported via other libraries that focus on them:

  • FsCodec supports encoding and decoding (concern 3)
  • Propulsion supports projections and reactions (concern 5)

Integration with other frameworks (e.g., Equinox wiring into ASP.NET Core) is something that is intentionally avoided; as you build your application, the nature of how you integrate things will naturally evolve.

We believe the fact Equinox is a library is critical:

  • It gives you the ability to pick your preferred way of supporting your event sourcing system.
  • There's less coupling to worry about as your application evolves over time.

If you're looking to learn more about and/or discuss Event Sourcing and it's myriad benefits, trade-offs and pitfalls as you apply it to your Domain, look no further than the thriving 4000+ member community on the DDD-CQRS-ES Discord; you'll get patient and impartial world class advice 24x7 (there are #equinox, #eventstore and #sql-stream-store channels for questions or feedback). (invite link)

Features

  • Designed not to invade application code; your domain tests can be written directly against your models.
  • Core ideas and features of the library are extracted from ideas and lessons learned from existing production software.
  • Test coverage for it's core features. In addition there are baseline and specific tests for each supported storage system and a comprehensive test and benchmarking story
  • Pluggable event serialization. All encoding is specified in terms of the FsCodec.IEventCodec contract. FsCodec provides for pluggable encoding of events based on:
  • Caching using the .NET MemoryCache to:
    • Minimize round trips; consistent implementation across stores :pray: @DSilence
    • Minimize latency and bandwidth / Request Charges by maintaining the folded state, without needing the Domain Model folded state to be serializable
    • Enable read through caching, coalescing concurrent reads via opt-in LoadOption.AllowStale
  • Mature and comprehensive logging (using Serilog internally), with optimal performance and pluggable integration with your apps hosting context (we ourselves typically feed log info to Splunk and the metrics embedded in the Serilog.Events.LogEvent Properties to Prometheus; see relevant tests for examples)
  • OpenTelemetry Integration (presently only implemented in Equinox.Core and Equinox.MessageDb ... #help-wanted)
  • Equinox.EventStore, Equinox.SqlStreamStore: In-stream Rolling Snapshots:
    • No additional round trips to the store needed at either the Load or Sync points in the flow
    • Support for multiple co-existing compaction schemas for a given stream (A 'compaction' event/snapshot is an Event). This is done by the FsCodec.IEventCodec
      • Compaction events typically do not get deleted (consistent with how EventStoreDB works), although it is safe to do so in concept (there are no assumptions that the events must be contiguous and/or that the number of events implies a specific version etc)
    • While snapshotting can deliver excellent performance especially when allied with the Cache, it's not a panacea, as noted in this EventStore article on the topic
  • Equinox.MessageDb: Adjacent Snapshots:
    • Maintains snapshot events in an adjacent, separated {Category}:snapshot-{StreamId} stream (in contrast to the EventStoreDb and SqlStreamStore RollingState strategy, which embeds the snapshots directly within the stream in question)
    • Generating & storing the snapshot takes place subsequent to the normal appending of events, once every batchSize events. This means the state of the stream can be reconstructed with exactly 2 round-trips to the database (caching can of course remove the snapshot reads on subsequent calls)
    • Note there'
View on GitHub
GitHub Stars496
CategoryData
Updated6d ago
Forks71

Languages

F#

Security Score

100/100

Audited on Mar 22, 2026

No findings