sandthorn 0.8.1 → 0.9.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/.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
|