ruby_cqrs 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Build Status](https://travis-ci.org/iravench/ruby_cqrs.svg?branch=master)](https://travis-ci.org/iravench/ruby_cqrs) [![Code Climate](https://codeclimate.com/github/iravench/ruby_cqrs/badges/gpa.svg)](https://codeclimate.com/github/iravench/ruby_cqrs) [![Test Coverage](https://codeclimate.com/github/iravench/ruby_cqrs/badges/coverage.svg)](https://codeclimate.com/github/iravench/ruby_cqrs)
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/ruby_cqrs.svg)](http://badge.fury.io/rb/ruby_cqrs) [![Build Status](https://travis-ci.org/iravench/ruby_cqrs.svg?branch=master)](https://travis-ci.org/iravench/ruby_cqrs) [![Code Climate](https://codeclimate.com/github/iravench/ruby_cqrs/badges/gpa.svg)](https://codeclimate.com/github/iravench/ruby_cqrs) [![Test Coverage](https://codeclimate.com/github/iravench/ruby_cqrs/badges/coverage.svg)](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
|