rails_simple_event_sourcing 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +125 -5
- data/app/models/concerns/rails_simple_event_sourcing/read_only.rb +4 -2
- data/lib/rails_simple_event_sourcing/command_handler.rb +18 -3
- data/lib/rails_simple_event_sourcing/command_handler_registry.rb +21 -0
- data/lib/rails_simple_event_sourcing/configuration.rb +11 -0
- data/lib/rails_simple_event_sourcing/engine.rb +1 -0
- data/lib/rails_simple_event_sourcing/version.rb +1 -1
- data/lib/rails_simple_event_sourcing.rb +10 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8bae2bfe7377ee1c5b4db35484463b437c75c8c9822791aef21867d749abd3fc
|
|
4
|
+
data.tar.gz: b025d6954826b5a1583d0531da3e816386e407c0b26803e903b1c700ecaab2a1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d3f40d21d78f8d5a978086439521814a9f3903eda901669b88cafc070ab8c01fd7664cb9e5cfefa35c401384c780bc867137e9486e3d3ee37d58ae4ffa3938f6
|
|
7
|
+
data.tar.gz: 3aa5edd3c94f4fe350c5409371430e7e242bdfef76e472bd9f394d011b69b97b295438c3161f2d70c38e7cdb1ca42b4d1f59b335994f175cdbd76f86ebd8b0e1
|
data/README.md
CHANGED
|
@@ -10,11 +10,13 @@ If you need a more comprehensive solution, check out:
|
|
|
10
10
|
- [Features](#features)
|
|
11
11
|
- [Requirements](#requirements)
|
|
12
12
|
- [Installation](#installation)
|
|
13
|
+
- [Configuration](#configuration)
|
|
13
14
|
- [Usage](#usage)
|
|
14
15
|
- [Directory Structure](#directory-structure)
|
|
15
16
|
- [Commands](#commands)
|
|
16
17
|
- [Command Handlers](#command-handlers)
|
|
17
18
|
- [Events](#events)
|
|
19
|
+
- [Registering Command Handlers](#registering-command-handlers)
|
|
18
20
|
- [Controller Integration](#controller-integration)
|
|
19
21
|
- [Update and Delete Operations](#update-and-delete-operations)
|
|
20
22
|
- [Metadata Tracking](#metadata-tracking)
|
|
@@ -22,6 +24,8 @@ If you need a more comprehensive solution, check out:
|
|
|
22
24
|
- [Testing](#testing)
|
|
23
25
|
- [Limitations](#limitations)
|
|
24
26
|
- [Troubleshooting](#troubleshooting)
|
|
27
|
+
- [Command Handler Registry](#command-handler-registry)
|
|
28
|
+
- [CommandHandlerNotFoundError](#commandhandlernotfounderror)
|
|
25
29
|
- [Contributing](#contributing)
|
|
26
30
|
- [License](#license)
|
|
27
31
|
|
|
@@ -31,6 +35,7 @@ If you need a more comprehensive solution, check out:
|
|
|
31
35
|
- **Automatic Aggregate Reconstruction** - Rebuild model state by replaying events
|
|
32
36
|
- **Built-in Metadata Tracking** - Captures request context (IP, user agent, params, etc.)
|
|
33
37
|
- **Read-only Model Protection** - Prevents accidental direct model modifications
|
|
38
|
+
- **Command Handler Registry** - Explicit command-to-handler mapping with fallback to convention
|
|
34
39
|
- **Simple Command Pattern** - Clear command → handler → event flow
|
|
35
40
|
- **PostgreSQL JSONB Storage** - Efficient JSON storage for event payloads and metadata
|
|
36
41
|
- **Minimal Configuration** - Convention over configuration approach
|
|
@@ -71,6 +76,19 @@ rake db:migrate
|
|
|
71
76
|
|
|
72
77
|
This creates the `rails_simple_event_sourcing_events` table that stores your event log.
|
|
73
78
|
|
|
79
|
+
## Configuration
|
|
80
|
+
|
|
81
|
+
You can configure the behavior of the gem using the configuration block:
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
# config/initializers/rails_simple_event_sourcing.rb
|
|
85
|
+
RailsSimpleEventSourcing.configure do |config|
|
|
86
|
+
# When true, falls back to convention-based handler resolution
|
|
87
|
+
# When false, requires explicit registration of all handlers
|
|
88
|
+
config.use_naming_convention_fallback = true
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
74
92
|
## Usage
|
|
75
93
|
|
|
76
94
|
### Architecture Overview
|
|
@@ -144,6 +162,11 @@ Command handlers contain the **business logic** for executing commands. They:
|
|
|
144
162
|
- Handle errors gracefully
|
|
145
163
|
- Return a `RailsSimpleEventSourcing::Result` object
|
|
146
164
|
|
|
165
|
+
**Handler Discovery:**
|
|
166
|
+
Handlers can be discovered in two ways:
|
|
167
|
+
1. **Explicit Registration** (recommended) - Using the `CommandHandlerRegistry` to register handlers
|
|
168
|
+
2. **Convention-based** - Using naming convention mapping (can be disabled via configuration)
|
|
169
|
+
|
|
147
170
|
**Result Object:**
|
|
148
171
|
The `Result` struct has three fields:
|
|
149
172
|
- `success?` - Boolean indicating if the operation succeeded
|
|
@@ -286,6 +309,38 @@ end
|
|
|
286
309
|
- Optional - you can have events without an aggregate (e.g., `UserLoginFailed` for logging only)
|
|
287
310
|
- The corresponding model should include `RailsSimpleEventSourcing::Events` for read-only protection
|
|
288
311
|
|
|
312
|
+
### Registering Command Handlers
|
|
313
|
+
|
|
314
|
+
The recommended approach is to register command handlers explicitly using the registry. This makes the command-to-handler mapping explicit and avoids relying on naming conventions.
|
|
315
|
+
|
|
316
|
+
```ruby
|
|
317
|
+
# config/initializers/rails_simple_event_sourcing.rb
|
|
318
|
+
|
|
319
|
+
RailsSimpleEventSourcing.configure do |config|
|
|
320
|
+
config.use_naming_convention_fallback = false
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
Rails.application.config.after_initialize do
|
|
324
|
+
# Register all command handlers
|
|
325
|
+
RailsSimpleEventSourcing::CommandHandlerRegistry.register(
|
|
326
|
+
Customer::Commands::Create,
|
|
327
|
+
Customer::CommandHandlers::Create
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
RailsSimpleEventSourcing::CommandHandlerRegistry.register(
|
|
331
|
+
Customer::Commands::Update,
|
|
332
|
+
Customer::CommandHandlers::Update
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
RailsSimpleEventSourcing::CommandHandlerRegistry.register(
|
|
336
|
+
Customer::Commands::Delete,
|
|
337
|
+
Customer::CommandHandlers::Delete
|
|
338
|
+
)
|
|
339
|
+
end
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
If you prefer the convention-based approach, you don't need to take any action, as the use_naming_convention_fallback is set to true by default.
|
|
343
|
+
|
|
289
344
|
### Controller Integration
|
|
290
345
|
|
|
291
346
|
Here's how to wire everything together in a controller:
|
|
@@ -525,6 +580,35 @@ end
|
|
|
525
580
|
|
|
526
581
|
## Testing
|
|
527
582
|
|
|
583
|
+
### Setting Up Tests with Command Handler Registry
|
|
584
|
+
|
|
585
|
+
If you're using the command handler registry with `use_naming_convention_fallback = false`, you'll need to register your command handlers in your tests. There are a few approaches to handling this:
|
|
586
|
+
|
|
587
|
+
1. **Create an initializer in the test app:**
|
|
588
|
+
|
|
589
|
+
```ruby
|
|
590
|
+
# test/dummy/config/initializers/rails_simple_event_sourcing.rb
|
|
591
|
+
Rails.application.config.after_initialize do
|
|
592
|
+
RailsSimpleEventSourcing::CommandHandlerRegistry.register(
|
|
593
|
+
Customer::Commands::Create,
|
|
594
|
+
Customer::CommandHandlers::Create
|
|
595
|
+
)
|
|
596
|
+
# Register other handlers...
|
|
597
|
+
end
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
2. **Register in test_helper.rb:**
|
|
601
|
+
|
|
602
|
+
```ruby
|
|
603
|
+
# test/test_helper.rb
|
|
604
|
+
# After requiring the environment
|
|
605
|
+
RailsSimpleEventSourcing::CommandHandlerRegistry.register(
|
|
606
|
+
Customer::Commands::Create,
|
|
607
|
+
Customer::CommandHandlers::Create
|
|
608
|
+
)
|
|
609
|
+
# Register other handlers...
|
|
610
|
+
```
|
|
611
|
+
|
|
528
612
|
### Testing Commands
|
|
529
613
|
|
|
530
614
|
```ruby
|
|
@@ -630,15 +714,51 @@ Be aware of these limitations when using this gem:
|
|
|
630
714
|
|
|
631
715
|
## Troubleshooting
|
|
632
716
|
|
|
717
|
+
### Command Handler Registry
|
|
718
|
+
|
|
719
|
+
The gem provides a registry pattern for explicitly mapping commands to their handlers. This is a more robust alternative to the convention-based mapping.
|
|
720
|
+
|
|
721
|
+
**Configuration:**
|
|
722
|
+
|
|
723
|
+
```ruby
|
|
724
|
+
# config/initializers/rails_simple_event_sourcing.rb
|
|
725
|
+
RailsSimpleEventSourcing.configure do |config|
|
|
726
|
+
# Set to false to disable convention-based mapping
|
|
727
|
+
config.use_naming_convention_fallback = false
|
|
728
|
+
end
|
|
729
|
+
|
|
730
|
+
# Register command handlers
|
|
731
|
+
Rails.application.config.after_initialize do
|
|
732
|
+
RailsSimpleEventSourcing::CommandHandlerRegistry.register(
|
|
733
|
+
Customer::Commands::Create,
|
|
734
|
+
Customer::CommandHandlers::Create
|
|
735
|
+
)
|
|
736
|
+
|
|
737
|
+
RailsSimpleEventSourcing::CommandHandlerRegistry.register(
|
|
738
|
+
Customer::Commands::Update,
|
|
739
|
+
Customer::CommandHandlers::Update
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
RailsSimpleEventSourcing::CommandHandlerRegistry.register(
|
|
743
|
+
Customer::Commands::Delete,
|
|
744
|
+
Customer::CommandHandlers::Delete
|
|
745
|
+
)
|
|
746
|
+
end
|
|
747
|
+
```
|
|
748
|
+
|
|
633
749
|
### CommandHandlerNotFoundError
|
|
634
750
|
|
|
635
|
-
**Error:** `RailsSimpleEventSourcing::CommandHandler::CommandHandlerNotFoundError: Handler Customer::CommandHandlers::Create not found`
|
|
751
|
+
**Error:** `RailsSimpleEventSourcing::CommandHandler::CommandHandlerNotFoundError: Handler Customer::CommandHandlers::Create not found` or `No handler registered for Customer::Commands::Create`
|
|
636
752
|
|
|
637
|
-
**
|
|
753
|
+
**Causes:**
|
|
754
|
+
1. The command handler class doesn't follow the naming convention (when using convention-based mapping)
|
|
755
|
+
2. The command handler hasn't been registered with the registry (when using explicit registration)
|
|
638
756
|
|
|
639
|
-
**
|
|
640
|
-
|
|
641
|
-
-
|
|
757
|
+
**Solutions:**
|
|
758
|
+
1. Ensure your handler namespace matches your command namespace:
|
|
759
|
+
- Command: `Customer::Commands::Create`
|
|
760
|
+
- Handler: `Customer::CommandHandlers::Create` (not `CustomerCommandHandlers::Create`)
|
|
761
|
+
2. Register your command handlers using the registry pattern shown above
|
|
642
762
|
|
|
643
763
|
### undefined method 'events' for Customer
|
|
644
764
|
|
|
@@ -5,12 +5,14 @@ module RailsSimpleEventSourcing
|
|
|
5
5
|
extend ActiveSupport::Concern
|
|
6
6
|
|
|
7
7
|
included do
|
|
8
|
+
attribute :write_access_enabled, :boolean, default: false
|
|
9
|
+
|
|
8
10
|
def readonly?
|
|
9
|
-
super ||
|
|
11
|
+
super || !write_access_enabled
|
|
10
12
|
end
|
|
11
13
|
|
|
12
14
|
def enable_write_access!
|
|
13
|
-
|
|
15
|
+
self.write_access_enabled = true
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
18
|
end
|
|
@@ -17,11 +17,26 @@ module RailsSimpleEventSourcing
|
|
|
17
17
|
private
|
|
18
18
|
|
|
19
19
|
def initialize_command_handler
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
raise CommandHandlerNotFoundError, "Handler #{handler_class_name} not found" unless handler_class
|
|
20
|
+
handler_class = find_handler_class
|
|
21
|
+
raise CommandHandlerNotFoundError, handler_not_found_message unless handler_class
|
|
23
22
|
|
|
24
23
|
handler_class.new(command: @command)
|
|
25
24
|
end
|
|
25
|
+
|
|
26
|
+
def find_handler_class
|
|
27
|
+
handler_class = CommandHandlerRegistry.handler_for(@command.class)
|
|
28
|
+
|
|
29
|
+
if handler_class.nil? && RailsSimpleEventSourcing.config.use_naming_convention_fallback
|
|
30
|
+
handler_class_name = @command.class.to_s.gsub('::Commands::', '::CommandHandlers::')
|
|
31
|
+
handler_class = handler_class_name.safe_constantize
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
handler_class
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def handler_not_found_message
|
|
38
|
+
"No handler registered for #{@command.class}. " \
|
|
39
|
+
"Register one with CommandHandlerRegistry.register(#{@command.class}, YourHandlerClass)"
|
|
40
|
+
end
|
|
26
41
|
end
|
|
27
42
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RailsSimpleEventSourcing
|
|
4
|
+
class CommandHandlerRegistry
|
|
5
|
+
def self.register(command_class, handler_class)
|
|
6
|
+
registry[command_class] = handler_class
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.handler_for(command_class)
|
|
10
|
+
registry[command_class]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.registry
|
|
14
|
+
@registry ||= {}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.clear
|
|
18
|
+
@registry = {}
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -2,7 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
require 'rails_simple_event_sourcing/version'
|
|
4
4
|
require 'rails_simple_event_sourcing/engine'
|
|
5
|
+
require 'rails_simple_event_sourcing/configuration'
|
|
6
|
+
require 'rails_simple_event_sourcing/command_handler_registry'
|
|
5
7
|
|
|
6
8
|
module RailsSimpleEventSourcing
|
|
7
|
-
|
|
9
|
+
def self.configure
|
|
10
|
+
yield(config) if block_given?
|
|
11
|
+
config
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.config
|
|
15
|
+
@config ||= Configuration.new
|
|
16
|
+
end
|
|
8
17
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails_simple_event_sourcing
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Damian Baćkowski
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-01-
|
|
11
|
+
date: 2026-01-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: pg
|
|
@@ -91,8 +91,10 @@ files:
|
|
|
91
91
|
- lib/rails_simple_event_sourcing/aggregate_repository.rb
|
|
92
92
|
- lib/rails_simple_event_sourcing/apply_with_returning_aggregate.rb
|
|
93
93
|
- lib/rails_simple_event_sourcing/command_handler.rb
|
|
94
|
+
- lib/rails_simple_event_sourcing/command_handler_registry.rb
|
|
94
95
|
- lib/rails_simple_event_sourcing/command_handlers/base.rb
|
|
95
96
|
- lib/rails_simple_event_sourcing/commands/base.rb
|
|
97
|
+
- lib/rails_simple_event_sourcing/configuration.rb
|
|
96
98
|
- lib/rails_simple_event_sourcing/engine.rb
|
|
97
99
|
- lib/rails_simple_event_sourcing/event_applicator.rb
|
|
98
100
|
- lib/rails_simple_event_sourcing/event_player.rb
|