synapse-core 0.5.6 → 0.6.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 +7 -0
- data/lib/synapse-core.rb +31 -1
- data/lib/synapse/command.rb +1 -0
- data/lib/synapse/command/callbacks/future.rb +3 -3
- data/lib/synapse/command/callbacks/void.rb +14 -0
- data/lib/synapse/command/command_bus.rb +2 -2
- data/lib/synapse/command/command_callback.rb +9 -2
- data/lib/synapse/command/gateway.rb +1 -1
- data/lib/synapse/command/gateway/interval_retry_scheduler.rb +6 -6
- data/lib/synapse/command/gateway/retry_scheduler.rb +7 -4
- data/lib/synapse/command/interceptor_chain.rb +1 -1
- data/lib/synapse/command/interceptors/serialization.rb +2 -1
- data/lib/synapse/command/simple_command_bus.rb +23 -34
- data/lib/synapse/common.rb +2 -2
- data/lib/synapse/common/concurrency/disposable_lock.rb +157 -0
- data/lib/synapse/common/concurrency/identifier_lock_manager.rb +164 -0
- data/lib/synapse/common/duplication.rb +4 -4
- data/lib/synapse/common/errors.rb +5 -0
- data/lib/synapse/configuration/container.rb +1 -1
- data/lib/synapse/configuration/container_builder.rb +1 -1
- data/lib/synapse/configuration/definition.rb +1 -1
- data/lib/synapse/domain/aggregate_root.rb +1 -1
- data/lib/synapse/domain/simple_stream.rb +3 -5
- data/lib/synapse/domain/stream.rb +2 -2
- data/lib/synapse/event_bus/event_bus.rb +2 -2
- data/lib/synapse/event_bus/simple_event_bus.rb +5 -6
- data/lib/synapse/event_sourcing/caching.rb +1 -1
- data/lib/synapse/event_sourcing/entity.rb +4 -2
- data/lib/synapse/event_sourcing/repository.rb +9 -13
- data/lib/synapse/event_sourcing/snapshot/taker.rb +1 -1
- data/lib/synapse/mapping/mapping.rb +1 -1
- data/lib/synapse/process_manager/correlation.rb +3 -29
- data/lib/synapse/process_manager/pessimistic_lock_manager.rb +3 -3
- data/lib/synapse/process_manager/process.rb +2 -6
- data/lib/synapse/process_manager/process_manager.rb +1 -1
- data/lib/synapse/process_manager/process_repository.rb +1 -1
- data/lib/synapse/process_manager/repository/in_memory.rb +5 -4
- data/lib/synapse/process_manager/simple_process_manager.rb +2 -2
- data/lib/synapse/repository/errors.rb +2 -2
- data/lib/synapse/repository/locking.rb +48 -0
- data/lib/synapse/repository/optimistic_lock_manager.rb +10 -9
- data/lib/synapse/repository/pessimistic_lock_manager.rb +5 -4
- data/lib/synapse/repository/repository.rb +1 -1
- data/lib/synapse/repository/simple_repository.rb +8 -7
- data/lib/synapse/serialization/converter.rb +3 -0
- data/lib/synapse/serialization/converter_factory.rb +6 -5
- data/lib/synapse/serialization/serialized_object.rb +4 -4
- data/lib/synapse/serialization/serialized_type.rb +4 -4
- data/lib/synapse/uow/listener_collection.rb +12 -9
- data/lib/synapse/uow/provider.rb +1 -1
- data/lib/synapse/uow/uow.rb +1 -1
- data/lib/synapse/upcasting/upcaster_chain.rb +1 -1
- data/lib/synapse/version.rb +1 -1
- data/test/command/serialization_test.rb +5 -2
- data/test/command/simple_command_bus_test.rb +9 -16
- data/test/command/validation_test.rb +1 -1
- data/test/common/concurrency/identifier_lock_manager_test.rb +137 -0
- data/test/configuration/component/serialization/converter_factory_test.rb +2 -2
- data/test/event_sourcing/repository_test.rb +18 -0
- data/test/repository/simple_repository_test.rb +42 -10
- data/test/test_helper.rb +3 -4
- metadata +25 -29
- data/lib/synapse.rb +0 -34
- data/lib/synapse/common/concurrency/identifier_lock.rb +0 -56
- data/lib/synapse/common/concurrency/public_lock.rb +0 -95
- data/lib/synapse/event_bus/clustering/cluster.rb +0 -10
- data/lib/synapse/event_bus/clustering/event_bus.rb +0 -55
- data/lib/synapse/event_bus/clustering/selector.rb +0 -14
- data/lib/synapse/rails/injection_helper.rb +0 -23
- data/lib/synapse/railtie.rb +0 -17
- data/test/common/concurrency/identifier_lock_test.rb +0 -25
- data/test/common/concurrency/public_lock_test.rb +0 -83
- data/test/process_manager/correlation_test.rb +0 -24
- data/test/rails/injection_helper_test.rb +0 -27
@@ -12,6 +12,7 @@ module Synapse
|
|
12
12
|
# before any listeners are allowed to do anything, and log that the commit is finished after
|
13
13
|
# all other listeners have finished.
|
14
14
|
class UnitOfWorkListenerCollection < UnitOfWorkListener
|
15
|
+
# @return [undefined]
|
15
16
|
def initialize
|
16
17
|
@listeners = Array.new
|
17
18
|
@logger = Logging.logger[self.class]
|
@@ -22,17 +23,17 @@ module Synapse
|
|
22
23
|
# @param [UnitOfWorkListener] listener
|
23
24
|
# @return [undefined]
|
24
25
|
def push(listener)
|
25
|
-
@logger.debug
|
26
|
+
@logger.debug "Registering listener {#{listener.class}}"
|
26
27
|
@listeners.push listener
|
27
28
|
end
|
28
29
|
|
29
|
-
|
30
|
+
alias_method :<<, :push
|
30
31
|
|
31
32
|
# @param [UnitOfWork] unit
|
32
33
|
# @return [undefined]
|
33
34
|
def on_start(unit)
|
34
35
|
@listeners.each do |listener|
|
35
|
-
@logger.debug
|
36
|
+
@logger.debug "Notifying {#{listener.class}} that unit of work is starting"
|
36
37
|
listener.on_start unit
|
37
38
|
end
|
38
39
|
end
|
@@ -54,7 +55,7 @@ module Synapse
|
|
54
55
|
# @return [undefined]
|
55
56
|
def on_prepare_commit(unit, aggregates, events)
|
56
57
|
@listeners.each do |listener|
|
57
|
-
@logger.debug
|
58
|
+
@logger.debug "Notifying {#{listener.class}} that unit of work is preparing for commit"
|
58
59
|
listener.on_prepare_commit unit, aggregates, events
|
59
60
|
end
|
60
61
|
end
|
@@ -64,7 +65,7 @@ module Synapse
|
|
64
65
|
# @return [undefined]
|
65
66
|
def on_prepare_transaction_commit(unit, transaction)
|
66
67
|
@listeners.each do |listener|
|
67
|
-
@logger.debug
|
68
|
+
@logger.debug "Notifying {#{listener.class}} that unit of work is preparing for tx commit"
|
68
69
|
listener.on_prepare_transaction_commit unit, transaction
|
69
70
|
end
|
70
71
|
end
|
@@ -73,7 +74,7 @@ module Synapse
|
|
73
74
|
# @return [undefined]
|
74
75
|
def after_commit(unit)
|
75
76
|
@listeners.reverse_each do |listener|
|
76
|
-
@logger.debug
|
77
|
+
@logger.debug "Notifying {#{listener.class}} that unit of work has been committed"
|
77
78
|
listener.after_commit unit
|
78
79
|
end
|
79
80
|
end
|
@@ -83,7 +84,7 @@ module Synapse
|
|
83
84
|
# @return [undefined]
|
84
85
|
def on_rollback(unit, cause = nil)
|
85
86
|
@listeners.reverse_each do |listener|
|
86
|
-
@logger.debug
|
87
|
+
@logger.debug "Notifying {#{listener.class}} that unit of work is rolling back"
|
87
88
|
listener.on_rollback unit, cause
|
88
89
|
end
|
89
90
|
end
|
@@ -92,13 +93,15 @@ module Synapse
|
|
92
93
|
# @return [undefined]
|
93
94
|
def on_cleanup(unit)
|
94
95
|
@listeners.reverse_each do |listener|
|
95
|
-
@logger.debug
|
96
|
+
@logger.debug "Notifying {#{listener.class}} that unit of work is cleaning up"
|
96
97
|
|
97
98
|
begin
|
98
99
|
listener.on_cleanup unit
|
99
100
|
rescue => exception
|
100
101
|
# Ignore this exception so that we can continue cleaning up
|
101
|
-
|
102
|
+
backtrace = exception.backtrace.join $RS
|
103
|
+
@logger.warn "Listener {#{listener.class}} raised exception during cleanup: " +
|
104
|
+
"#{exception.inspect} #{backtrace}"
|
102
105
|
end
|
103
106
|
end
|
104
107
|
end
|
data/lib/synapse/uow/provider.rb
CHANGED
@@ -3,6 +3,7 @@ module Synapse
|
|
3
3
|
# Entry point for components to access units of work. Components managing transactional
|
4
4
|
# boundaries can register and clear unit of work instances.
|
5
5
|
class UnitOfWorkProvider
|
6
|
+
# @return [undefined]
|
6
7
|
def initialize
|
7
8
|
@threads = Hash.new
|
8
9
|
end
|
@@ -40,7 +41,6 @@ module Synapse
|
|
40
41
|
stack.last
|
41
42
|
end
|
42
43
|
|
43
|
-
|
44
44
|
# Pushes the given unit of work onto the top of the stack, making it the active unit of work
|
45
45
|
#
|
46
46
|
# If there are other units of work bound to this provider, they will be held until the given
|
data/lib/synapse/uow/uow.rb
CHANGED
@@ -160,7 +160,7 @@ module Synapse
|
|
160
160
|
# @return [AggregateRoot] Returns nil if no similar aggregate was found
|
161
161
|
def find_similar_aggregate(aggregate)
|
162
162
|
@aggregates.each_key do |candidate|
|
163
|
-
if aggregate.class === candidate
|
163
|
+
if aggregate.class === candidate && aggregate.id == candidate.id
|
164
164
|
return candidate
|
165
165
|
end
|
166
166
|
end
|
data/lib/synapse/version.rb
CHANGED
@@ -10,11 +10,14 @@ module Synapse
|
|
10
10
|
command = CommandMessage.build
|
11
11
|
chain = Object.new
|
12
12
|
unit = Object.new
|
13
|
+
result = Object.new
|
13
14
|
|
14
|
-
mock(chain).proceed(command)
|
15
|
+
mock(chain).proceed(command) do
|
16
|
+
result
|
17
|
+
end
|
15
18
|
mock(unit).register_listener(is_a(SerializationOptimizingListener))
|
16
19
|
|
17
|
-
interceptor.intercept(command, unit, chain)
|
20
|
+
assert_same result, interceptor.intercept(command, unit, chain)
|
18
21
|
end
|
19
22
|
end
|
20
23
|
|
@@ -108,29 +108,22 @@ module Synapse
|
|
108
108
|
@command_bus.dispatch_with_callback command, callback
|
109
109
|
end
|
110
110
|
|
111
|
-
should '
|
112
|
-
|
113
|
-
|
114
|
-
mock(@logger).debug(anything).ordered
|
115
|
-
mock(@logger).info(anything).ordered
|
111
|
+
should 'return the previous handler when a subscribed handler is replaced' do
|
112
|
+
handler_a = Object.new
|
113
|
+
handler_b = Object.new
|
116
114
|
|
117
|
-
@command_bus.subscribe TestCommand,
|
118
|
-
@command_bus.subscribe TestCommand,
|
115
|
+
assert_nil (@command_bus.subscribe TestCommand, handler_a)
|
116
|
+
assert_same (@command_bus.subscribe TestCommand, handler_b), handler_a
|
119
117
|
end
|
120
118
|
|
121
|
-
should '
|
119
|
+
should 'return true when a handler is unsubscribed' do
|
122
120
|
handler_a = Object.new
|
123
121
|
handler_b = Object.new
|
124
122
|
|
125
|
-
|
126
|
-
mock(@logger).debug(anything).ordered # now subscribed
|
127
|
-
mock(@logger).info(anything).ordered # subscribed to different
|
128
|
-
mock(@logger).debug(anything).ordered # now unsubscribed
|
129
|
-
|
130
|
-
@command_bus.unsubscribe TestCommand, handler_a
|
123
|
+
refute @command_bus.unsubscribe TestCommand, handler_a
|
131
124
|
@command_bus.subscribe TestCommand, handler_a
|
132
|
-
@command_bus.unsubscribe TestCommand, handler_b
|
133
|
-
@command_bus.unsubscribe TestCommand, handler_a
|
125
|
+
refute @command_bus.unsubscribe TestCommand, handler_b
|
126
|
+
assert @command_bus.unsubscribe TestCommand, handler_a
|
134
127
|
end
|
135
128
|
end
|
136
129
|
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Synapse
|
4
|
+
class IdentifierLockManagerTest < Test::Unit::TestCase
|
5
|
+
CountdownLatch = Contender::CountdownLatch
|
6
|
+
|
7
|
+
should 'dispose locks when they are no longer in use' do
|
8
|
+
manager = IdentifierLockManager.new
|
9
|
+
|
10
|
+
identifier = SecureRandom.uuid
|
11
|
+
manager.obtain_lock identifier
|
12
|
+
manager.release_lock identifier
|
13
|
+
|
14
|
+
assert_equal 0, manager.internal_locks.size
|
15
|
+
end
|
16
|
+
|
17
|
+
should 'not dispose locks when they are still in use' do
|
18
|
+
manager = IdentifierLockManager.new
|
19
|
+
|
20
|
+
identifier = SecureRandom.uuid
|
21
|
+
|
22
|
+
refute manager.owned? identifier
|
23
|
+
|
24
|
+
manager.obtain_lock identifier
|
25
|
+
assert manager.owned? identifier
|
26
|
+
|
27
|
+
manager.obtain_lock identifier
|
28
|
+
assert manager.owned? identifier
|
29
|
+
|
30
|
+
manager.release_lock identifier
|
31
|
+
assert manager.owned? identifier
|
32
|
+
|
33
|
+
manager.release_lock identifier
|
34
|
+
refute manager.owned? identifier
|
35
|
+
end
|
36
|
+
|
37
|
+
should 'detect a deadlock between two threads' do
|
38
|
+
manager = IdentifierLockManager.new
|
39
|
+
|
40
|
+
start_latch = CountdownLatch.new 1
|
41
|
+
latch = CountdownLatch.new 1
|
42
|
+
deadlock = Atomic.new false
|
43
|
+
|
44
|
+
lock_a = SecureRandom.uuid
|
45
|
+
lock_b = SecureRandom.uuid
|
46
|
+
|
47
|
+
start_lock_thread start_latch, latch, deadlock, manager, lock_a, manager, lock_b
|
48
|
+
|
49
|
+
manager.obtain_lock lock_b
|
50
|
+
|
51
|
+
start_latch.await
|
52
|
+
latch.countdown
|
53
|
+
|
54
|
+
begin
|
55
|
+
manager.obtain_lock lock_a
|
56
|
+
assert deadlock.get
|
57
|
+
rescue DeadlockError
|
58
|
+
# This is expected behavior
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
should 'detect a deadlock between two threads across lock managers' do
|
63
|
+
manager_a = IdentifierLockManager.new
|
64
|
+
manager_b = IdentifierLockManager.new
|
65
|
+
|
66
|
+
start_latch = CountdownLatch.new 1
|
67
|
+
latch = CountdownLatch.new 1
|
68
|
+
deadlock = Atomic.new false
|
69
|
+
|
70
|
+
lock_a = SecureRandom.uuid
|
71
|
+
lock_b = SecureRandom.uuid
|
72
|
+
|
73
|
+
start_lock_thread start_latch, latch, deadlock, manager_a, lock_a, manager_b, lock_a
|
74
|
+
|
75
|
+
manager_b.obtain_lock lock_a
|
76
|
+
|
77
|
+
start_latch.await
|
78
|
+
latch.countdown
|
79
|
+
|
80
|
+
begin
|
81
|
+
manager_a.obtain_lock lock_a
|
82
|
+
assert deadlock.get
|
83
|
+
rescue DeadlockError
|
84
|
+
# This is expected behavior
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
should 'detect a deadlock between three threads in a vector' do
|
89
|
+
manager = IdentifierLockManager.new
|
90
|
+
|
91
|
+
start_latch = CountdownLatch.new 3
|
92
|
+
latch = CountdownLatch.new 1
|
93
|
+
deadlock = Atomic.new false
|
94
|
+
|
95
|
+
lock_a = SecureRandom.uuid
|
96
|
+
lock_b = SecureRandom.uuid
|
97
|
+
lock_c = SecureRandom.uuid
|
98
|
+
lock_d = SecureRandom.uuid
|
99
|
+
|
100
|
+
start_lock_thread start_latch, latch, deadlock, manager, lock_a, manager, lock_b
|
101
|
+
start_lock_thread start_latch, latch, deadlock, manager, lock_b, manager, lock_c
|
102
|
+
start_lock_thread start_latch, latch, deadlock, manager, lock_c, manager, lock_d
|
103
|
+
|
104
|
+
manager.obtain_lock lock_d
|
105
|
+
|
106
|
+
start_latch.await
|
107
|
+
latch.countdown
|
108
|
+
|
109
|
+
begin
|
110
|
+
manager.obtain_lock lock_a
|
111
|
+
assert deadlock.get
|
112
|
+
rescue DeadlockError
|
113
|
+
# This is expected behavior
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def start_lock_thread(start_latch, latch, deadlock, manager_a, lock_a, manager_b, lock_b)
|
120
|
+
Thread.new do
|
121
|
+
manager_a.obtain_lock lock_a
|
122
|
+
start_latch.countdown
|
123
|
+
|
124
|
+
begin
|
125
|
+
latch.await
|
126
|
+
|
127
|
+
manager_b.obtain_lock lock_b
|
128
|
+
manager_b.release_lock lock_b
|
129
|
+
rescue DeadlockError
|
130
|
+
deadlock.set true
|
131
|
+
ensure
|
132
|
+
manager_a.release_lock lock_a
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -33,7 +33,7 @@ module Synapse
|
|
33
33
|
@builder.converter_factory
|
34
34
|
|
35
35
|
factory = @container.resolve :converter_factory
|
36
|
-
factory.converters.
|
36
|
+
factory.converters.first.is_a? Serialization::JsonToObjectConverter
|
37
37
|
|
38
38
|
# Customized
|
39
39
|
@builder.converter_factory :alt_factory do
|
@@ -41,7 +41,7 @@ module Synapse
|
|
41
41
|
end
|
42
42
|
|
43
43
|
factory = @container.resolve :alt_factory
|
44
|
-
factory.converters.
|
44
|
+
factory.converters.first.is_a? Serialization::JsonToObjectConverter
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
@@ -70,6 +70,24 @@ module Synapse
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
+
should 'raise an exception while saving if lock could not be validated' do
|
74
|
+
event = create_event(123, 0, StubCreatedEvent.new(123))
|
75
|
+
|
76
|
+
mock(@event_store).read_events(@factory.type_identifier, 123) do
|
77
|
+
Domain::SimpleDomainEventStream.new event
|
78
|
+
end
|
79
|
+
|
80
|
+
aggregate = @repository.load 123
|
81
|
+
|
82
|
+
mock(@lock_manager).validate_lock(aggregate) do
|
83
|
+
false
|
84
|
+
end
|
85
|
+
|
86
|
+
assert_raise Repository::ConcurrencyError do
|
87
|
+
@unit.commit
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
73
91
|
should 'defer version checking to a conflict resolver if one is set' do
|
74
92
|
@repository.conflict_resolver = AcceptAllConflictResolver.new
|
75
93
|
|
@@ -2,12 +2,15 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
module Synapse
|
4
4
|
module Repository
|
5
|
+
|
5
6
|
class SimpleRepositoryTest < Test::Unit::TestCase
|
6
7
|
def setup
|
7
8
|
@unit_provider = UnitOfWork::UnitOfWorkProvider.new
|
8
9
|
@unit_factory = UnitOfWork::UnitOfWorkFactory.new @unit_provider
|
9
10
|
|
10
|
-
@
|
11
|
+
@lock_manager = NullLockManager.new
|
12
|
+
|
13
|
+
@repository = SimpleRepository.new @lock_manager, TestMappedAggregate
|
11
14
|
@repository.event_bus = EventBus::SimpleEventBus.new
|
12
15
|
@repository.unit_provider = @unit_provider
|
13
16
|
end
|
@@ -15,43 +18,71 @@ module Synapse
|
|
15
18
|
should 'load an aggregate using its finder' do
|
16
19
|
unit = @unit_factory.create
|
17
20
|
|
18
|
-
|
19
|
-
|
21
|
+
aggregate_id = SecureRandom.uuid
|
22
|
+
aggregate = TestMappedAggregate.new aggregate_id
|
23
|
+
|
24
|
+
mock(TestMappedAggregate).find(aggregate_id) do
|
20
25
|
aggregate
|
21
26
|
end
|
22
27
|
|
23
|
-
loaded = @repository.load
|
28
|
+
loaded = @repository.load aggregate_id
|
24
29
|
|
25
30
|
assert_same loaded, aggregate
|
26
31
|
end
|
27
32
|
|
28
33
|
should 'raise an exception if the aggregate could not be found' do
|
29
|
-
|
34
|
+
aggregate_id = SecureRandom.uuid
|
35
|
+
|
36
|
+
mock(TestMappedAggregate).find(aggregate_id)
|
30
37
|
|
31
38
|
assert_raise AggregateNotFoundError do
|
32
|
-
@repository.load
|
39
|
+
@repository.load aggregate_id
|
33
40
|
end
|
34
41
|
end
|
35
42
|
|
36
43
|
should 'raise an exception if the loaded aggregate has an unexpected version' do
|
37
44
|
unit = @unit_factory.create
|
38
45
|
|
39
|
-
|
46
|
+
aggregate_id = SecureRandom.uuid
|
47
|
+
aggregate = TestMappedAggregate.new aggregate_id
|
40
48
|
aggregate.version = 5
|
41
49
|
|
42
|
-
mock(TestMappedAggregate).find(
|
50
|
+
mock(TestMappedAggregate).find(aggregate_id) do
|
43
51
|
aggregate
|
44
52
|
end
|
45
53
|
|
46
54
|
assert_raise ConflictingAggregateVersionError do
|
47
|
-
@repository.load
|
55
|
+
@repository.load aggregate_id, 4
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
should 'raise an exception while saving if lock could not be validated' do
|
60
|
+
unit = @unit_factory.create
|
61
|
+
|
62
|
+
aggregate_id = SecureRandom.uuid
|
63
|
+
aggregate = TestMappedAggregate.new aggregate_id
|
64
|
+
aggregate.version = 5
|
65
|
+
|
66
|
+
mock(TestMappedAggregate).find(aggregate_id) do
|
67
|
+
aggregate
|
68
|
+
end
|
69
|
+
|
70
|
+
@repository.load aggregate_id
|
71
|
+
|
72
|
+
mock(@lock_manager).validate_lock(aggregate) do
|
73
|
+
false
|
74
|
+
end
|
75
|
+
|
76
|
+
assert_raise ConcurrencyError do
|
77
|
+
unit.commit
|
48
78
|
end
|
49
79
|
end
|
50
80
|
|
51
81
|
should 'delete the aggregate if it has been marked for deletion' do
|
52
82
|
unit = @unit_factory.create
|
53
83
|
|
54
|
-
|
84
|
+
aggregate_id = SecureRandom.uuid
|
85
|
+
aggregate = TestMappedAggregate.new aggregate_id
|
55
86
|
aggregate.delete_this_thing
|
56
87
|
|
57
88
|
@repository.add aggregate
|
@@ -75,5 +106,6 @@ module Synapse
|
|
75
106
|
mark_deleted
|
76
107
|
end
|
77
108
|
end
|
109
|
+
|
78
110
|
end
|
79
111
|
end
|