sandthorn 0.8.1 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/README.md +61 -3
- data/lib/sandthorn/bounded_context.rb +39 -0
- data/lib/sandthorn/event_stores.rb +16 -0
- data/lib/sandthorn/version.rb +1 -1
- data/spec/bounded_context_spec.rb +40 -0
- data/spec/event_stores_spec.rb +37 -0
- data/spec/get_events_spec.rb +2 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 738faf44676674ac1da5b4591cfa857fa39196d2
|
4
|
+
data.tar.gz: b908878bd176b73a8a7aa5bf42173af2b28cbe79
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e8d654e0dad2df6b94227088dfdebabdebdd5ca3eebfd68dfbffc29a9d0d437d5b8edcddc2ef1d90e036bc9a2170cf3749df6b0c206535dfcea132b20e631b7
|
7
|
+
data.tar.gz: f35b3ccf742865f59a910475423e78c778b9db9c6ee4d976b22de9dd426040379a59d89b8f816b33245685da9124b3285d32467ebffabcf8ae95fca3bf5e442c
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -23,7 +23,7 @@ If you have been following [Uncle Bob](http://blog.8thlight.com/uncle-bob/2014/0
|
|
23
23
|
Check out examples of Sandthorn:
|
24
24
|
|
25
25
|
* [Examples](https://github.com/Sandthorn/sandthorn_examples) including a product shop and TicTacToe game.
|
26
|
-
* Live [demo](http://
|
26
|
+
* Live [demo](http://infinite-mesa-8629.herokuapp.com/) comparing Active Record and Sandthorn.
|
27
27
|
|
28
28
|
## How do I use Sandthorn?
|
29
29
|
|
@@ -119,12 +119,15 @@ Or install it yourself as:
|
|
119
119
|
|
120
120
|
# Configuring Sandthorn
|
121
121
|
|
122
|
-
|
122
|
+
## Driver
|
123
|
+
|
124
|
+
Sandthorn relies on a driver that is specific to the data storage that you are using. This means Sandthorn can be used with any data storage given that a driver exists.
|
123
125
|
|
124
126
|
To setup a driver you need to add it to your project's Gemfile and configure it in your application code.
|
125
127
|
|
126
128
|
gem 'sandthorn_driver_sequel'
|
127
129
|
|
130
|
+
|
128
131
|
The driver is configured when your application launches. Here's an example of how to do it using the Sequel driver and a sqlite3 database.
|
129
132
|
|
130
133
|
```ruby
|
@@ -147,6 +150,36 @@ SandthornDriverSequel.migrate_db url: url
|
|
147
150
|
|
148
151
|
Optionally, when using Sandthorn in your tests you can configure it in a `spec_helper.rb` which is then required by your test suites [example](https://github.com/Sandthorn/sandthorn_examples/blob/master/sandthorn_tictactoe/spec/spec_helper.rb#L20-L30). Note that the Sequel driver accepts a special parameter to empty the database between each test.
|
149
152
|
|
153
|
+
The Sequel driver is the only production-ready driver to date.
|
154
|
+
|
155
|
+
|
156
|
+
## Map aggregate types to event stores
|
157
|
+
|
158
|
+
Its possible to map aggregate_types to events stores from the configuration setup. This makes it possible to work with data from different stores that are using the same context, and will override any event_store setting within an aggregate.
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
url_foo = "sqlite://spec/db/sequel_driver_foo.sqlite3"
|
162
|
+
driver_foo = SandthornDriverSequel.driver_from_url(url: url_foo)
|
163
|
+
|
164
|
+
url_bar = "sqlite://spec/db/sequel_driver_bar.sqlite3"
|
165
|
+
driver_bar = SandthornDriverSequel.driver_from_url(url: url_bar)
|
166
|
+
|
167
|
+
class AnAggregate
|
168
|
+
Include Sandthorn::AggregateRoot
|
169
|
+
end
|
170
|
+
|
171
|
+
class AnOtherAggregate
|
172
|
+
Include Sandthorn::AggregateRoot
|
173
|
+
end
|
174
|
+
|
175
|
+
Sandthorn.configure do |conf|
|
176
|
+
conf.event_stores = { foo: driver_foo, bar: driver_bar }
|
177
|
+
conf.map_types = { foo: [AnAggregate], bar: [AnOtherAggregate] }
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
181
|
+
## Data serialization / deserialization
|
182
|
+
|
150
183
|
Its possible to configure how events and snapshots are serialized / deserialized. The default are YAML but can be overloaded in the configure block.
|
151
184
|
|
152
185
|
```ruby
|
@@ -158,10 +191,11 @@ Sandthorn.configure do |conf|
|
|
158
191
|
end
|
159
192
|
```
|
160
193
|
|
161
|
-
The Sequel driver is the only production-ready driver to date.
|
162
194
|
|
163
195
|
# Usage
|
164
196
|
|
197
|
+
## Aggregate Root
|
198
|
+
|
165
199
|
Any object that should have event sourcing capability must include the methods provided by `Sandthorn::AggregateRoot`. These make it possible to `commit` events and `save` changes to an aggregate. Use the `include` directive as follows:
|
166
200
|
|
167
201
|
```ruby
|
@@ -251,6 +285,30 @@ end
|
|
251
285
|
|
252
286
|
In this case, the resulting events from the commands `new` and `mark` will have the trace `{ip: :127.0.0.1}` attached to them.
|
253
287
|
|
288
|
+
## Bounded Context
|
289
|
+
|
290
|
+
A bounded context is a system divider that split large systems into smaller parts. [Bounded Context by Martin Fowler](http://martinfowler.com/bliki/BoundedContext.html)
|
291
|
+
|
292
|
+
A module can include `Sandthorn::BoundedContext` and all aggregates within the module can be retreived via the ::aggregate_types method on the module. A use case is to use it when Sandthorn is configured and setup all aggregates in a bounded context to a driver.
|
293
|
+
|
294
|
+
```ruby
|
295
|
+
require 'sandthorn/bounded_context'
|
296
|
+
|
297
|
+
module TicTacToe
|
298
|
+
include Sandthorn::BoundedContext
|
299
|
+
|
300
|
+
class Board
|
301
|
+
include Sandthorn::AggregateRoot
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
Sandthorn.configure do |conf|
|
306
|
+
conf.event_stores = { foo: driver_foo}
|
307
|
+
conf.map_types = { foo: TicTacToe.aggregate_types }
|
308
|
+
end
|
309
|
+
|
310
|
+
TicTacToe.aggregate_types -> [TicTacToy::Board]
|
311
|
+
```
|
254
312
|
|
255
313
|
# Development
|
256
314
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Sandthorn
|
2
|
+
module BoundedContext
|
3
|
+
module ClassMethods
|
4
|
+
def aggregate_types
|
5
|
+
@aggregate_list = p_aggregate_types(self)
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def p_aggregate_types(bounded_context_module)
|
11
|
+
return [] unless bounded_context_module.respond_to?(:constants)
|
12
|
+
|
13
|
+
classes = classes_in(bounded_context_module)
|
14
|
+
aggregate_list = classes.select { |item| item.include?(Sandthorn::AggregateRoot) }
|
15
|
+
modules = modules_in(bounded_context_module, classes)
|
16
|
+
|
17
|
+
aggregate_list += modules.flat_map { |m| p_aggregate_types(m) }
|
18
|
+
|
19
|
+
aggregate_list
|
20
|
+
end
|
21
|
+
|
22
|
+
def classes_in(namespace)
|
23
|
+
namespace.constants.map(&namespace.method(:const_get)).grep(Class)
|
24
|
+
end
|
25
|
+
|
26
|
+
def modules_in(namespace, classes)
|
27
|
+
namespace.constants.map(&namespace.method(:const_get)).grep(Module).delete_if do |m|
|
28
|
+
classes.include?(m) || m == Sandthorn::BoundedContext::ClassMethods
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
extend ClassMethods
|
34
|
+
|
35
|
+
def self.included( other )
|
36
|
+
other.extend( ClassMethods )
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -40,6 +40,12 @@ module Sandthorn
|
|
40
40
|
store_map.values
|
41
41
|
end
|
42
42
|
|
43
|
+
def map_types(hash)
|
44
|
+
hash.each_pair do |event_store, aggregate_types|
|
45
|
+
map_aggregate_types_to_event_store(aggregate_types, event_store)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
43
49
|
private
|
44
50
|
|
45
51
|
attr_reader :store_map
|
@@ -60,5 +66,15 @@ module Sandthorn
|
|
60
66
|
store.respond_to?(:get_events)
|
61
67
|
end
|
62
68
|
|
69
|
+
def map_aggregate_type_to_event_store(aggregate_type, event_store)
|
70
|
+
aggregate_type.event_store(event_store)
|
71
|
+
end
|
72
|
+
|
73
|
+
def map_aggregate_types_to_event_store(aggregate_types = [], event_store)
|
74
|
+
aggregate_types.each do |aggregate_type|
|
75
|
+
map_aggregate_type_to_event_store(aggregate_type, event_store)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
63
79
|
end
|
64
80
|
end
|
data/lib/sandthorn/version.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'sandthorn/bounded_context'
|
3
|
+
|
4
|
+
module Sandthorn
|
5
|
+
describe BoundedContext do
|
6
|
+
it 'should respond to `aggregate_types`' do
|
7
|
+
expect(BoundedContext).to respond_to(:aggregate_types)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "::aggregate_types" do
|
12
|
+
module TestModule
|
13
|
+
include Sandthorn::BoundedContext
|
14
|
+
class AnAggregate
|
15
|
+
include Sandthorn::AggregateRoot
|
16
|
+
end
|
17
|
+
|
18
|
+
class NotAnAggregate
|
19
|
+
end
|
20
|
+
|
21
|
+
module Deep
|
22
|
+
class DeepAggregate
|
23
|
+
include Sandthorn::AggregateRoot
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "aggregate_types should include AnAggregate" do
|
29
|
+
expect(TestModule.aggregate_types).to include(TestModule::AnAggregate)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "aggregate_types should not include NotAnAggregate" do
|
33
|
+
expect(TestModule.aggregate_types).not_to include(TestModule::NotAnAggregate)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "aggregate_types should include DeepAnAggregate in a nested Module" do
|
37
|
+
expect(TestModule.aggregate_types).to include(TestModule::Deep::DeepAggregate)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/spec/event_stores_spec.rb
CHANGED
@@ -4,6 +4,13 @@ module Sandthorn
|
|
4
4
|
describe EventStores do
|
5
5
|
let(:stores) { EventStores.new }
|
6
6
|
|
7
|
+
before do
|
8
|
+
class AnAggregate
|
9
|
+
include Sandthorn::AggregateRoot
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
7
14
|
describe "#initialize" do
|
8
15
|
context "when given a single event_store" do
|
9
16
|
it "sets it as the default event store" do
|
@@ -77,5 +84,35 @@ module Sandthorn
|
|
77
84
|
expect(stores[:foo]).to eq(store)
|
78
85
|
end
|
79
86
|
end
|
87
|
+
|
88
|
+
describe "#map_types" do
|
89
|
+
|
90
|
+
context "map two events stores" do
|
91
|
+
|
92
|
+
class AnAggregate1
|
93
|
+
include Sandthorn::AggregateRoot
|
94
|
+
end
|
95
|
+
|
96
|
+
class AnAggregate2
|
97
|
+
include Sandthorn::AggregateRoot
|
98
|
+
end
|
99
|
+
|
100
|
+
before do
|
101
|
+
store = double
|
102
|
+
stores.add(:foo, store)
|
103
|
+
stores.add(:bar, store)
|
104
|
+
stores.map_types(foo: [AnAggregate1], bar: [AnAggregate2])
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should map event_store foo to AnAggregate1" do
|
108
|
+
expect(AnAggregate1.event_store).to eq(:foo)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should map event_store bar to AnAggregate2" do
|
112
|
+
expect(AnAggregate2.event_store).to eq(:bar)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
80
117
|
end
|
81
118
|
end
|
data/spec/get_events_spec.rb
CHANGED
@@ -6,7 +6,7 @@ end
|
|
6
6
|
|
7
7
|
class AnotherAggregate
|
8
8
|
include Sandthorn::AggregateRoot
|
9
|
-
event_store :
|
9
|
+
event_store :should_override_this
|
10
10
|
end
|
11
11
|
|
12
12
|
describe Sandthorn do
|
@@ -66,6 +66,7 @@ describe Sandthorn do
|
|
66
66
|
url = "sqlite://spec/db/other_db.sqlite3"
|
67
67
|
driver = SandthornDriverSequel.driver_from_url(url: url)
|
68
68
|
Sandthorn.event_stores.add(:other, driver)
|
69
|
+
Sandthorn.event_stores.map_types(other: [AnotherAggregate])
|
69
70
|
migrator = SandthornDriverSequel::Migration.new url: url
|
70
71
|
SandthornDriverSequel.migrate_db url: url
|
71
72
|
migrator.send(:clear_for_test)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sandthorn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lars Krantz
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2015-09-
|
13
|
+
date: 2015-09-29 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
@@ -192,6 +192,7 @@ files:
|
|
192
192
|
- lib/sandthorn/aggregate_root_base.rb
|
193
193
|
- lib/sandthorn/aggregate_root_marshal.rb
|
194
194
|
- lib/sandthorn/aggregate_root_snapshot.rb
|
195
|
+
- lib/sandthorn/bounded_context.rb
|
195
196
|
- lib/sandthorn/errors.rb
|
196
197
|
- lib/sandthorn/event.rb
|
197
198
|
- lib/sandthorn/event_inspector.rb
|
@@ -202,6 +203,7 @@ files:
|
|
202
203
|
- spec/aggregate_root_spec.rb
|
203
204
|
- spec/aggregate_snapshot_spec.rb
|
204
205
|
- spec/benchmark_spec.rb
|
206
|
+
- spec/bounded_context_spec.rb
|
205
207
|
- spec/complex_aggregate_spec.rb
|
206
208
|
- spec/db/sequel_driver.sqlite3_old
|
207
209
|
- spec/different_driver_spec.rb
|
@@ -243,6 +245,7 @@ test_files:
|
|
243
245
|
- spec/aggregate_root_spec.rb
|
244
246
|
- spec/aggregate_snapshot_spec.rb
|
245
247
|
- spec/benchmark_spec.rb
|
248
|
+
- spec/bounded_context_spec.rb
|
246
249
|
- spec/complex_aggregate_spec.rb
|
247
250
|
- spec/db/sequel_driver.sqlite3_old
|
248
251
|
- spec/different_driver_spec.rb
|