Skip to content
CAI
Software that uses CAICheck a score

looplab/eventhorizon

38.1

Weak · 21 September 2026

13.6k

lines of production code

Go

primary language

4

measurements over time

CAI band scale
CAI trend line
CAI lens gauges

What this system is

This system is a comprehensive Go framework for building event-sourced and CQRS-based applications. It provides core abstractions for managing aggregates, handling commands, and processing events through various backends like MongoDB, Redis, Kafka, and NATS. The framework also includes utilities for HTTP integration, distributed tracing, and testing, enabling developers to construct scalable, distributed systems with reliable event handling and persistence.

How it got here

2014–2017 — comprehensive infrastructure and storage expansion

18 changes.

This period focused on significantly expanding the framework's capabilities by introducing new storage backends, including in-memory, MongoDB, and Redis-based implementations for event stores, repositories, and event buses. The work also established core architectural components such as an event-sourced aggregate store with snapshot support, a command handler bus, and HTTP utilities, while providing practical examples to demonstrate these new features.

2018–2020 — Middleware and example expansion

17 changes.

This period focused on extending the framework's capabilities through new middleware for asynchronous processing, scheduling, and observation, alongside enhanced projector and saga handling. The release also introduced a debug event recorder and expanded the example applications, including a complete TodoMVC implementation and an Elm-based frontend.

2021–2026 — Extensive feature expansion and infrastructure

17 changes.

This period was defined by a massive expansion of the codebase, introducing new implementations for event buses (Kafka, NATS), codecs (JSON, BSON), and outboxes (MongoDB, memory). The work also significantly broadened the framework's capabilities with namespace support, command validation, distributed locking, and OpenTracing integration, while establishing robust acceptance and performance testing across all new components.

Features

Add CommandCodec and EventCodec for JSON and BSON serialization

Added new codec implementations for serializing and deserializing commands and events in both JSON and BSON formats. The BSON codecs (in \codec/bson\) include a custom UUID registry to ensure backward compatibility with existing data encoded using mongo-driver v1, while the JSON codecs (in \codec/json\) provide equivalent functionality for JSON-based workflows. Each codec handles marshaling and unmarshaling of command and event data, preserving context and metadata. Tests verify round-trip consistency and backward compatibility for BSON.

codec/bson · high confidence

Add Google Cloud Pub/Sub event bus implementation

Introduces a new GCP Pub/Sub-based event bus that publishes events to a GCP topic and supports configurable topic options, custom codecs, and message ordering. Includes integration and load tests for the new implementation.

eventbus/gcp · high confidence

Add Kafka-based event bus implementation

Introduces a new Kafka-backed EventBus implementation in the eventbus/kafka package. The new component supports configuring the Kafka broker address list, enabling automatic topic creation, setting the number of partitions, specifying a custom topic name, and defining the consumer start offset. It also includes integration and load tests for the new bus.

eventbus/kafka · high confidence

Add MongoDB example for guest list application

Added a new example demonstrating a CQRS/ES application using the MongoDB adapter. The example includes the main implementation file (mongodb.go) and an integration test (mongodb\_test.go) that sets up event stores, repositories, and command buses to manage invitations and guest lists, allowing users to see how to integrate MongoDB with the event horizon framework.

examples/guestlist/mongodb · high confidence

Add MongoDB outbox implementation with atomic storage and publishing

Introduces a new MongoDB-based outbox implementation that stores and publishes events atomically. The new \outbox/mongodb\ package provides an \Outbox\ struct that manages event handlers, supports custom collection names, and includes a \GracefulContext\ for managing context cancellation with a grace period. Tests verify handler registration, integration with MongoDB, and validation of collection names (e.g., rejecting spaces).

outbox/mongodb · high confidence

Add OpenTracing integration for commands, events, and repositories

The tracing package now provides OpenTracing support across the framework. Command handlers, event handlers, event buses, and repositories are wrapped with middleware that creates and manages OpenTracing spans, allowing distributed tracing of command and event processing as well as repository operations.

tracing · high confidence

Add Redis-backed event bus using Redis Streams

Introduces a new Redis-based event bus implementation that publishes events to a Redis Stream and uses consumer groups for asynchronous, thread-safe event handling. The implementation includes integration and load tests to verify correct event propagation and performance.

eventbus/redis · high confidence

Add Todo domain implementation with event sourcing

The Todo MVC example now includes a complete backend domain implementation for managing a todo list. This adds an event-sourced \Aggregate\ that handles commands like creating, deleting, and modifying todo items, along with a \Projector\ that maintains a \TodoList\ read model. The domain also defines specific commands (e.g., \AddItem\, \CheckItem\) and events (e.g., \ItemAdded\, \ItemChecked\) to support the example's functionality.

examples/todomvc/backend/domains · high confidence

Add UUID alias type to ease replacement

The uuid package now provides a UUID alias type and wrapper functions (New, Parse, MustParse) that delegate to the underlying github.com/google/uuid library. This allows forks of Event Horizon to replace the underlying UUID implementation more easily, as the package exposes a stable interface for common UUID operations.

uuid · medium confidence

Add aggregate store with event publishing

A new AggregateStore implementation is introduced in the model package, providing a concrete store that loads and saves aggregates via a ReadWriteRepo. The store automatically publishes uncommitted events to an injected EventHandler (such as an EventBus) after each successful save, and clears the event queue. This adds the capability to automatically propagate domain events from aggregates to the event bus upon persistence.

aggregatestore/model · high confidence

Add async and ephemeral event handler middleware

Users can now wrap event handlers with an async middleware that processes events in a separate goroutine and reports errors via a channel, or with an ephemeral middleware that marks handlers as ephemeral for optimization purposes. The async middleware's error type implements Unwrap() to support error inspection, and tests for both middleware are included.

middleware/eventhandler/async · high confidence

Add command validation middleware

Introduces a new command validation middleware that intercepts commands to run a \Validate()\ method before execution. The middleware wraps commands to attach a validation function, and returns a structured \Error\ type that supports error unwrapping and checking, ensuring that invalid commands are rejected early.

middleware/commandhandler/validate · high confidence

Add ephemeral observer middleware for event handlers

Introduced a new observer middleware in the event handler package that allows multiple handlers to process the same event by appending a group suffix to the handler type. The middleware supports several grouping strategies: named groups for manual grouping, UUID groups for unique identifiers, random groups for isolation, and hostname-based groups for per-host observers. This enables flexible event observation patterns without modifying existing handler implementations.

middleware/eventhandler/observer · high confidence

Add event handler waiter for matching events

Introduced a new \waiter\ package providing an \EventHandler\ that allows consumers to wait for specific events matching a given criteria. The \EventHandler\ forwards incoming events to registered \EventListener\s, which can filter events using a match function. Users can call \Listen\ to register a match function and then \Wait\ to block (or use a context) until a matching event arrives or the context expires. This adds a new capability for synchronously waiting on asynchronous event streams.

eventhandler/waiter · high confidence

Add guestlist domain with invitation management and saga logic

Added the guestlist domain package containing the InvitationAggregate, which handles commands to create, accept, decline, confirm, and deny invitations. The update includes the associated command and event definitions, a projector to maintain a read model for individual invitations, and a saga that automatically confirms or denies invites based on a guest limit. Additionally, a logging middleware and a global guest list projector for counting guests are included in the setup.

examples/guestlist/domains · high confidence

Add in-memory caching read repository

A new cached read repository implementation has been added to the repo/cache package. It wraps an underlying ReadWriteRepo and stores fetched entities in an in-memory map, automatically invalidating the cache when relevant events are received. This provides a transparent caching layer for read operations, improving performance for frequently accessed, smaller collections. The implementation includes both the cache logic and its associated tests.

repo/cache · high confidence

Add in-memory outbox implementation

Added a new in-memory outbox implementation for eventhorizon, providing an in-memory store and publisher for events. The implementation includes a graceful context mechanism for handling cancellation and a periodic sweep process for processing unhandled events. Tests and benchmarks have been added to verify the outbox functionality.

outbox/memory · high confidence

Add in-memory repository implementation

A new in-memory repository implementation is introduced, providing an in-memory storage solution for read models using JSON encoding. This addition includes the core repository logic and its associated acceptance tests, enabling developers to use a lightweight, non-persistent storage option for testing or simple use cases.

repo/memory · high confidence

Add local lock middleware for command handlers

A new lock middleware has been added to the command handler pipeline, allowing developers to ensure that only one command per aggregate ID is processed at a time. The implementation includes a local lock provider (suitable for single-instance environments) and a middleware that acquires a lock before handling a command and releases it afterward, returning an error if the lock is already held.

middleware/commandhandler/lock · high confidence

Add memory-based guest list example

A new example application demonstrating a CQRS/ES architecture using in-memory storage for the event store, event bus, and repositories. The example shows how to set up and run a guest list system where users can send invitations, accept or decline them, and view the final guest list status.

examples/guestlist/memory · high confidence

Add outbox pattern example for atomic event storage and publishing

A new example demonstrating the outbox pattern is now available in the guestlist application. This example shows how to store and publish events atomically using a MongoDB-backed outbox, ensuring that domain events are reliably persisted and distributed via the event bus.

examples/guestlist/outbox · high confidence

Add persistence to the command scheduler middleware

The command scheduler middleware now supports persisting scheduled commands to a repository, allowing them to survive restarts or crashes. The \Scheduler\ type includes a \Load\ method to retrieve previously scheduled commands from storage, and the middleware wraps commands with execution times to be scheduled and persisted. Tests verify immediate execution, delayed execution, and persistence across restarts using both in-memory and MongoDB repositories.

middleware/commandhandler/scheduler · high confidence

Add scheduler middleware for periodic event handling

A new scheduler middleware has been added to the event handler pipeline, enabling the periodic scheduling of events using cron-style expressions. This allows applications to configure background tasks that trigger event handling at specified intervals, with support for cancellation via context. The implementation includes a \Scheduler\ struct that manages scheduled events and a middleware that routes these events to the handler chain.

middleware/eventhandler/scheduler · high confidence

Add version-aware read repository middleware with context-based min version support

A new versioned read repository middleware is introduced, allowing queries to specify a minimum entity version via context. The middleware wraps an existing read repository and, when a min version is present in the context, ensures the returned entity meets or exceeds that version. If the entity's version is too low, the repository retries the find operation with exponential backoff (up to 5 seconds) until the deadline expires or the version requirement is met. Context marshaling and unmarshaling for the min version key are also provided.

repo/version · high confidence

Added debug event recording capability

A new \recorder\ package has been introduced, providing a debug wrapper around the \eventhorizon.EventStore\ that records all handled events. This allows users to track the status (Pending, Succeeded, or Failed) of each event during the recording phase, enabling easier debugging and testing of event handling workflows.

eventstore/recorder · high confidence

Added distributed tracing to the TodoMVC backend

The TodoMVC backend example now includes distributed tracing capabilities. A new \tracing.go\ file configures a Jaeger/OpenTracing tracer that sends spans in Zipkin format to a configurable URL. This tracing is integrated into the HTTP server, command bus, event bus, event store, and outbox, allowing users to monitor and debug the application's internal operations.

examples/todomvc/backend · high confidence

Automated golangci-lint binary installation script

A new shell script, scripts/build-linter.sh, has been added to the repository. This script automates the installation of the golangci-lint tool by fetching the version specified in .tool-versions and downloading the corresponding binary to the ./bin directory.

scripts · high confidence

Elm-based TodoMVC example added

A new TodoMVC example built with Elm has been added to the examples directory. The implementation includes the Elm source code (Main.elm, Todo.elm), an Elm project configuration (elm.json) specifying Elm 0.19.1 and its dependencies, and the associated frontend assets (index.html, CSS stylesheets, and a .gitignore file).

examples/todomvc/frontend · high confidence

Introduce CQRS saga event handler

Added a new EventHandler implementation for CQRS sagas, which listens to events and triggers commands via a provided command handler. The implementation includes a Saga interface, an Error type for saga-specific errors, and a NewEventHandler constructor. Tests verify that events are correctly routed to the saga and that missing events return an appropriate error.

eventhandler/saga · high confidence

Introduce CommandHandler for routing commands to registered handlers

The command handler bus now provides a concrete CommandHandler struct that routes commands to registered handlers. It enforces that each command type has only one handler (returning ErrHandlerAlreadySet on duplicate registration) and returns ErrHandlerNotFound when no handler is registered for a command. The implementation uses a read-write mutex for thread-safe handler storage and validates commands via eh.CheckCommand before dispatching.

commandhandler/bus · high confidence

Introduce aggregate command handler with early cancellation and error wrapping

A new CommandHandler is added to the aggregate package to dispatch commands to aggregates, loading them from a store and saving resulting events. The implementation includes early cancellation via context checks, validation of incoming commands, and specific error handling: it returns ErrAggregateNotFound when an aggregate is missing, and wraps aggregate command errors in an AggregateError type. Tests verify the handler's behavior, including context propagation, error conditions, and performance.

commandhandler/aggregate · high confidence

Introduce event-sourced aggregate store with snapshot support

The \aggregatestore/events\ package now provides a complete event-sourcing implementation for aggregates. It introduces the \VersionedAggregate\ interface and an \AggregateBase\ struct that aggregates can embed to handle versioning and event tracking. The \AggregateStore\ manages loading aggregates by replaying events from an event store, and now supports optional snapshots via configurable \SnapshotStrategy\ implementations (e.g., taking snapshots every N events or at fixed time intervals). This allows users to build and persist aggregates using the event-sourcing pattern with improved performance for large event streams.

aggregatestore/events · high confidence

Introduce local in-memory event bus implementation

A new local event bus implementation is added, providing an in-memory, asynchronous event handling mechanism that processes events in registration order. The implementation includes a \Group\ for shared channel management, configurable codecs, and error reporting via a dedicated channel. The addition is accompanied by comprehensive unit and acceptance tests to verify handler registration, event propagation, and load performance.

eventbus/local · high confidence

Introduce namespace support for event stores, repositories, and outboxes

A new \namespace\ package provides wrappers for event stores, repositories, and outboxes that route operations to underlying implementations based on a namespace identifier passed in the context. This allows applications to manage multiple distinct storage backends (e.g., separate databases or collections) by simply setting the namespace in the request context. The package includes \EventStore\, \Repo\, and \Outbox\ implementations that automatically create and cache instances per namespace, with support for pre-registering namespaces and proper resource cleanup.

namespace · high confidence

Memory event store now supports event replacement, renaming, and optional event handling

The in-memory event store implementation has been expanded with maintenance capabilities: a new \Replace\ method allows replacing an existing event by version, and a \RenameEvent\ method enables bulk renaming of event types across all aggregates. Additionally, the store now accepts an optional event handler via \WithEventHandler\, which is invoked for each saved event, and the \Save\ method now validates that all events in a batch belong to the same aggregate and version sequence. These changes are accompanied by dedicated tests for the new maintenance operations and the event handler integration.

eventstore/memory · medium confidence

MongoDB v2 event store adds global position strategy and maintenance operations

The MongoDB v2 event store now supports configuring the global event position strategy via a new \WithGlobalPositionStrategy\ option, allowing users to choose between strict commit-ordering (\GlobalPositionInTX\, the default) and higher-throughput concurrent saves (\GlobalPositionOutsideTX\) which may leave gaps in the global position sequence. The store also introduces maintenance operations including \Replace\, \RenameEvent\, and \Clear\ to manage existing events, and adds a \STRATEGY.md\ document explaining the trade-offs between the two global position strategies.

_eventstore/mongodb\v2 · high confidence

NATS JetStream event bus implementation

Added a new NATS JetStream-based event bus implementation, including the core \EventBus\ struct, handler registration, and integration tests. The implementation supports configurable NATS connection options and stream configurations, allowing users to publish and subscribe to events via NATS JetStream.

eventbus/nats · high confidence

New HTTP handlers for commands, queries, and event streaming

The httputils package now provides ready-to-use HTTP handlers for the Event Horizon framework. A CommandHandler accepts POST requests with JSON bodies to execute commands, a QueryHandler exposes GET endpoints to read from a ReadRepo (supporting both single-item and all-items queries), and an EventBusHandler upgrades HTTP connections to WebSockets for real-time event streaming. These additions allow developers to quickly expose CQRS and event-sourcing capabilities over HTTP.

httputils · high confidence

New MongoDB repository implementation with v2 driver

Added a new MongoDB repository implementation in repo/mongodb that uses the official go.mongodb.org/mongo-driver/v2. The repo supports optional connection checking, custom query execution via FindCustom, and integrates with the existing eventhorizon interfaces. Tests are marked as integration tests and use random database names to avoid collisions.

repo/mongodb · high confidence

New TodoMVC backend handler implementation

The TodoMVC example now includes a new HTTP handler implementation in the backend package. This handler exposes RESTful endpoints for creating, deleting, and managing to-do items, as well as a WebSocket endpoint for real-time event streaming. The implementation integrates the command handler, event bus, and read repository to serve the frontend application.

examples/todomvc/backend/handler · high confidence

New mock implementations for testing

The mocks package now provides ready-to-use test doubles for the Event Horizon framework, including an Aggregate, Command, and Read Model implementations. These mocks allow developers to easily simulate command handling, event data, and entity states in their own test suites without needing to set up complex in-memory stores.

mocks · high confidence

Behavioural changes

Add validation to reject MongoDB collection names containing spaces

The mongoutils package now includes a new CheckCollectionName function that validates collection names. It returns an error if the name is empty or contains a space, preventing the use of collection names that are difficult to read. Corresponding unit tests have been added to verify this validation logic.

mongoutils · medium confidence

Async command handler middleware with error channel

The async middleware for command handling has been refactored to return errors via a dedicated channel rather than blocking the caller. The middleware now spawns a goroutine to handle commands asynchronously, allowing the caller to continue immediately while errors are delivered through a buffered channel. The Error struct now implements the errors.Unwrap method to support error wrapping and inspection, and the test suite has been updated to use synctest for reliable asynchronous testing.

middleware/commandhandler/async · high confidence

Introduce configurable projector event handling with version gap and retry support

The event handler for projectors has been updated to support irregular versioning, allowing projections to handle gaps in event versions without failing. Additionally, a retry-once option has been added to handle race conditions during projection, and entity lookup can now be customized via a lookup function. The handler also includes improved error handling and logging for projector errors.

eventhandler/projector · high confidence

MongoDB event store migrated to mongo-driver v2 with new maintenance operations

The MongoDB event store implementation has been migrated to use the official mongo-driver v2, updating the underlying database client and BSON handling. This change introduces new maintenance capabilities, specifically the ability to replace existing events and rename event types within the store. Additionally, the store now supports configuring a custom collection name and attaching event handlers that can be triggered either after saving events or within the same transaction as the save operation.

eventstore/mongodb · high confidence

Upgrade golangci-lint to v2 and migrate configuration

The project has upgraded the golangci-lint tool to version 2.10.0, requiring a migration of the \.golangci.yml\ configuration file from the v1 format to v2. This change updates the linter's behavior and configuration structure, affecting how code quality checks are performed during development and CI. The configuration file now uses the new v2 schema with updated linter settings and exclusions.

(repo-wide) · high confidence

Test coverage

Acceptance test suite for EventBus implementations; Add acceptance tests for the repository interface; Add outbox acceptance and performance tests; Added acceptance and performance tests for the event store; Added acceptance tests for Event and Command codecs.

Dependencies

Update Go module dependencies and target version

The project's go.mod and go.sum files have been updated to specify Go version 1.25.6 and include a comprehensive set of dependencies. Key direct dependencies include cloud.google.com/go/pubsub/v2, github.com/go-redis/redis/v8, github.com/google/uuid, github.com/nats-io/nats.go, github.com/segmentio/kafka-go, and go.mongodb.org/mongo-driver/v2, alongside numerous indirect dependencies for testing and infrastructure.

(dependencies) · 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

Score

  • CAI 36 → 38 (+2.1)
  • Rubric changed (rubric-2026.08.19 → rubric-2026.09.15) — scores are not directly comparable.

Lenses

  • Code Health 79 → 85 (+6.8)
  • Architecture 100 → 100 (+0.0)
  • Maturity 50 → 50 (-0.2)
  • Readiness 66 → 75 (+9.4)
  • Security 56 → 64 (+8.3)
  • Event Sourcing 10 → 10 (+0.0)

Resolved (65)

  • Aggregate.ApplyEvent (cognitive 24) (examples/todomvc/backend/domains/todo/aggregate.go)
  • Aggregate.ApplyEvent (cyclomatic 17) (examples/todomvc/backend/domains/todo/aggregate.go)
  • Aggregate.HandleCommand (cognitive 45) (examples/todomvc/backend/domains/todo/aggregate.go)
  • Aggregate.HandleCommand (cyclomatic 27) (examples/todomvc/backend/domains/todo/aggregate.go)
  • Coverage not included — suite not readable by the collector
  • Critical CVE: [GHSA redacted] (go.mod)
  • Dependency hygiene not measured — dependency manifest found but not parsed for hygiene
  • Duplicated block (10 lines × 2) (eventbus/acceptance_testing.go)
  • Duplicated block (10 lines × 2) (eventstore/acceptance_testing.go)
  • Duplicated block (10 lines × 2) (eventstore/mongodb/eventstore.go)
  • Duplicated block (10 lines × 4) (eventstore/mongodb/eventstore.go)
  • Duplicated block (11 lines × 2) (codec/bson/event.go)
  • Duplicated block (11 lines × 2) (eventbus/acceptance_testing.go)
  • Duplicated block (12 lines × 2) (eventstore/maintenance_testing.go)
  • Duplicated block (12 lines × 2) (outbox/acceptance_testing.go)
  • Duplicated block (13 lines × 2) (aggregate.go)
  • Duplicated block (13 lines × 2) (eventstore/acceptance_testing.go)
  • Duplicated block (14 lines × 2) (codec/acceptance_testing.go)
  • Duplicated block (14 lines × 2) (eventbus/nats/eventbus.go)
  • Duplicated block (14 lines × 2) (eventstore/memory/eventstore.go)
  • …and 45 more

New (137)

  • Critical CVE: [GHSA redacted] (go.mod)
  • Dependency pinned to a stale untagged commit: github.com/gorhill/cronexpr
  • Documentation: no installation or build instructions (README.md)
  • Documentation: no usage examples (README.md)
  • Duplicated block (10 lines × 2) (eventstore/mongodb/eventstore.go)
  • Duplicated block (10 lines × 2) (tracing/eventbus.go)
  • Duplicated block (10–12 lines × 2) (eventstore/acceptance_testing.go)
  • Duplicated block (11 lines × 2) (eventstore/mongodb/eventstore.go)
  • Duplicated block (11 lines × 3) (namespace/repo.go)
  • Duplicated block (12 lines × 2) (examples/todomvc/backend/main.go)
  • Duplicated block (12 lines × 3) (eventstore/acceptance_testing.go)
  • Duplicated block (13 lines × 2) (eventstore/mongodb/eventmaintenance.go)
  • Duplicated block (13–14 lines × 2) (codec/bson/event.go)
  • Duplicated block (13–15 lines × 2) (tracing/eventstore.go)
  • Duplicated block (14 lines × 3) (eventbus/acceptance_testing.go)
  • Duplicated block (14 lines × 3) (repo/acceptance_testing.go)
  • Duplicated block (15 lines × 2) (aggregate.go)
  • Duplicated block (15 lines × 2) (eventstore/mongodb/eventstore.go)
  • Duplicated block (15 lines × 2) (eventstore/mongodb/eventstore.go)
  • Duplicated block (15 lines × 3) (eventstore/mongodb/eventstore.go)
  • …and 117 more

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

Survey your own repository

looplab/eventhorizon 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 b4c7c132e519d8a73ac3bee014652977fb170c29 — 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-fa71c66cabd8.