simple_event_sourcing 0.3.0 → 0.4.0
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/.gitignore +0 -0
- data/.rspec +0 -0
- data/.simplecov +0 -0
- data/.travis.yml +0 -0
- data/CODE_OF_CONDUCT.md +0 -0
- data/Dockerfile +0 -0
- data/Gemfile +0 -0
- data/Gemfile.lock +1 -1
- data/LICENSE.txt +0 -0
- data/README.md +5 -6
- data/Rakefile +0 -0
- data/docker-compose.yml +0 -0
- data/example/employee.rb +6 -6
- data/example/main.rb +7 -2
- data/lib/simple_event_sourcing/aggregate_root/base.rb +75 -0
- data/lib/simple_event_sourcing/aggregate_root/history.rb +21 -0
- data/lib/simple_event_sourcing/aggregate_root/id.rb +54 -0
- data/lib/simple_event_sourcing/events/events.rb +35 -39
- data/lib/simple_event_sourcing/version.rb +1 -1
- data/lib/simple_event_sourcing.rb +4 -6
- data/run.sh +0 -0
- data/simple_event_sourcing.gemspec +0 -0
- metadata +6 -5
- data/lib/simple_event_sourcing/aggregate_root/aggregate_root.rb +0 -62
- data/lib/simple_event_sourcing/aggregate_root/self_applier.rb +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0611f1925717d9e7186c6afd3cb564ed9224969bffd3b681e20a503f6326749b
|
4
|
+
data.tar.gz: 8e9c67af00a6782f5097d4aad1892925bd9c20245d3c78e0ebebe28db44cdffc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f162f575634be6a4a5aaaa935afa736c7e9059507bd8b054489ff07e5d18fdd8900124287f6eedb6423c86bfd1b5fd27de41dbb28caf5de72a46834248f91f7
|
7
|
+
data.tar.gz: 0a7c0bcb4a586321420781093f3630d88f882d37b1d8d3b257c44f067838f1c3af55322b73e0faadd6d261afbde17d56d92ad7a6845b82ff1059beda96b86b3a
|
data/.gitignore
CHANGED
File without changes
|
data/.rspec
CHANGED
File without changes
|
data/.simplecov
CHANGED
File without changes
|
data/.travis.yml
CHANGED
File without changes
|
data/CODE_OF_CONDUCT.md
CHANGED
File without changes
|
data/Dockerfile
CHANGED
File without changes
|
data/Gemfile
CHANGED
File without changes
|
data/Gemfile.lock
CHANGED
data/LICENSE.txt
CHANGED
File without changes
|
data/README.md
CHANGED
@@ -9,9 +9,10 @@ This gem provides a simple way for add events sourcing related behaviour to your
|
|
9
9
|
Base classes
|
10
10
|
|
11
11
|
- AggregateRoot
|
12
|
+
- UUID Id
|
12
13
|
- Event
|
13
14
|
- EventStream
|
14
|
-
-
|
15
|
+
- EventDispatcher
|
15
16
|
|
16
17
|
## Installation
|
17
18
|
|
@@ -63,7 +64,7 @@ class Employee
|
|
63
64
|
|
64
65
|
def save
|
65
66
|
# Persist the entity
|
66
|
-
publish_events { |event| SimpleEventSourcing::
|
67
|
+
publish_events { |event| SimpleEventSourcing::Events::EventDispatcher.publish(event) }
|
67
68
|
end
|
68
69
|
|
69
70
|
end
|
@@ -78,7 +79,7 @@ First, you must add behaviour including the AggregateRoot module
|
|
78
79
|
You must create your own domain events and a event stream
|
79
80
|
|
80
81
|
```ruby
|
81
|
-
class EmployeeStreamEvents < SimpleEventSourcing::
|
82
|
+
class EmployeeStreamEvents < SimpleEventSourcing::AggregateRoot::History
|
82
83
|
def get_aggregate_class
|
83
84
|
Employee
|
84
85
|
end
|
@@ -109,14 +110,12 @@ on SalaryHasChangedEvent do |event|
|
|
109
110
|
end
|
110
111
|
```
|
111
112
|
|
112
|
-
|
113
|
-
|
114
113
|
Once you persist the entity you must publish all recorded events.
|
115
114
|
|
116
115
|
```ruby
|
117
116
|
def save
|
118
117
|
# Persist the entity
|
119
|
-
publish_events { |event| SimpleEventSourcing::
|
118
|
+
publish_events { |event| SimpleEventSourcing::EventDispatcher.publish(event) }
|
120
119
|
end
|
121
120
|
```
|
122
121
|
|
data/Rakefile
CHANGED
File without changes
|
data/docker-compose.yml
CHANGED
File without changes
|
data/example/employee.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
require_relative '../lib/simple_event_sourcing'
|
2
2
|
|
3
|
-
class EmployeeStreamEvents < SimpleEventSourcing::
|
3
|
+
class EmployeeStreamEvents < SimpleEventSourcing::AggregateRoot::History
|
4
4
|
def get_aggregate_class
|
5
5
|
Employee
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
9
|
|
10
|
-
class NewEmployeeIsHiredEvent < SimpleEventSourcing::Event
|
10
|
+
class NewEmployeeIsHiredEvent < SimpleEventSourcing::Events::Event
|
11
11
|
attr_reader :name, :title,:salary
|
12
12
|
|
13
13
|
def initialize(aggregate_id, name, title, salary)
|
@@ -19,7 +19,7 @@ class NewEmployeeIsHiredEvent < SimpleEventSourcing::Event
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
class SalaryHasChangedEvent < SimpleEventSourcing::Event
|
22
|
+
class SalaryHasChangedEvent < SimpleEventSourcing::Events::Event
|
23
23
|
attr_reader :aggregate_id, :new_salary
|
24
24
|
|
25
25
|
def initialize(aggregate_id, new_salary)
|
@@ -29,7 +29,7 @@ class SalaryHasChangedEvent < SimpleEventSourcing::Event
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
class CongratulateEmployeeSubscriber < SimpleEventSourcing::EventSubscriber
|
32
|
+
class CongratulateEmployeeSubscriber < SimpleEventSourcing::Events::EventSubscriber
|
33
33
|
|
34
34
|
def is_subscribet_to?(event)
|
35
35
|
event.class == SalaryHasChangedEvent
|
@@ -44,7 +44,7 @@ end
|
|
44
44
|
|
45
45
|
class Employee
|
46
46
|
|
47
|
-
include SimpleEventSourcing::AggregateRoot
|
47
|
+
include SimpleEventSourcing::AggregateRoot::Base
|
48
48
|
|
49
49
|
attr_reader :name, :title, :salary
|
50
50
|
|
@@ -71,7 +71,7 @@ class Employee
|
|
71
71
|
|
72
72
|
def save
|
73
73
|
# Persist the entity
|
74
|
-
publish_events { |event| SimpleEventSourcing::
|
74
|
+
publish_events { |event| SimpleEventSourcing::Events::EventDispatcher.publish(event) }
|
75
75
|
end
|
76
76
|
|
77
77
|
end
|
data/example/main.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
|
+
|
2
|
+
unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)+'/../lib'))
|
3
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)+'/../lib'))
|
4
|
+
end
|
5
|
+
|
1
6
|
require 'simple_event_sourcing'
|
2
|
-
require_relative 'employee'
|
7
|
+
require_relative './employee'
|
3
8
|
|
4
|
-
SimpleEventSourcing::
|
9
|
+
SimpleEventSourcing::Events::EventDispatcher.add_subscriber(CongratulateEmployeeSubscriber.new)
|
5
10
|
|
6
11
|
fred = Employee.new(name: "Fred Flintstone", title: "Crane Operator", salary: 30000.0)
|
7
12
|
fred.salary=35000.0
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module SimpleEventSourcing
|
2
|
+
module AggregateRoot
|
3
|
+
|
4
|
+
module Base
|
5
|
+
|
6
|
+
attr_accessor :aggregate_id
|
7
|
+
attr_reader :events
|
8
|
+
|
9
|
+
def initialize(_args = nil)
|
10
|
+
@events = []
|
11
|
+
@aggregate_id ||= SimpleEventSourcing::Id::UUIDId.generate
|
12
|
+
end
|
13
|
+
|
14
|
+
def have_changed?
|
15
|
+
(@events.count > 0)
|
16
|
+
end
|
17
|
+
|
18
|
+
def publish_events
|
19
|
+
@events.each do |event|
|
20
|
+
yield(event)
|
21
|
+
end
|
22
|
+
clear_events
|
23
|
+
end
|
24
|
+
|
25
|
+
def apply_record_event(event)
|
26
|
+
handle_message(event)
|
27
|
+
record_event event
|
28
|
+
end
|
29
|
+
|
30
|
+
def handle_message(message)
|
31
|
+
handler = self.class.message_mapping[message.class]
|
32
|
+
self.instance_exec(message, &handler) if handler
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.included(o)
|
36
|
+
o.extend(ClassMethods)
|
37
|
+
end
|
38
|
+
|
39
|
+
module ClassMethods
|
40
|
+
def create_from_agrregate_id(id)
|
41
|
+
aggregate = new
|
42
|
+
aggregate.aggregate_id = id
|
43
|
+
aggregate
|
44
|
+
end
|
45
|
+
def on(*message_classes, &block)
|
46
|
+
message_classes.each { |message_class| message_mapping[message_class] = block }
|
47
|
+
end
|
48
|
+
|
49
|
+
def message_mapping
|
50
|
+
@message_mapping ||= {}
|
51
|
+
end
|
52
|
+
|
53
|
+
def handles_message?(message)
|
54
|
+
message_mapping.keys.include? message.class
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def record_event(event)
|
61
|
+
@events << event
|
62
|
+
end
|
63
|
+
|
64
|
+
def clear_events
|
65
|
+
@events = []
|
66
|
+
end
|
67
|
+
|
68
|
+
def apply_event(event)
|
69
|
+
handle_message(event)
|
70
|
+
#method = 'apply_' + event.class.name.snakecase
|
71
|
+
#send(method, event)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SimpleEventSourcing
|
2
|
+
module AggregateRoot
|
3
|
+
class History < Array
|
4
|
+
attr_reader :aggregate_id
|
5
|
+
|
6
|
+
def initialize(aggregate_id)
|
7
|
+
@aggregate_id = aggregate_id
|
8
|
+
end
|
9
|
+
|
10
|
+
def get_aggregate
|
11
|
+
aggregate = get_aggregate_class.create_from_agrregate_id @aggregate_id
|
12
|
+
each { |event| aggregate.apply_record_event event }
|
13
|
+
aggregate
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_aggregate_class
|
17
|
+
raise StandardError('Method must be implemented')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module SimpleEventSourcing
|
4
|
+
|
5
|
+
module Id
|
6
|
+
|
7
|
+
class BaseId
|
8
|
+
def initialize(value)
|
9
|
+
@value = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def id
|
13
|
+
@value
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
@value
|
18
|
+
end
|
19
|
+
|
20
|
+
def ==(other_id)
|
21
|
+
self.class == other_id.class && @value == other_id.id
|
22
|
+
end
|
23
|
+
|
24
|
+
alias eql? ==
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
class UUIDId < BaseId
|
29
|
+
def initialize(value)
|
30
|
+
raise UUIDValidationError unless valid? value
|
31
|
+
super(value)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.generate
|
35
|
+
new SecureRandom.uuid
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def valid?(uuid)
|
41
|
+
uuid_regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
|
42
|
+
return true if uuid_regex =~ uuid.to_s.downcase
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class UUIDValidationError < StandardError
|
48
|
+
def initialize(msg="Value is not a valid UUID")
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -1,60 +1,56 @@
|
|
1
1
|
module SimpleEventSourcing
|
2
|
+
module Events
|
2
3
|
|
3
|
-
|
4
|
-
|
4
|
+
class Event
|
5
|
+
attr_reader :occured_on
|
5
6
|
|
6
|
-
|
7
|
-
|
7
|
+
def initialize
|
8
|
+
@occured_on ||= Time.new
|
9
|
+
end
|
8
10
|
end
|
9
|
-
end
|
10
11
|
|
11
|
-
|
12
|
-
attr_reader :aggregate_id
|
12
|
+
class EventSubscriber
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
def is_subscribet_to?(event)
|
15
|
+
raise StandardError "Method not implemented"
|
16
|
+
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
aggregate
|
22
|
-
end
|
18
|
+
def check(event)
|
19
|
+
raise EventIsNotAllowedToBeHandled unless is_subscribet_to? event
|
20
|
+
end
|
23
21
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
22
|
+
def handle_event(event)
|
23
|
+
raise StandardError "Method not implemented"
|
24
|
+
end
|
28
25
|
|
29
|
-
|
26
|
+
def handle(event)
|
27
|
+
check event
|
28
|
+
handle_event event
|
29
|
+
end
|
30
30
|
|
31
|
-
def is_subscribet_to?(event)
|
32
|
-
raise StandardError "Method not implemented"
|
33
31
|
end
|
34
32
|
|
35
|
-
|
36
|
-
raise StandardError "Method not implemented"
|
37
|
-
end
|
38
|
-
end
|
33
|
+
class EventIsNotAllowedToBeHandled < StandardError; end
|
39
34
|
|
35
|
+
module EventDispatcher
|
36
|
+
@@subscribers = []
|
40
37
|
|
41
|
-
|
42
|
-
|
38
|
+
def self.add_subscriber(subscriber)
|
39
|
+
@@subscribers << subscriber
|
40
|
+
end
|
43
41
|
|
44
|
-
|
45
|
-
|
46
|
-
|
42
|
+
def self.delete_subscriber(subscriber)
|
43
|
+
@@subscribers.delete(subscriber)
|
44
|
+
end
|
47
45
|
|
48
|
-
|
49
|
-
|
50
|
-
|
46
|
+
def self.get_subscribers
|
47
|
+
@@subscribers
|
48
|
+
end
|
51
49
|
|
52
|
-
|
53
|
-
|
50
|
+
def self.publish(event)
|
51
|
+
@@subscribers.each { |subscriber| subscriber.handle(event) if subscriber.is_subscribet_to? event }
|
52
|
+
end
|
54
53
|
end
|
55
54
|
|
56
|
-
def self.publish(event)
|
57
|
-
@@subscribers.each { |subscriber| subscriber.handle(event) if subscriber.is_subscribet_to? event }
|
58
|
-
end
|
59
55
|
end
|
60
56
|
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
require 'simple_event_sourcing/version'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
end
|
3
|
+
require 'simple_event_sourcing/aggregate_root/id'
|
4
|
+
require 'simple_event_sourcing/aggregate_root/base'
|
5
|
+
require 'simple_event_sourcing/aggregate_root/history'
|
6
|
+
require 'simple_event_sourcing/events/events'
|
data/run.sh
CHANGED
File without changes
|
File without changes
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_event_sourcing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Manuel López Torrent
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-04-
|
11
|
+
date: 2018-04-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -105,8 +105,9 @@ files:
|
|
105
105
|
- example/employee.rb
|
106
106
|
- example/main.rb
|
107
107
|
- lib/simple_event_sourcing.rb
|
108
|
-
- lib/simple_event_sourcing/aggregate_root/
|
109
|
-
- lib/simple_event_sourcing/aggregate_root/
|
108
|
+
- lib/simple_event_sourcing/aggregate_root/base.rb
|
109
|
+
- lib/simple_event_sourcing/aggregate_root/history.rb
|
110
|
+
- lib/simple_event_sourcing/aggregate_root/id.rb
|
110
111
|
- lib/simple_event_sourcing/events/events.rb
|
111
112
|
- lib/simple_event_sourcing/version.rb
|
112
113
|
- run.sh
|
@@ -131,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
132
|
version: '0'
|
132
133
|
requirements: []
|
133
134
|
rubyforge_project:
|
134
|
-
rubygems_version: 2.7.
|
135
|
+
rubygems_version: 2.7.6
|
135
136
|
signing_key:
|
136
137
|
specification_version: 4
|
137
138
|
summary: This gem provides a simple way for add events sourcing related behaviour
|
@@ -1,62 +0,0 @@
|
|
1
|
-
require 'securerandom'
|
2
|
-
require 'facets'
|
3
|
-
|
4
|
-
require_relative 'self_applier'
|
5
|
-
|
6
|
-
module SimpleEventSourcing
|
7
|
-
module AggregateRoot
|
8
|
-
|
9
|
-
attr_accessor :aggregate_id
|
10
|
-
attr_reader :events
|
11
|
-
|
12
|
-
def initialize(_args = nil)
|
13
|
-
@events = []
|
14
|
-
@aggregate_id ||= SecureRandom.uuid
|
15
|
-
end
|
16
|
-
|
17
|
-
def have_changed?
|
18
|
-
(@events.count > 0)
|
19
|
-
end
|
20
|
-
|
21
|
-
def publish_events
|
22
|
-
@events.each do |event|
|
23
|
-
yield(event)
|
24
|
-
end
|
25
|
-
clear_events
|
26
|
-
end
|
27
|
-
|
28
|
-
def apply_record_event(event)
|
29
|
-
handle_message(event)
|
30
|
-
record_event event
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.included(o)
|
34
|
-
o.extend(ClassMethods)
|
35
|
-
end
|
36
|
-
|
37
|
-
module ClassMethods
|
38
|
-
def create_from_agrregate_id(id)
|
39
|
-
aggregate = new
|
40
|
-
aggregate.aggregate_id = id
|
41
|
-
aggregate
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
def record_event(event)
|
48
|
-
@events << event
|
49
|
-
end
|
50
|
-
|
51
|
-
def clear_events
|
52
|
-
@events = []
|
53
|
-
end
|
54
|
-
|
55
|
-
def apply_event(event)
|
56
|
-
handle_message(event)
|
57
|
-
#method = 'apply_' + event.class.name.snakecase
|
58
|
-
#send(method, event)
|
59
|
-
end
|
60
|
-
|
61
|
-
end
|
62
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
module SimpleEventSourcing
|
2
|
-
module AggregateRoot
|
3
|
-
##
|
4
|
-
# Creates ability to use DSL like:
|
5
|
-
# class MyEventHandler < Sequent::Core::BaseEventHandler
|
6
|
-
#
|
7
|
-
# on MyEvent do |event|
|
8
|
-
# do_some_logic
|
9
|
-
# end
|
10
|
-
# end
|
11
|
-
#
|
12
|
-
# You typically do not need to include this module in your classes. If you extend from
|
13
|
-
# Sequent::Core::AggregateRoot, Sequent::Core::Projector, Sequent::Core::Workflow or Sequent::Core::BaseCommandHandler
|
14
|
-
# you will get this functionality for free.
|
15
|
-
#
|
16
|
-
|
17
|
-
module ClassMethods
|
18
|
-
def on(*message_classes, &block)
|
19
|
-
message_classes.each { |message_class| message_mapping[message_class] = block }
|
20
|
-
end
|
21
|
-
|
22
|
-
def message_mapping
|
23
|
-
@message_mapping ||= {}
|
24
|
-
end
|
25
|
-
|
26
|
-
def handles_message?(message)
|
27
|
-
message_mapping.keys.include? message.class
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.included(host_class)
|
32
|
-
host_class.extend(ClassMethods)
|
33
|
-
end
|
34
|
-
|
35
|
-
def handle_message(message)
|
36
|
-
handler = self.class.message_mapping[message.class]
|
37
|
-
self.instance_exec(message, &handler) if handler
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|