Rails Event Store (RES)

repository·master·Indexed 23 days ago

https://github.com/railseventstore/rails_event_store

A comprehensive library for managing events in Rails applications, supporting patterns such as Publish-Subscribe, Event Sourcing, and Read Models. The ecosystem includes the ruby_event_store-cli for event inspection, ruby_event_store-mcp for AI-driven tool integration, ruby_event_store-outbox for transactional background job enqueuing, and integrations for Flipper and Minitest.

Tokens
59.6K
Snippets
173
Records
314
Agent score
81%

What's inside Rails Event Store

  1. Overview of Rails Event Store (RES)

    master

    Rails Event Store (RES) is a library designed for publishing, consuming, storing, and retrieving events. It is intended to support event-driven architectures within Rails applications.

    Key use cases include:

    • Publish-Subscribe bus: Using RES as a central bus for event distribution.
    • Decoupling: Separating core business logic from external concerns (e.g., in Hexagonal architectures).
    • Alternative to ActiveRecord Callbacks: Moving away from side-effects in models toward explicit domain events.
    • Communication Layer: Facilitating interaction between loosely coupled components.
    • Synchronous/Asynchronous Reaction: Reacting to events either immediately or via background processing.
    • Side-effect Extraction: Moving logic like notifications or metrics out of controllers/services and into dedicated event handlers.
    • Audit Logs: Building a reliable history of system changes.
    • Read Models: Creating specialized views of data for optimized querying.
    • Event Sourcing: Implementing full event-sourcing patterns.
  2. Overview of RailsEventStore gems

    master

    The RailsEventStore project consists of several core gems and various contributed gems that extend its functionality.

    Core Gems

    • rails_event_store: The primary gem for Rails integration.
    • ruby_event_store: The core Ruby implementation.
    • ruby_event_store-active_record: ActiveRecord adapter for ruby_event_store.
    • ruby_event_store-browser: Browser-based support for ruby_event_store.
    • ruby_event_store-rspec: RSpec testing support.
    • aggregate_root: Provides aggregate root functionality.

    Contributed Gems

    There are many community-contributed gems for specific use cases, including:

    • ruby_event_store-outbox: Outbox pattern support.
    • ruby_event_store-cli: Command line interface.
    • ruby_event_store-process_manager: Process management.
    • ruby_event_store-sequel: Sequel adapter.
    • ruby_event_store-sidekiq_scheduler: Sidekiq integration.
    • ruby_event_store-profiler: Profiling tools.
  3. Overview of RailsEventStore

    master

    RailsEventStore is a Rails wrapper for RubyEventStore that provides enhanced functionality specifically for Rails applications. It includes several 'batteries included' features to streamline event-driven development in a Rails environment:

    • Asynchronous Event Dispatch: Uses ActiveJob to perform after-commit event dispatching.
    • Instrumentation: Built-in ActiveSupport::Notifications support for monitoring and debugging.
    • Request Metadata Enrichment: Automatically enriches events with relevant request metadata.
    • Bounded Context Scaffolding: Provides an opinionated directory structure generator to help organize code by bounded contexts.
  4. Available transformations in RubyEventStore::Transformations

    master

    The RubyEventStore::Transformations gem provides additional transformation layers for use with PipelineMapper. It currently includes two specific transformations:

    1. IdentityMap: Loads records into previously-known event instances while retaining their object_id. This is primarily useful for testing scenarios where you need to assert against specific object identities or inspect output that relies on object stability.
    2. WithIndifferentAccess: Ensures data and metadata are handled with indifferent access. It deep-symbolizes data and metadata before writing to the store and decorates them with HashWithIndifferentAccess upon reading. This is highly recommended when working with JSON-backed data and metadata to avoid symbol/string key mismatches.
  5. Use RubyEventStore::ActiveRecord for persistent event storage

    master

    RubyEventStore::ActiveRecord provides a persistent event repository implementation for RubyEventStore using ActiveRecord. It is designed to provide log-like properties for event streams on top of SQL database engines by using linearized writes.

    It includes the necessary database schema and migrations and is compatible with the following database engines:

    • PostgreSQL
    • MySQL
    • SQLite
  6. What is Bi-Temporal EventSourcing?

    master

    Bi-Temporal EventSourcing is an approach used when knowing when an event was recorded (the timestamp) is insufficient, and you also need to know when the event was actually valid in the real world.

    This is useful for handling corrections without mutating immutable event data. Instead of modifying an existing event to fix an error, you publish a new 'Retroactive' event that includes a valid_at property in its metadata. This new event describes the correct value and specifies the time period it applies to.

  7. Extend the Browser with Extensions

    master

    Extensions allow you to plug new pages and links into the Browser UI. An extension is an object passed to extensions: [...] in the configuration. There is no base class; the Browser discovers functionality using respond_to? on several optional hooks.

    Available Hooks

    • register_routes(router, context): Add custom routes. The router allows adding routes, and the context provides rendering and event store access.
    • stream_links(stream_name, urls): Add links displayed under the header of a stream page.
    • event_links(event, urls): Add links displayed on an event page.
    • nav_links(urls): Add links to the top navigation bar.
    • views_root: Specify the directory for extension templates.
    • assets_root: Specify the directory for extension assets (CSS/JS). Files in this directory are automatically served and linked.

    The Context Object

    In register_routes, the context provides:

    • context.render(template, urls:, title: nil, **locals): Renders a template wrapped in the browser layout.
    • context.not_found(urls, message: "Page not found"): Renders a styled 404 page.
    • context.event_store: The event store client.

    Building URLs

    Always use urls.app_url_for to build paths to ensure compatibility with different mount points:

    urls.app_url_for("my_extension", "param_value")
    run RubyEventStore::Browser::App.for(
      event_store_locator: -> { event_store },
      extensions: [DeploymentsExtension.new],
    )
  8. How upcasting works for evolving events

    master

    Upcasting is a non-destructive strategy for evolving events where transformations occur at read time without modifying the underlying storage. When an event is loaded from the event store, a mapper applies a chain of upcast functions to convert older event representations into the current shape on the fly.

    Use upcasting when:

    • You are renaming an event class and want old records to be read as the new class.
    • You are evolving the data schema of an event and want to express the evolution as a versioned chain.

    Important Constraint: Each upcast function must change the event_type. If an upcast returns a record with the same event_type as the input, the system will raise a RubyEventStore::InvalidUpcast error.

    upcast_map = {
      "OrderPlaced" => lambda do |record|
        RubyEventStore::Record.new(
          event_id:   record.event_id,
          metadata:   record.metadata,
          timestamp:  record.timestamp,
          valid_at:   record.valid_at,
          event_type: record.event_type,
          data:       record.data.merge(currency: "USD"),
        )
      end
    }