Skip to content
CAI
Software that uses CAICheck a score

nstoredev/NStore

58.0

Adequate · 21 September 2026

13.1k

lines of production code

C#

primary language

4

measurements over time

CAI band scale
CAI trend line
CAI lens gauges

What this system is

NStore is an event sourcing library for .NET that provides a structured framework for managing persistent streams of domain events. It supports multiple persistence backends, including in-memory, SQL Server, SQLite, and MongoDB, while offering features like optimistic concurrency, batched writes, and snapshotting. The system enables developers to build domain models using aggregates and process event streams through flexible aggregation and projection mechanisms.

How it got here

2017 — Core architecture and multi-provider expansion

32 changes.

This period established the foundational event sourcing architecture, introducing core abstractions for streams, persistence, and domain aggregates alongside critical behavioral changes like at-least-once delivery. It significantly expanded the project's capabilities by implementing initial persistence providers for MongoDB, SQL Server, and SQLite, while adding TPL-based asynchronous processing and batch operations to enhance performance and scalability.

2019–2026 — tutorial expansion and concurrency reliability

9 changes.

This period focused on enhancing the NStore tutorial application with a complete shopping cart domain model and structured examples for persistence, streams, and aggregates. Concurrently, the core library improved reliability in parallel asynchronous operations by preserving cancellation contexts, while extensive test coverage was added for persistence layers, snapshot stores, and new Docker-based integration testing infrastructure.

Features

Added NStore Quickstart application demonstrating core persistence and stream processing

A new quickstart application has been added to demonstrate the core capabilities of the NStore library. The example showcases setting up an in-memory persistence store, opening a stream for read/write operations, appending events, reading stream chunks, and performing stream aggregation to count unique favorites.

src/NStore.Quickstart · high confidence

Added NStore brand assets

The \logo\ directory now includes the official NStore branding assets: a full logo SVG (\logo.svg\) featuring the 'NStore' wordmark in the Convergence font alongside the geometric icon, a standalone icon-only SVG (\logo-no-text.svg\), and an HTML preview file (\nstore-logo.html\) for visual verification.

logo · high confidence

Added shopping cart domain model and tutorial support infrastructure

The tutorial sample application now includes a complete shopping cart domain model, featuring the ShoppingCart aggregate, ShoppingCartState, and associated event/data classes (ItemAddedToCart, ItemData). To support this and other tutorial scenarios, new support utilities were added in the Support namespace: a CallerEnricher for detailed Serilog logging, a ConsoleLoggerAdapter to bridge Microsoft.Extensions.Logging with NStore's logging interface, a LoggerFactoryAdapter, a JsonMsSqlSerializer for SQL Server persistence, a PersistenceFactory for initializing in-memory or SQL Server backends, and a SerializationHelper for deep cloning and JSON serialization.

src/NStore.Tutorial/Support · high confidence

Asynchronous write batching and subscription processing via TPL Dataflow

The NStore.Tpl library now provides asynchronous persistence and subscription capabilities using .NET's TPL Dataflow. A new \PersistenceBatchAppendDecorator\ batches write operations (\AppendAsync\) into configurable groups for improved performance, while \TplSubscription\ decouples event consumption from the main persistence thread using an \ActionBlock\. These components introduce a new \AsyncWriteJob\ to handle write completion asynchronously.

src/NStore.Tpl · high confidence

Experimental domain runtime and builder introduced

New experimental classes (DomainBuilder, DomainRuntime, Recording) provide a fluent API for configuring persistence, snapshots, and aggregate factories, and expose methods to mutate aggregates, record sessions, and push events to streams.

src/NStore.Domain/Experimental · high confidence

Initial in-memory persistence implementation with multi-partition support

The in-memory persistence layer has been introduced, providing a fully functional \InMemoryPersistence\ store that supports multiple partitions, forward and backward reading, and synchronous reads. This implementation includes a configurable \InMemoryPersistenceOptions\ class for setting up cloning functions and network latency simulation (via \INetworkSimulator\ implementations like \NoNetworkLatencySimulator\ and \ReliableNetworkSimulator\), enabling realistic testing of persistence behavior under various network conditions.

src/NStore.Core/InMemory · high confidence

Initial release of Microsoft SQL Server persistence provider

This change introduces the \NStore.Persistence.MsSql\ package, providing a new SQL Server backend for event sourcing. The implementation includes \MsSqlPersistence\ and \MsSqlContext\ classes that handle database interactions, supporting features such as multi-partition reads, operation-based queries, and configurable connection pooling. It also adds built-in retry logic for transient SQL errors and allows for optional stream idempotency via unique indexes on operation IDs.

src/NStore.Persistence.MsSql · high confidence

Initial release of SQL persistence base implementation

This change introduces the foundational SQL persistence layer for NStore, adding the \NStore.BaseSqlPersistence\ package. It provides abstract base classes (\AbstractSqlContext\, \AbstractSqlPersistence\) and configuration options (\BaseSqlPersistenceOptions\) that concrete providers (like MSSQL) can inherit from. Key capabilities include support for multi-partition reads with individual index ranges, backward reading, stream idempotency (via \OperationId\), and batch operations. The implementation also enforces that negative stream indices are not allowed and handles connection disposal for both synchronous and asynchronous contexts.

src/NStore.BaseSqlPersistence · high confidence

Initial release of SQLite persistence provider

This change introduces a new SQLite-based persistence implementation for NStore, adding the \NStore.Persistence.Sqlite\ package. It provides a full-featured store including batch operations, multi-partition reads, and query support by OperationId, with built-in retry logic for transient SQLite errors and specific exception handling for duplicate stream/index operations.

src/NStore.Persistence.Sqlite · high confidence

Initial sample application with command-line options and multiple persistence backends

The NStore.Sample project has been introduced, providing a runnable demonstration of the library's capabilities. Users can now run the sample app with command-line arguments to select between InMemory and MongoDB storage providers, enable or disable snapshots, toggle a quiet mode for reduced console output, and switch to a fast mode that minimizes simulated network latency. The application demonstrates sequential stream writing, polling clients, and projections, serving as a practical reference for integrating NStore.

src/NStore.Sample · high confidence

Introduce batch snapshot store with best-effort persistence

Added a new \ISnapshotBatchStore\ interface and \DefaultSnapshotBatchStore\ implementation to enable efficient bulk loading and saving of snapshots across multiple partitions. The batch store executes individual read and write operations in parallel to improve throughput, while adopting best-effort semantics for writes: individual snapshot save failures are logged and ignored rather than throwing exceptions, ensuring system availability since state can always be rebuilt from the event stream. The location also includes the core snapshot abstractions (\ISnapshotStore\, \SnapshotInfo\, \ISnapshottable\), a null store implementation for testing, and extension methods for simplified usage.

src/NStore.Core/Snapshots · high confidence

Introduces batch repository and structured aggregate save outcomes

The domain layer now supports efficient multi-aggregate operations via a new \BatchRepository\ and \IBatchRepository\ interface, enabling bulk loading and saving of aggregates with optimistic concurrency control. Saving multiple aggregates returns a \BatchSaveResult\ containing per-aggregate \AggregateSaveResult\ details, which categorize outcomes (such as concurrency conflicts, invariant failures, or idempotent skips) via the \AggregateSaveOutcome\ enum rather than throwing exceptions for all non-successful saves. This change also introduces an \IAggregateFactory\ for external aggregate instantiation, an \AggregateAlreadyInitializedException\ to prevent double-initialization, and an \InvariantCheckFailedException\ to surface business rule violations during save operations.

src/NStore.Domain · high confidence

Introduces structured logging, batched writes, and multi-partition read capabilities

The persistence layer now supports structured logging via a new \INStoreLogger\ interface and \LogDecorator\, allowing implementations to track operations like appends and reads. For performance, a new \IEnhancedPersistence\ interface and \AppendBatchAsync\ extension method enable parallel batched writes with configurable batch sizes and writer counts, returning detailed results for each job (committed, duplicated, or failed). Read capabilities are expanded with \IMultiPartitionPersistenceReader\, which allows querying multiple partitions simultaneously with specific index ranges, supporting both forward and backward reads, as well as retrieving the latest chunk for multiple partitions in a single call. The core \IChunk\ model now includes an \OperationId\ for idempotency, and new exceptions (\DuplicateStreamIndexException\, \InvalidStreamIndexException\) provide clearer error reporting for concurrency and validation issues.

src/NStore.Core/Persistence · high confidence

New NStore tutorial application with Serilog console logging

A new tutorial sample application has been added to demonstrate NStore capabilities, including persistence, streams, and aggregates. The application is configured to use Serilog for structured console logging and supports both in-memory and SQL Server persistence backends.

src/NStore.Tutorial · high confidence

New sample support utilities and TplRepository

The sample application now includes a set of support utilities to enhance development and debugging experiences. A new TplRepository class is introduced to configure consumers using TplSubscription, enabling specific task-based parallel processing patterns. To improve observability, the sample adds a colored console reporter, a null reporter for silent modes, and a custom MongoDB payload serializer that wraps payloads with diagnostic context (such as OS and app version) and implements varied serialization strategies (BSON, JSON, or byte arrays) for testing purposes. Additionally, a progress bar component is added for visual feedback in console applications, and helper extensions for asynchronous iteration and object serialization via Newtonsoft.Json are provided.

src/NStore.Sample/Support · high confidence

New stream aggregation API with flexible payload processing

Users can now aggregate event streams into state using the new \StreamProcessor\ and \AggregateAsync\ extension methods. This change introduces a pluggable \IPayloadProcessor\ interface, allowing state updates to be driven by public or private event-handler methods (e.g., \OnEvent\) or custom lambda expressions. The processor includes built-in caching for reflection calls to optimize performance and supports asynchronous processing, enabling more flexible and efficient stream-to-state reconstruction.

src/NStore.Core/Processing · high confidence

New tutorial series demonstrating persistence, streams, and aggregates

The tutorial application now includes a structured set of examples covering core NStore capabilities. It introduces an abstract base class to manage runtime initialization and logging. New tutorials demonstrate low-level persistence operations, including appending chunks with optional idempotency keys, and stream-based operations such as creating, reading, updating, and deleting streams. Additionally, it provides examples for working with aggregates, showing how to load, mutate, and save a shopping cart entity, as well as how to delete the underlying stream associated with an aggregate.

src/NStore.Tutorial/Tutorials · high confidence

Sample app introduces structured projection processing with metrics and simulation

The sample application now features a dedicated projection subsystem that processes event streams through a pluggable architecture. Users can observe how events are dispatched to specific handlers (such as 'Rooms on Sale' and 'Confirmed Bookings') via an abstract base class that dynamically invokes methods based on event types. The system includes built-in metrics tracking for event counts and dispatch times, supports a 'quiet mode' to suppress console output, and integrates a network simulator to introduce artificial latency, allowing users to visualize processing delays and sequence handling in a controlled environment.

src/NStore.Sample/Projections · high confidence

Sample domain model for room bookings and reservations

The sample application now includes a domain model for managing room bookings. This introduces a Room aggregate that supports enabling/disabling bookings and adding reservations, along with a DateRange value object that validates date intervals and checks for overlaps. The model also includes a ReservationRequest aggregate and enforces invariants, such as preventing a room from being disabled while it has active reservations.

src/NStore.Sample/Domain · high confidence

Behavioural changes

At-least-once delivery semantics for event processing

The \PollingClient\ now advances its checkpoint only after \ISubscription.OnNextAsync\ completes successfully. If the consumer throws or cancels, the chunk is retained and redelivered on the next poll, ensuring at-least-once delivery. Previously, the position was advanced before dispatch, which could permanently skip chunks if the consumer failed. Consumers must now ensure their side effects are idempotent, as permanently failing chunks will be retried indefinitely and block the projection.

(repo-wide) · high confidence

MongoDB persistence layer refactored with batching, custom serialization, and connection options

The MongoDB persistence implementation has been significantly restructured to support batched append operations with configurable retry limits (defaulting to 100 attempts) and a new \BatchRetryLimitExceededException\ for systemic failures. Users can now configure custom payload serialization via \IMongoPayloadSerializer\ and \MongoPayloadSerializer\, and fine-tune connection behavior using options such as \ReadonlyUser\ (to skip index creation for read-only accounts), \CursorBatchSize\ for read performance tuning, and \CreateClientFunction\ for custom MongoClient instantiation. The persistence layer also introduces a dedicated sequence database connection (\SequenceConnectionString\) and collection (\SequenceCollectionName\) for stream position tracking, alongside support for custom chunk types via generics.

src/NStore.Persistence.Mongo · high confidence

Preserves cancellation context during parallel async operations

The library now correctly propagates cancellation tokens and handles failures in parallel asynchronous iterations. A new utility extension method, ForEachAsync, is introduced to execute actions concurrently with a specified degree of parallelism, ensuring that if one task fails or is cancelled, the cancellation context is preserved and surfaced appropriately rather than being lost or masked by subsequent task completions. This improves reliability when processing collections asynchronously, preventing silent failures or incorrect exception handling in scenarios involving multiple concurrent operations.

src/NStore.Core · high confidence

Refactored stream API with new interfaces and concurrency models

The stream handling layer has been refactored to introduce a clearer separation of concerns and improved concurrency support. The core \IStream\ interface now extends \IReadOnlyStream\, which exposes new capabilities such as \PeekAsync\, \IsEmpty\, and \ContainsOperationAsync\. A new \IRandomAccessStream\ interface allows for explicit index-based persistence via \PersistAsync\. The \StreamsFactory\ now provides distinct methods to open streams with different behaviors: standard (\Open\), read-only (\OpenReadOnly\), and optimistic concurrency (\OpenOptimisticConcurrency\). The optimistic concurrency model is implemented by \OptimisticConcurrencyStream\, which enforces version checks and throws \ConcurrencyException\ on conflicts, while \ReadOnlyStream\ prevents write operations. Additionally, a new \IBatchStream\ interface and associated request types (\BatchStreamReadRequest\, \BatchAppendRequest\) have been introduced to support batched read and append operations across multiple streams, aiming to reduce database latency.

src/NStore.Core/Streams · high confidence

Test coverage

Added Docker-based harness for running provider tests against real backends; Added MongoDB batch insert performance benchmarks and test infrastructure; Added MongoDB performance test runner script; Added SQLite persistence test infrastructure; Added comprehensive test coverage for persistence layer; Added comprehensive test suite for domain aggregates, repositories, and batch operations; Added integration tests for MsSql persistence; Added tests for AsyncParallelExtensions behavior; Added tests for DefaultSnapshotBatchStore; Added tests for InMemoryPersistence and PersistenceBatchAppendDecorator; Added tests for Process Manager timeout and state handling; Added tests for date range validation and room booking invariants; Added tests for experimental domain builder and Twitter domain scenario; Added tests for stream processing and method invocation; Added unit tests for persistence layer components; Added unit tests for stream operations and factory.

Dependencies

Adopts central package version management and upgrades to .NET 10.0

The project now uses a centralized \Directory.Packages.props\ file to manage dependency versions, ensuring consistency across all 19 project files. This change includes upgrading the target framework to .NET 10.0 (alongside net8.0 and netstandard2.0 for libraries) and updating key dependencies such as MongoDB.Driver to 3.9.0, xunit to 2.9.3, and Microsoft.Extensions packages to 10.0.11. This centralization simplifies dependency updates and reduces version conflicts across the solution.

(dependencies) · high confidence

Housekeeping

Initial project scaffolding and build configuration

The src directory has been initialized with the core solution structure, including a new .editorconfig for consistent formatting, a global.json pinning the SDK to version 10.0.0, and AssemblyInfo.cs setting the version to 0.0.0.1. The NStore.sln solution file now defines the project hierarchy (NStore.Core, NStore.Domain, persistence providers, etc.), and Directory.Build.props suppresses specific xUnit warnings to streamline the build process.

src · high confidence

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

How this codebase got here

This is the PUBLIC form of this artifact. Findings are listed in full, but the details of SECURITY findings — which rule fired, in which file, on which line, and how to fix it — are deliberately withheld, and any secret-scanner results are excluded entirely. Where detail is absent here it was REMOVED FOR PUBLICATION; it is not missing from the analysis. The complete artifact is available from the repository owner.

Score

  • CAI 60 → 58 (-2.5)
  • Rubric changed (rubric-2026.08.18 → rubric-2026.09.15) — scores are not directly comparable.

Lenses

  • Code Health 57 → 57 (-0.0)
  • Architecture 86 → 84 (-2.1)
  • Maturity 60 → 60 (-0.4)
  • Readiness 73 → 54 (-19.2)
  • Security 68 → 86 (+18.3)
  • Performance 61 → 61 (-0.5)

Resolved (47)

  • Boundary-crossing change coupling: AbstractSqlPersistence.cs ↔ MongoPersistence.cs (src/NStore.BaseSqlPersistence/AbstractSqlPersistence.cs)
  • Boundary-crossing change coupling: BaseSqlPersistenceOptions.cs ↔ MongoPersistence.cs (src/NStore.BaseSqlPersistence/BaseSqlPersistenceOptions.cs)
  • Bounded contexts not declared
  • ClassTooLong: InMemoryPersistence (src/NStore.Core/InMemory/InMemoryPersistence.cs)
  • Coverage not measured — analyzer environment
  • Duplicated block (10 lines × 2) (src/NStore.Persistence.Mongo/MongoPersistence.cs)
  • Duplicated block (14 lines × 2) (src/NStore.Persistence.Mongo/MongoPersistence.cs)
  • Duplicated block (15 lines × 2) (src/NStore.Persistence.MsSql/MsSqlPersistenceOptions.cs)
  • Duplicated block (16 lines × 2) (src/NStore.Persistence.Mongo/MongoPersistence.cs)
  • Duplicated block (17 lines × 2) (src/NStore.BaseSqlPersistence/BaseSqlPersistenceOptions.cs)
  • Duplicated block (18 lines × 2) (src/NStore.Core/InMemory/InMemoryPersistence.cs)
  • Duplicated block (18 lines × 2) (src/NStore.Persistence.Mongo/MongoPersistence.cs)
  • Duplicated block (20 lines × 2) (src/NStore.Persistence.Mongo/MongoPersistence.cs)
  • Duplicated block (25 lines × 2) (src/NStore.BaseSqlPersistence/AbstractSqlPersistence.cs)
  • Further sole-owners (lower concentration)
  • High CVE: SQLitePCLRaw.lib.e_sqlite3 2.0.6
  • High CVE: Snappier 1.0.0
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • High: security finding (details withheld)
  • …and 27 more

New (97)

  • Cross-context type IReadOnlyStream (NStore.Core → NStore.Domain)
  • Cross-context type IStream (NStore.Core → NStore.Tutorial)
  • Cross-context type PartitionReadRequest (NStore.Core → NStore.BaseSqlPersistence)
  • Cross-context type PartitionReadRequest (NStore.Core → NStore.BaseSqlPersistence)
  • Cross-context type PartitionReadRequest (NStore.Core → NStore.BaseSqlPersistence)
  • Cross-context type PartitionReadRequest (NStore.Core → NStore.BaseSqlPersistence)
  • Cross-context type PartitionReadRequest (NStore.Core → NStore.Persistence.Mongo)
  • Cross-context type PartitionReadRequest (NStore.Core → NStore.Persistence.Mongo)
  • Cross-context type PartitionReadRequest (NStore.Core → NStore.Persistence.MsSql)
  • Cross-context type PartitionReadRequest (NStore.Core → NStore.Persistence.Sqlite)
  • Cross-context type PartitionReadRequest (NStore.Core → NStore.Tpl)
  • Cross-context type PartitionReadRequest (NStore.Core → NStore.Tpl)
  • DeadPreprocessorBranch (src/NStore.Core/Persistence/PollingClient.cs)
  • DeadPreprocessorBranch (src/NStore.Persistence.Mongo.Tests/MongoPersistenceFixture.cs)
  • DeadPreprocessorBranch (src/NStore.Persistence.Mongo.Tests/MongoPersistenceFixture.cs)
  • DeadPreprocessorBranch (src/NStore.Persistence.Tests/batch_writes_test.cs)
  • Documentation: no installation or build instructions (README.md)
  • Documentation: no usage examples (README.md)
  • Duplicated block (10 lines × 2) (src/NStore.Persistence.Mongo/MongoPersistence.cs)
  • Duplicated block (10 lines × 2) (src/NStore.Persistence.Mongo/MongoPersistence.cs)
  • …and 77 more

Changes since last survey

  • 3 commits — 1 feature/other, 2 fixes

By area

  • (root) — 2 commits
  • src/NStore.BaseSqlPersistence — 1 commit

Notable commits

  • fix: Fixed limit:0 read in mongo/sql
  • fix: Small fixes
  • change: Updated all references

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

Survey your own repository

nstoredev/NStore was measured the same way every project in this corpus was: the same rubric, at a pinned commit, with the result published in full. Point a surveyor at a repository you know and see whether you agree with it.

About this page

  • The score is its most recent published measurement, taken on 21 September 2026 at a pinned commit. It is not a live figure and does not change until the project is measured again.
  • Measured at commit da69193724c8a65f6048f096f74939ae81d456ea — the exact code this score is about.
  • Scored under rubric-2026.09.15 — the same rubric and the same method as every other entry in this index.
  • Measured by watchdog.canine.dev using codehealth-analyzer preprod-28e75b8e3254.