ruby_cqrs 0.2.0 → 0.2.1
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 +2 -2
- data/lib/ruby_cqrs/domain/aggregate.rb +3 -3
- data/lib/ruby_cqrs/domain/aggregate_repository.rb +5 -5
- data/lib/ruby_cqrs/version.rb +1 -1
- data/spec/feature/basic_usage_spec.rb +38 -29
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5f70e7b70c21215981195e374595fce852f0d94
|
4
|
+
data.tar.gz: 5312d18d87f660591396ca99cc120edb5e63bfb1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95dbd4d1ebc007450eba00a2f7bc4e8cf2dd47b439299bed29ee1bea5138b09e3058a68528f214345ae7cfdca02df8b78dead78037e810e5f6dcc5fb1a694a41
|
7
|
+
data.tar.gz: 0f4ea2e1eea528a598fa1e34a760994a51276e79c929a7078767ae7e5a4c31afffe5fad7c732ce1c9988618dd5a78a98e029a4ea955ced1c6a30550902dbea37
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
[](https://travis-ci.org/iravench/ruby_cqrs) [](https://codeclimate.com/github/iravench/ruby_cqrs) [](https://codeclimate.com/github/iravench/ruby_cqrs)
|
1
|
+
[](http://badge.fury.io/rb/ruby_cqrs) [](https://travis-ci.org/iravench/ruby_cqrs) [](https://codeclimate.com/github/iravench/ruby_cqrs) [](https://codeclimate.com/github/iravench/ruby_cqrs)
|
2
2
|
|
3
3
|
# RubyCqrs
|
4
4
|
|
5
|
-
A Ruby implementation of CQRS, using event sourcing.
|
5
|
+
A Ruby implementation of CQRS, using event sourcing. go wiki cqrs for more information.
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
@@ -23,14 +23,14 @@ module RubyCqrs
|
|
23
23
|
def load_from state
|
24
24
|
sorted_events = state[:events].sort { |x, y| x.version <=> y.version }
|
25
25
|
@aggregate_id = state[:aggregate_id]
|
26
|
-
|
26
|
+
try_apply_snapshot_in state
|
27
27
|
sorted_events.each do |event|
|
28
28
|
apply(event)
|
29
29
|
@source_version += 1
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
def
|
33
|
+
def try_apply_snapshot_in state
|
34
34
|
if state.has_key? :snapshot and self.is_a? Snapshotable
|
35
35
|
self.send :apply_snapshot, state[:snapshot][:state]
|
36
36
|
@version = state[:snapshot][:version]
|
@@ -42,7 +42,7 @@ module RubyCqrs
|
|
42
42
|
def get_changes
|
43
43
|
return nil unless @pending_events.size > 0
|
44
44
|
changes = {
|
45
|
-
:events => @pending_events,
|
45
|
+
:events => @pending_events.dup,
|
46
46
|
:aggregate_id => @aggregate_id,
|
47
47
|
:aggregate_type => self.class.name,
|
48
48
|
:expecting_source_version => @source_version,
|
@@ -37,7 +37,7 @@ module RubyCqrs
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def create_instance_from state
|
40
|
-
|
40
|
+
try_decode_serialized_from state
|
41
41
|
instance = state[:aggregate_type].constantize.new
|
42
42
|
instance.send(:load_from, state)
|
43
43
|
instance
|
@@ -68,17 +68,17 @@ module RubyCqrs
|
|
68
68
|
raise ArgumentError unless aggregate.is_a? Aggregate
|
69
69
|
aggregate_change = aggregate.send(:get_changes)
|
70
70
|
next if aggregate_change.nil?
|
71
|
-
|
71
|
+
try_encode_serializable_in aggregate_change
|
72
72
|
product << aggregate_change
|
73
73
|
end
|
74
74
|
to_return
|
75
75
|
end
|
76
76
|
|
77
|
-
def
|
77
|
+
def try_decode_serialized_from state
|
78
78
|
state[:snapshot] = decode_snapshot_state_from state[:snapshot]\
|
79
79
|
if state.has_key? :snapshot
|
80
80
|
|
81
|
-
state[:events].map
|
81
|
+
state[:events] = state[:events].map { |event_record| decode_event_from event_record }\
|
82
82
|
if state[:events].size > 0
|
83
83
|
end
|
84
84
|
|
@@ -94,7 +94,7 @@ module RubyCqrs
|
|
94
94
|
decoded_event
|
95
95
|
end
|
96
96
|
|
97
|
-
def
|
97
|
+
def try_encode_serializable_in change
|
98
98
|
if change.has_key? :snapshot
|
99
99
|
encoded_snapshot = encode_data_from change[:snapshot][:state]
|
100
100
|
change[:snapshot] = { :state_type => change[:snapshot][:state_type],
|
data/lib/ruby_cqrs/version.rb
CHANGED
@@ -1,25 +1,29 @@
|
|
1
1
|
require_relative('../spec_helper.rb')
|
2
2
|
|
3
|
+
# after installing the gem, require its features
|
4
|
+
require('ruby_cqrs')
|
3
5
|
# define your domain object
|
4
6
|
class Customer
|
5
|
-
# mark
|
7
|
+
# mark your domain object as an aggregate
|
6
8
|
include RubyCqrs::Domain::Aggregate
|
7
|
-
# makr as snapshotable
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
9
|
+
# optionally, also makr as snapshotable in order to avoid heavy events reading pressure
|
10
|
+
# if your aggregate won't be generating much events, you can just ignore this
|
11
|
+
# the default setting is thant when over 30 events get raised after the last snapshot taken,
|
12
|
+
# a new snapshot will be generated upon calling aggregate_repository.save
|
13
|
+
# change this default value by defining SNAPSHOT_THRESHOLD = 20(or other number)
|
11
14
|
include RubyCqrs::Domain::Snapshotable
|
12
15
|
|
13
16
|
attr_reader :name, :credit
|
14
17
|
|
15
18
|
# unfortunately, you should not try to define your own initialize method
|
16
|
-
# at the time being, it could potentially cause error
|
17
|
-
#
|
19
|
+
# at the time being, it could potentially cause error with aggregate_repository
|
20
|
+
# when it tries to instantiate an aggregate back to live.
|
21
|
+
# Still looking for better way to do this.
|
18
22
|
|
19
|
-
# define
|
23
|
+
# define domain behaviors
|
20
24
|
def create_profile name, credit
|
21
|
-
# again, this
|
22
|
-
# it should
|
25
|
+
# again, this one method kinda serve as your normal initialize method here,
|
26
|
+
# which means it should be called once only
|
23
27
|
raise RuntimeError.new('the profile has already been created') unless @name.nil?
|
24
28
|
raise AgumentError if name.nil?
|
25
29
|
raise AgumentError if credit < 100
|
@@ -36,9 +40,8 @@ class Customer
|
|
36
40
|
end
|
37
41
|
|
38
42
|
private
|
39
|
-
# when an event
|
40
|
-
#
|
41
|
-
# manage the domain object's internal state here
|
43
|
+
# when an event gets raised or replayed, these on_ methods will be called automatically,
|
44
|
+
# with the events's detail information, you can manage the aggregate's internal state
|
42
45
|
def on_customer_created customer_created
|
43
46
|
@name = customer_created.name
|
44
47
|
@credit = customer_created.credit
|
@@ -48,9 +51,9 @@ private
|
|
48
51
|
@credit -= product_ordered.cost
|
49
52
|
end
|
50
53
|
|
51
|
-
# when
|
52
|
-
#
|
53
|
-
#
|
54
|
+
# when an aggregate is marked as snapshotable,
|
55
|
+
# following two methods must be implemented in order to record and restore
|
56
|
+
# the aggregate's vital state respectively,
|
54
57
|
def take_a_snapshot
|
55
58
|
CustomerSnapshot.new(:name => @name, :credit => @credit)
|
56
59
|
end
|
@@ -61,10 +64,10 @@ private
|
|
61
64
|
end
|
62
65
|
end
|
63
66
|
|
64
|
-
# define a snapshot to keep all vital state
|
65
|
-
# the repository will
|
66
|
-
#
|
67
|
-
# to
|
67
|
+
# define a snapshot to keep all vital state
|
68
|
+
# the repository will look for the latest snapshot it can find, plus
|
69
|
+
# all events happened right after when that particular snapshot gets taken
|
70
|
+
# in order to revive your aggregate instance
|
68
71
|
class CustomerSnapshot
|
69
72
|
include Beefcake::Message
|
70
73
|
include RubyCqrs::Domain::Snapshot
|
@@ -73,7 +76,9 @@ class CustomerSnapshot
|
|
73
76
|
required :credit, :int32, 2
|
74
77
|
end
|
75
78
|
|
76
|
-
# defined the events your
|
79
|
+
# defined the events your aggregate will raise
|
80
|
+
# your domain's event collection can be defined in a way easy to share among teams
|
81
|
+
# which enables smooth integration through event logs
|
77
82
|
class CustomerCreated
|
78
83
|
include RubyCqrs::Domain::Event
|
79
84
|
include Beefcake::Message
|
@@ -89,15 +94,19 @@ class ProductOrdered
|
|
89
94
|
required :cost, :int32, 1
|
90
95
|
end
|
91
96
|
|
92
|
-
# here goes the spec of how you can use
|
97
|
+
# here goes the spec of how you can use previously defined aggregate objects
|
93
98
|
describe 'Your awesome customer domain objects powered by ruby_cqrs' do
|
94
|
-
# a context related to
|
95
|
-
# normally, when
|
99
|
+
# a context related to a command, which is specific to your problem domain
|
100
|
+
# normally, when the command arrives, one or more domain objects are activated
|
96
101
|
# in order to fulfill the command's request
|
102
|
+
#
|
103
|
+
# a command context then encapsulates any information which could be of importance
|
104
|
+
# but not part of your current domain, you can later choose to persist all or
|
105
|
+
# part of the context to support further processing or integration.
|
97
106
|
let(:command_context) {}
|
98
|
-
# you should implement your own event_store in order to persist
|
99
|
-
# you
|
100
|
-
#
|
107
|
+
# you should implement your own event_store in order to persist aggregate state
|
108
|
+
# you can read about the InMemoryEventStore implementation
|
109
|
+
# to understand the data format pass in and out
|
101
110
|
let(:event_store) { RubyCqrs::Data::InMemoryEventStore.new }
|
102
111
|
# every time when a command arrives,
|
103
112
|
# an aggregate repository gets created with related command context and event_store implementation
|
@@ -160,7 +169,7 @@ describe 'Your awesome customer domain objects powered by ruby_cqrs' do
|
|
160
169
|
(1..30).each { lucy.order_product(10) }
|
161
170
|
repository.save lucy
|
162
171
|
|
163
|
-
# well, you just have to trust a snapshot has been generated :)
|
172
|
+
# well, you'll just have to trust me a snapshot has been generated :)
|
164
173
|
end
|
165
174
|
|
166
175
|
it 'finds an old customer instance back from snapshot' do
|
@@ -168,7 +177,7 @@ describe 'Your awesome customer domain objects powered by ruby_cqrs' do
|
|
168
177
|
repository.save lucy
|
169
178
|
lucy_reload = repository.find_by lucy.aggregate_id
|
170
179
|
|
171
|
-
# again,
|
180
|
+
# again, a snapshot has been applied to gain you a bit performance boost ;)
|
172
181
|
|
173
182
|
expect(lucy_reload.name).to eq('Lucy')
|
174
183
|
expect(lucy_reload.credit).to eq(700)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_cqrs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Raven Chen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: uuidtools
|
@@ -107,7 +107,7 @@ rubyforge_project:
|
|
107
107
|
rubygems_version: 2.4.6
|
108
108
|
signing_key:
|
109
109
|
specification_version: 4
|
110
|
-
summary: ruby_cqrs-0.2.
|
110
|
+
summary: ruby_cqrs-0.2.1
|
111
111
|
test_files:
|
112
112
|
- spec/feature/basic_usage_spec.rb
|
113
113
|
- spec/feature/snapshot_spec.rb
|