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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a1a17a62b61cd04ee60ba16439ae0d5b93ffd179
4
- data.tar.gz: 7ee6bcb37e9016d254c2ed32dcefb45831e55f8b
3
+ metadata.gz: 738faf44676674ac1da5b4591cfa857fa39196d2
4
+ data.tar.gz: b908878bd176b73a8a7aa5bf42173af2b28cbe79
5
5
  SHA512:
6
- metadata.gz: 7b9bdd8af7ca4e12d2924786328675264f60411a066b069bab152ab0a54bdecf2db3ea3bb0e69a4419c9b0114782d3e3ba753fc48e020f8c0315ca2ec6168017
7
- data.tar.gz: 3584ac990d2df159a15ed211d43a7ef0c17abf966599efedbc27fbde9d5e727920e0c8bbc2add4f6b3f315fe398f068a94b4d7c3a2a327546f619bce55e240aa
6
+ metadata.gz: 6e8d654e0dad2df6b94227088dfdebabdebdd5ca3eebfd68dfbffc29a9d0d437d5b8edcddc2ef1d90e036bc9a2170cf3749df6b0c206535dfcea132b20e631b7
7
+ data.tar.gz: f35b3ccf742865f59a910475423e78c778b9db9c6ee4d976b22de9dd426040379a59d89b8f816b33245685da9124b3285d32467ebffabcf8ae95fca3bf5e442c
@@ -3,3 +3,4 @@ rvm:
3
3
  - 2.1.0
4
4
  - 2.1.1
5
5
  - 2.0.0
6
+ sudo: false
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://demo.sandthorn.org) comparing Active Record and Sandthorn.
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
- Sandthorn relies on a driver 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.
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
@@ -1,3 +1,3 @@
1
1
  module Sandthorn
2
- VERSION = "0.8.1"
2
+ VERSION = "0.9.0"
3
3
  end
@@ -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
@@ -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
@@ -6,7 +6,7 @@ end
6
6
 
7
7
  class AnotherAggregate
8
8
  include Sandthorn::AggregateRoot
9
- event_store :other
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.8.1
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-10 00:00:00.000000000 Z
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