aggregate_root 0.0.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 +7 -0
- data/.gitignore +9 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +3 -0
- data/LICENSE +5 -0
- data/README.md +111 -0
- data/Rakefile +4 -0
- data/aggregate_root.gemspec +29 -0
- data/lib/aggregate_root.rb +5 -0
- data/lib/aggregate_root/base.rb +31 -0
- data/lib/aggregate_root/repository.rb +26 -0
- data/lib/aggregate_root/version.rb +3 -0
- metadata +152 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 7b7092c2b7985985ffa355189839d94ef36599f3
|
|
4
|
+
data.tar.gz: e421f24047e7491d6a0fc052d020224102ff7f16
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 23493dde4e5a29af698b702d8c8f5cda809113ad1f7fa435dc8c508196e6c74d55eb41fb63a031431955944c615aa51498f1e9bb169849bad29cf9d04aee52e9
|
|
7
|
+
data.tar.gz: 7cee5df16ff7570043cd1a6f54d668abb27791a28718b9fd407f0c008d4040dfb6042e5fcf2bad815ca5cfd6d2120c02b1f00bc7c982c9192d03d3e2e6dfe35f
|
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# AggregateRoot
|
|
2
|
+
|
|
3
|
+
Event sourced (with Rails Event Store) aggregate root implementation.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
* Add following line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'aggregate_root'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
To create a new aggregate domain object include `AggregateRoot::Base` module.
|
|
16
|
+
It is important to assign `id` at initializer - it will be used as a event store stream name.
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
class Order
|
|
20
|
+
include AggregateRoot::Base
|
|
21
|
+
|
|
22
|
+
def initialize(id = generate_id)
|
|
23
|
+
self.id = id
|
|
24
|
+
# any other code here
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# ... more later
|
|
28
|
+
end
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
#### Define aggregate logic
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
OrderSubmitted = Class.new(RailsEventStore::Event)
|
|
35
|
+
OrderExpired = Class.new(RailsEventStore::Event)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
class Order
|
|
40
|
+
include AggregateRoot::Base
|
|
41
|
+
HasBeenAlreadySubmitted = Class.new(StandardError)
|
|
42
|
+
HasExpired = Class.new(StandardError)
|
|
43
|
+
|
|
44
|
+
def initialize(id = generate_id)
|
|
45
|
+
self.id = id
|
|
46
|
+
self.state = :new
|
|
47
|
+
# any other code here
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def submit
|
|
51
|
+
raise HasBeenAlreadySubmitted if state == :submitted
|
|
52
|
+
raise HasExpired if state == :expired
|
|
53
|
+
apply OrderSubmitted.new(delivery_date: Time.now + 24.hours)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def expire
|
|
57
|
+
apply OrderExpired.new
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
attr_accessor :state
|
|
62
|
+
|
|
63
|
+
def apply_order_submitted(event)
|
|
64
|
+
self.state = :submitted
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def apply_order_expired(event)
|
|
68
|
+
self.state = :expired
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### Loading an aggregate root object from event store
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
repository = ArggregateRoot::Repository.new
|
|
77
|
+
order = Order.new(ORDER_ID)
|
|
78
|
+
repository.load(order)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Load gets all domain event stored for the aggregate in event store and apply them
|
|
82
|
+
in order to given aggregate to rebuild aggregate's state.
|
|
83
|
+
|
|
84
|
+
#### Storing an aggregate root's changes in event store
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
repository = ArggregateRoot::Repository.new
|
|
88
|
+
order = Order.new(ORDER_ID)
|
|
89
|
+
repository.load(order)
|
|
90
|
+
order.submit
|
|
91
|
+
repository.store(order)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Store gets all unpublished aggregate's domain events (created by executing a domain logic method like `submit`)
|
|
95
|
+
and publish them in order of creation to event store.
|
|
96
|
+
|
|
97
|
+
#### Resources
|
|
98
|
+
|
|
99
|
+
There're already few blogposts about Rails EventStore. Check them out:
|
|
100
|
+
|
|
101
|
+
* [Why use Event Sourcing](http://blog.arkency.com/2015/03/why-use-event-sourcing/)
|
|
102
|
+
* [The Event Store for Rails developers](http://blog.arkency.com/2015/04/the-event-store-for-rails-developers/)
|
|
103
|
+
* [Fast introduction to Event Sourcing for Ruby programmers](http://blog.arkency.com/2015/03/fast-introduction-to-event-sourcing-for-ruby-programmers/)
|
|
104
|
+
|
|
105
|
+
## About
|
|
106
|
+
|
|
107
|
+
<img src="http://arkency.com/images/arkency.png" alt="Arkency" width="20%" align="left" />
|
|
108
|
+
|
|
109
|
+
Rails Event Store is funded and maintained by Arkency. Check out our other [open-source projects](https://github.com/arkency).
|
|
110
|
+
|
|
111
|
+
You can also [hire us](http://arkency.com) or [read our blog](http://blog.arkency.com).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'aggregate_root/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = 'aggregate_root'
|
|
8
|
+
spec.version = AggregateRoot::VERSION
|
|
9
|
+
spec.authors = ['mpraglowski']
|
|
10
|
+
spec.email = ['m@praglowski.com']
|
|
11
|
+
|
|
12
|
+
spec.summary = %q{Event sourced (with Rails Event Store) aggregate root implementation}
|
|
13
|
+
spec.description = %q{Event sourced (with Rails Event Store) aggregate root implementation}
|
|
14
|
+
spec.homepage = ''
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
17
|
+
spec.bindir = 'exe'
|
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
19
|
+
spec.require_paths = ['lib']
|
|
20
|
+
|
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.9'
|
|
22
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
|
23
|
+
spec.add_development_dependency 'pry'
|
|
24
|
+
spec.add_development_dependency 'rspec'
|
|
25
|
+
spec.add_development_dependency 'rails', '~> 4.2.1'
|
|
26
|
+
|
|
27
|
+
spec.add_dependency 'rails_event_store', '>= 0.1.4'
|
|
28
|
+
spec.add_dependency 'activesupport', '>= 3.0'
|
|
29
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'active_support/inflector'
|
|
2
|
+
|
|
3
|
+
module AggregateRoot
|
|
4
|
+
module Base
|
|
5
|
+
attr_reader :id
|
|
6
|
+
|
|
7
|
+
def apply(event)
|
|
8
|
+
apply_event(event)
|
|
9
|
+
unpublished_events << event
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def apply_old_event(event)
|
|
13
|
+
apply_event(event)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def unpublished_events
|
|
17
|
+
@unpublished_events ||= []
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
attr_writer :id
|
|
22
|
+
|
|
23
|
+
def apply_event(event)
|
|
24
|
+
send("apply_#{event.event_type.underscore.gsub('/', '_')}", event)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def generate_uuid
|
|
28
|
+
SecureRandom.uuid
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module AggregateRoot
|
|
2
|
+
class Repository
|
|
3
|
+
def initialize(event_store = default_event_store)
|
|
4
|
+
@event_store = event_store
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def store(aggregate)
|
|
8
|
+
aggregate.unpublished_events.each do |event|
|
|
9
|
+
event_store.publish_event(event, aggregate.id)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def load(aggregate)
|
|
14
|
+
events = event_store.read_all_events(aggregate.id)
|
|
15
|
+
events.each do |event|
|
|
16
|
+
aggregate.apply_old_event(event)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
attr_accessor :event_store
|
|
21
|
+
|
|
22
|
+
def default_event_store
|
|
23
|
+
RailsEventStore::Client.new
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: aggregate_root
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- mpraglowski
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2016-01-27 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.9'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.9'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '10.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '10.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: pry
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rspec
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rails
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: 4.2.1
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: 4.2.1
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: rails_event_store
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: 0.1.4
|
|
90
|
+
type: :runtime
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: 0.1.4
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: activesupport
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '3.0'
|
|
104
|
+
type: :runtime
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '3.0'
|
|
111
|
+
description: Event sourced (with Rails Event Store) aggregate root implementation
|
|
112
|
+
email:
|
|
113
|
+
- m@praglowski.com
|
|
114
|
+
executables: []
|
|
115
|
+
extensions: []
|
|
116
|
+
extra_rdoc_files: []
|
|
117
|
+
files:
|
|
118
|
+
- ".gitignore"
|
|
119
|
+
- CHANGELOG.md
|
|
120
|
+
- Gemfile
|
|
121
|
+
- LICENSE
|
|
122
|
+
- README.md
|
|
123
|
+
- Rakefile
|
|
124
|
+
- aggregate_root.gemspec
|
|
125
|
+
- lib/aggregate_root.rb
|
|
126
|
+
- lib/aggregate_root/base.rb
|
|
127
|
+
- lib/aggregate_root/repository.rb
|
|
128
|
+
- lib/aggregate_root/version.rb
|
|
129
|
+
homepage: ''
|
|
130
|
+
licenses: []
|
|
131
|
+
metadata: {}
|
|
132
|
+
post_install_message:
|
|
133
|
+
rdoc_options: []
|
|
134
|
+
require_paths:
|
|
135
|
+
- lib
|
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
137
|
+
requirements:
|
|
138
|
+
- - ">="
|
|
139
|
+
- !ruby/object:Gem::Version
|
|
140
|
+
version: '0'
|
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
142
|
+
requirements:
|
|
143
|
+
- - ">="
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: '0'
|
|
146
|
+
requirements: []
|
|
147
|
+
rubyforge_project:
|
|
148
|
+
rubygems_version: 2.2.5
|
|
149
|
+
signing_key:
|
|
150
|
+
specification_version: 4
|
|
151
|
+
summary: Event sourced (with Rails Event Store) aggregate root implementation
|
|
152
|
+
test_files: []
|