synapse-core 0.1.2

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.
Files changed (155) hide show
  1. data/lib/synapse.rb +351 -0
  2. data/lib/synapse/command/command_bus.rb +45 -0
  3. data/lib/synapse/command/command_callback.rb +18 -0
  4. data/lib/synapse/command/command_filter.rb +17 -0
  5. data/lib/synapse/command/command_handler.rb +13 -0
  6. data/lib/synapse/command/dispatch_interceptor.rb +16 -0
  7. data/lib/synapse/command/duplication.rb +43 -0
  8. data/lib/synapse/command/errors.rb +27 -0
  9. data/lib/synapse/command/filters/validation.rb +32 -0
  10. data/lib/synapse/command/gateway.rb +34 -0
  11. data/lib/synapse/command/interceptor_chain.rb +31 -0
  12. data/lib/synapse/command/interceptors/serialization.rb +35 -0
  13. data/lib/synapse/command/message.rb +19 -0
  14. data/lib/synapse/command/rollback_policy.rb +22 -0
  15. data/lib/synapse/command/simple_command_bus.rb +138 -0
  16. data/lib/synapse/command/wiring.rb +47 -0
  17. data/lib/synapse/domain/aggregate_root.rb +121 -0
  18. data/lib/synapse/domain/event_container.rb +127 -0
  19. data/lib/synapse/domain/message.rb +82 -0
  20. data/lib/synapse/domain/message_builder.rb +34 -0
  21. data/lib/synapse/domain/stream.rb +108 -0
  22. data/lib/synapse/duplication.rb +60 -0
  23. data/lib/synapse/errors.rb +13 -0
  24. data/lib/synapse/event_bus/event_bus.rb +40 -0
  25. data/lib/synapse/event_bus/event_listener.rb +16 -0
  26. data/lib/synapse/event_bus/event_listener_proxy.rb +12 -0
  27. data/lib/synapse/event_bus/simple_event_bus.rb +69 -0
  28. data/lib/synapse/event_bus/wiring.rb +23 -0
  29. data/lib/synapse/event_sourcing/aggregate_factory.rb +69 -0
  30. data/lib/synapse/event_sourcing/aggregate_root.rb +104 -0
  31. data/lib/synapse/event_sourcing/conflict_resolver.rb +80 -0
  32. data/lib/synapse/event_sourcing/entity.rb +64 -0
  33. data/lib/synapse/event_sourcing/member.rb +72 -0
  34. data/lib/synapse/event_sourcing/repository.rb +119 -0
  35. data/lib/synapse/event_sourcing/snapshot/count_stream.rb +86 -0
  36. data/lib/synapse/event_sourcing/snapshot/count_trigger.rb +91 -0
  37. data/lib/synapse/event_sourcing/snapshot/taker.rb +73 -0
  38. data/lib/synapse/event_sourcing/storage_listener.rb +34 -0
  39. data/lib/synapse/event_sourcing/stream_decorator.rb +25 -0
  40. data/lib/synapse/event_store/errors.rb +16 -0
  41. data/lib/synapse/event_store/event_store.rb +43 -0
  42. data/lib/synapse/event_store/in_memory.rb +59 -0
  43. data/lib/synapse/event_store/mongo/cursor_event_stream.rb +63 -0
  44. data/lib/synapse/event_store/mongo/event_store.rb +86 -0
  45. data/lib/synapse/event_store/mongo/per_commit_strategy.rb +253 -0
  46. data/lib/synapse/event_store/mongo/per_event_strategy.rb +143 -0
  47. data/lib/synapse/event_store/mongo/storage_strategy.rb +113 -0
  48. data/lib/synapse/event_store/mongo/template.rb +73 -0
  49. data/lib/synapse/identifier.rb +23 -0
  50. data/lib/synapse/message.rb +101 -0
  51. data/lib/synapse/message_builder.rb +38 -0
  52. data/lib/synapse/process_manager/correlation.rb +32 -0
  53. data/lib/synapse/process_manager/correlation_resolver.rb +14 -0
  54. data/lib/synapse/process_manager/correlation_set.rb +58 -0
  55. data/lib/synapse/process_manager/process.rb +71 -0
  56. data/lib/synapse/repository/errors.rb +26 -0
  57. data/lib/synapse/repository/lock_manager.rb +40 -0
  58. data/lib/synapse/repository/locking.rb +97 -0
  59. data/lib/synapse/repository/pessimistic_lock_manager.rb +61 -0
  60. data/lib/synapse/repository/repository.rb +109 -0
  61. data/lib/synapse/serialization/converter.rb +39 -0
  62. data/lib/synapse/serialization/converter/chain.rb +45 -0
  63. data/lib/synapse/serialization/converter/factory.rb +68 -0
  64. data/lib/synapse/serialization/converter/identity.rb +29 -0
  65. data/lib/synapse/serialization/converter/json.rb +31 -0
  66. data/lib/synapse/serialization/converter/ox.rb +31 -0
  67. data/lib/synapse/serialization/errors.rb +12 -0
  68. data/lib/synapse/serialization/lazy_object.rb +61 -0
  69. data/lib/synapse/serialization/message/data.rb +25 -0
  70. data/lib/synapse/serialization/message/metadata.rb +13 -0
  71. data/lib/synapse/serialization/message/serialization_aware.rb +17 -0
  72. data/lib/synapse/serialization/message/serialization_aware_message.rb +66 -0
  73. data/lib/synapse/serialization/message/serialized_message.rb +201 -0
  74. data/lib/synapse/serialization/message/serialized_message_builder.rb +64 -0
  75. data/lib/synapse/serialization/message/serialized_object_cache.rb +50 -0
  76. data/lib/synapse/serialization/message/serializer.rb +47 -0
  77. data/lib/synapse/serialization/revision_resolver.rb +30 -0
  78. data/lib/synapse/serialization/serialized_object.rb +37 -0
  79. data/lib/synapse/serialization/serialized_type.rb +31 -0
  80. data/lib/synapse/serialization/serializer.rb +98 -0
  81. data/lib/synapse/serialization/serializer/marshal.rb +32 -0
  82. data/lib/synapse/serialization/serializer/oj.rb +34 -0
  83. data/lib/synapse/serialization/serializer/ox.rb +31 -0
  84. data/lib/synapse/uow/factory.rb +28 -0
  85. data/lib/synapse/uow/listener.rb +79 -0
  86. data/lib/synapse/uow/listener_collection.rb +93 -0
  87. data/lib/synapse/uow/nesting.rb +262 -0
  88. data/lib/synapse/uow/provider.rb +71 -0
  89. data/lib/synapse/uow/storage_listener.rb +14 -0
  90. data/lib/synapse/uow/transaction_manager.rb +27 -0
  91. data/lib/synapse/uow/uow.rb +178 -0
  92. data/lib/synapse/upcasting/chain.rb +78 -0
  93. data/lib/synapse/upcasting/context.rb +58 -0
  94. data/lib/synapse/upcasting/data.rb +30 -0
  95. data/lib/synapse/upcasting/single_upcaster.rb +57 -0
  96. data/lib/synapse/upcasting/upcaster.rb +55 -0
  97. data/lib/synapse/version.rb +3 -0
  98. data/lib/synapse/wiring/message_wiring.rb +41 -0
  99. data/lib/synapse/wiring/wire.rb +55 -0
  100. data/lib/synapse/wiring/wire_registry.rb +61 -0
  101. data/test/command/duplication_test.rb +54 -0
  102. data/test/command/gateway_test.rb +25 -0
  103. data/test/command/interceptor_chain_test.rb +26 -0
  104. data/test/command/serialization_test.rb +37 -0
  105. data/test/command/simple_command_bus_test.rb +141 -0
  106. data/test/command/validation_test.rb +42 -0
  107. data/test/command/wiring_test.rb +73 -0
  108. data/test/domain/aggregate_root_test.rb +57 -0
  109. data/test/domain/fixtures.rb +31 -0
  110. data/test/domain/message_test.rb +61 -0
  111. data/test/domain/stream_test.rb +35 -0
  112. data/test/duplication_test.rb +40 -0
  113. data/test/event_bus/wiring_test.rb +46 -0
  114. data/test/event_sourcing/aggregate_factory_test.rb +28 -0
  115. data/test/event_sourcing/aggregate_root_test.rb +76 -0
  116. data/test/event_sourcing/entity_test.rb +34 -0
  117. data/test/event_sourcing/fixtures.rb +85 -0
  118. data/test/event_sourcing/repository_test.rb +102 -0
  119. data/test/event_sourcing/snapshot/aggregate_taker_test.rb +39 -0
  120. data/test/event_sourcing/snapshot/deferred_taker_test.rb +19 -0
  121. data/test/event_sourcing/snapshot/integration_test.rb +65 -0
  122. data/test/event_sourcing/storage_listener_test.rb +77 -0
  123. data/test/event_store/in_memory_test.rb +47 -0
  124. data/test/process_manager/correlation_set_test.rb +49 -0
  125. data/test/process_manager/correlation_test.rb +24 -0
  126. data/test/process_manager/process_test.rb +52 -0
  127. data/test/repository/locking_test.rb +101 -0
  128. data/test/serialization/converter/factory_test.rb +33 -0
  129. data/test/serialization/converter/identity_test.rb +17 -0
  130. data/test/serialization/converter/json_test.rb +31 -0
  131. data/test/serialization/converter/ox_test.rb +40 -0
  132. data/test/serialization/fixtures.rb +17 -0
  133. data/test/serialization/lazy_object_test.rb +32 -0
  134. data/test/serialization/message/metadata_test.rb +19 -0
  135. data/test/serialization/message/serialization_aware_message_test.rb +88 -0
  136. data/test/serialization/message/serialized_message_builder_test.rb +41 -0
  137. data/test/serialization/message/serialized_message_test.rb +140 -0
  138. data/test/serialization/message/serializer_test.rb +50 -0
  139. data/test/serialization/revision_resolver_test.rb +12 -0
  140. data/test/serialization/serialized_object_test.rb +36 -0
  141. data/test/serialization/serialized_type_test.rb +27 -0
  142. data/test/serialization/serializer/marshal_test.rb +22 -0
  143. data/test/serialization/serializer/oj_test.rb +24 -0
  144. data/test/serialization/serializer/ox_test.rb +36 -0
  145. data/test/serialization/serializer_test.rb +20 -0
  146. data/test/test_helper.rb +19 -0
  147. data/test/uow/factory_test.rb +23 -0
  148. data/test/uow/outer_commit_listener_test.rb +50 -0
  149. data/test/uow/provider_test.rb +70 -0
  150. data/test/uow/uow_test.rb +337 -0
  151. data/test/upcasting/chain_test.rb +29 -0
  152. data/test/upcasting/fixtures.rb +66 -0
  153. data/test/wiring/wire_registry_test.rb +60 -0
  154. data/test/wiring/wire_test.rb +51 -0
  155. metadata +263 -0
@@ -0,0 +1,262 @@
1
+ module Synapse
2
+ module UnitOfWork
3
+ # Partial implementation of a unit of work that can be nested
4
+ #
5
+ # This implementation provides common actions that will be needed for nestable units
6
+ # of work, such as registration with the unit of work provider and nested commit
7
+ # and rollback operations
8
+ #
9
+ # @abstract
10
+ class NestableUnitOfWork
11
+ # @param [UnitOfWorkProvider] provider
12
+ # @return [undefined]
13
+ def initialize(provider)
14
+ @inner_units = Array.new
15
+ @provider = provider
16
+ @started = false
17
+ end
18
+
19
+ # Commits this unit of work
20
+ #
21
+ # All reigstered aggregates that have not been registered as stored are saved in their
22
+ # respective repositories, buffered events are sent to their respective event buses, and
23
+ # all registered listeners are notified of the commit.
24
+ #
25
+ # After the commit (successful or not), the unit of work is unregistered and cleans up any
26
+ # resources it acquired. The effectively means that a rollback is done if the unit of work
27
+ # failed to commit.
28
+ #
29
+ # @raise [RuntimeError] If unit of work hasn't been started yet
30
+ # @return [undefined]
31
+ def commit
32
+ unless started?
33
+ raise 'Unit of work has not been started yet'
34
+ end
35
+
36
+ begin
37
+ notify_prepare_commit
38
+ store_aggregates
39
+
40
+ unless @outer_unit
41
+ perform_commit
42
+ stop
43
+ perform_cleanup
44
+ end
45
+ rescue => cause
46
+ perform_rollback cause
47
+ stop
48
+
49
+ unless @outer_unit
50
+ perform_cleanup
51
+ end
52
+
53
+ raise cause
54
+ ensure
55
+ clear
56
+ end
57
+ end
58
+
59
+ # Clears this unit of work of any buffered changes
60
+ #
61
+ # Any buffered events and registered aggregates are discarded and any registered unit of work
62
+ # listeners are notified of the rollback.
63
+ #
64
+ # @param [Error] cause
65
+ # @return [undefined]
66
+ def rollback(cause = nil)
67
+ begin
68
+ if started?
69
+ @inner_units.each do |inner_unit|
70
+ @provider.push inner_unit
71
+ inner_unit.rollback cause
72
+ end
73
+ perform_rollback cause
74
+ end
75
+ ensure
76
+ finalize_rollback
77
+ end
78
+ end
79
+
80
+ # Starts the unit of work, preparing it for aggregate registration
81
+ #
82
+ # @raise [RuntimeError] If unit of work has already been started
83
+ # @return [undefined]
84
+ def start
85
+ if started?
86
+ raise 'Unit of work has already been started'
87
+ end
88
+
89
+ perform_start
90
+
91
+ if @provider.started?
92
+ # This is a nested unit of work
93
+ @outer_unit = @provider.current
94
+
95
+ if NestableUnitOfWork === @outer_unit
96
+ @outer_unit.register_inner_unit self
97
+ else
98
+ # Outer unit is not aware of inner units, hook in with a listener
99
+ @outer_unit.register_listener OuterCommitUnitOfWorkListener.new self, @provider
100
+ end
101
+ end
102
+
103
+ @provider.push self
104
+ @started = true
105
+ end
106
+
107
+ # Returns true if this unit of work has been started
108
+ # @return [Boolean]
109
+ def started?
110
+ @started
111
+ end
112
+
113
+ protected
114
+
115
+ # Executes logic required to commit this unit of work
116
+ #
117
+ # @abstract
118
+ # @return [undefined]
119
+ def perform_commit; end
120
+
121
+ # Executes logic required to rollback this unit of work
122
+ #
123
+ # @abstract
124
+ # @param [Error] cause
125
+ # @return [undefined]
126
+ def perform_rollback(cause = nil); end
127
+
128
+ # Notifies listeners that this unit of work is cleaning up
129
+ #
130
+ # @abstract
131
+ # @return [undefined]
132
+ def notify_cleanup; end
133
+
134
+ # Notifies listeners that this unit of work is preparing to be committed
135
+ #
136
+ # @abstract
137
+ # @return [undefined]
138
+ def notify_prepare_commit; end
139
+
140
+ # Executes logic required when starting this unit of work
141
+ #
142
+ # @abstract
143
+ # @return [undefined]
144
+ def perform_start; end
145
+
146
+ # Storages aggregates registered with this unit of work
147
+ #
148
+ # @abstract
149
+ # @return [undefined]
150
+ def store_aggregates; end
151
+
152
+ # Commits all registered inner units of work. This should be invoked after events have been
153
+ # dispatched and before any listeners are notified of the commit.
154
+ #
155
+ # @return [undefined]
156
+ def commit_inner_units
157
+ @inner_units.each do |inner_unit|
158
+ if inner_unit.started?
159
+ inner_unit.perform_inner_commit
160
+ end
161
+ end
162
+ end
163
+
164
+ # Registers a unit of work nested in this unit of work
165
+ #
166
+ # @private
167
+ # @param [UnitOfWork] inner_unit
168
+ # @return [undefined]
169
+ def register_inner_unit(inner_unit)
170
+ @inner_units.push inner_unit
171
+ end
172
+
173
+ # Executes logic required to clean up this unit of work
174
+ #
175
+ # @private
176
+ # @return [undefined]
177
+ def perform_cleanup
178
+ @inner_units.each do |inner_unit|
179
+ inner_unit.perform_cleanup
180
+ end
181
+
182
+ notify_cleanup
183
+ end
184
+
185
+ # Commits this unit of work as an inner unit of work
186
+ #
187
+ # @private
188
+ # @return [undefined]
189
+ def perform_inner_commit
190
+ @provider.push self
191
+
192
+ begin
193
+ perform_commit
194
+ rescue => cause
195
+ perform_rollback cause
196
+ end
197
+
198
+ clear
199
+ stop
200
+ end
201
+
202
+ private
203
+
204
+ # @return [undefined]
205
+ def finalize_rollback
206
+ unless @outer_unit
207
+ perform_cleanup
208
+ end
209
+
210
+ clear
211
+ stop
212
+ end
213
+
214
+ # @return [undefined]
215
+ def clear
216
+ @provider.clear self
217
+ end
218
+
219
+ # @return [undefined]
220
+ def stop
221
+ @started = false
222
+ end
223
+ end
224
+
225
+ # Listener that allows a nested unit of work to properly operate within in a unit of
226
+ # work that is not aware of nesting
227
+ class OuterCommitUnitOfWorkListener < UnitOfWorkListener
228
+ # @param [UnitOfWork] inner_unit
229
+ # @param [UnitOfWorkProvider] provider
230
+ # @return [undefined]
231
+ def initialize(inner_unit, provider)
232
+ @inner_unit = inner_unit
233
+ @provider = provider
234
+ end
235
+
236
+ # @param [UnitOfWork] outer_unit
237
+ # @return [undefined]
238
+ def after_commit(outer_unit)
239
+ @inner_unit.perform_inner_commit
240
+ end
241
+
242
+ # @param [UnitOfWork] outer_unit
243
+ # @param [Error] cause
244
+ # @return [undefined]
245
+ def on_rollback(outer_unit, cause = nil)
246
+ @provider.push @inner_unit
247
+
248
+ begin
249
+ @inner_unit.perform_rollback cause
250
+ ensure
251
+ @provider.clear @inner_unit
252
+ end
253
+ end
254
+
255
+ # @param [UnitOfWork] outer_unit
256
+ # @return [undefined]
257
+ def on_cleanup(outer_unit)
258
+ @inner_unit.perform_cleanup
259
+ end
260
+ end
261
+ end
262
+ end
@@ -0,0 +1,71 @@
1
+ module Synapse
2
+ module UnitOfWork
3
+ # Entry point for components to access units of work. Components managing transactional
4
+ # boundaries can register and clear unit of work instances.
5
+ class UnitOfWorkProvider
6
+ def initialize
7
+ @threads = Hash.new
8
+ end
9
+
10
+ # Clears the given unit of work from this provider
11
+ #
12
+ # If the given unit of work is not known to the provider, or it is not the active unit
13
+ # of work, then this method will raise an exception.
14
+ #
15
+ # @param [UnitOfWork] unit
16
+ # @return [undefined]
17
+ def clear(unit)
18
+ unless stack.last == unit
19
+ raise ArgumentError, 'The given unit of work is not the active unit of work'
20
+ end
21
+
22
+ stack.pop
23
+ end
24
+
25
+ # Commits the current unit of work
26
+ # @return [undefined]
27
+ def commit
28
+ current.commit
29
+ end
30
+
31
+ # Returns the current unit of work if one is set
32
+ #
33
+ # @raise [RuntimeError] If no unit of work is active
34
+ # @return [UnitOfWork]
35
+ def current
36
+ if stack.empty?
37
+ raise 'No unit of work is active'
38
+ end
39
+
40
+ stack.last
41
+ end
42
+
43
+
44
+ # Pushes the given unit of work onto the top of the stack, making it the active unit of work
45
+ #
46
+ # If there are other units of work bound to this provider, they will be held until the given
47
+ # unit of work is cleared.
48
+ #
49
+ # @param [UnitOfWork] unit
50
+ # @return [undefined]
51
+ def push(unit)
52
+ stack.push unit
53
+ end
54
+
55
+ # Returns true if there is an active unit of work
56
+ # @return [Boolean]
57
+ def started?
58
+ !stack.empty?
59
+ end
60
+
61
+ private
62
+
63
+ # @return [Array<UnitOfWork>]
64
+ def stack
65
+ @threads.fetch Thread.current
66
+ rescue KeyError
67
+ @threads.store Thread.current, Array.new
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,14 @@
1
+ module Synapse
2
+ module UnitOfWork
3
+ # Represents a mechanism for a unit of work to commit an aggregate to an underlying store
4
+ # @abstract
5
+ class StorageListener
6
+ # Commits the given aggregate to the underlying storage mechanism
7
+ #
8
+ # @abstract
9
+ # @param [AggregateRoot] aggregate
10
+ # @return [undefined]
11
+ def store(aggregate); end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,27 @@
1
+ module Synapse
2
+ module UnitOfWork
3
+ # Represents a mechanism for a unit of work to integrate with an underlying transaction
4
+ # management system
5
+ #
6
+ # @abstract
7
+ class TransactionManager
8
+ # Creates and returns a transaction for use by the unit of work
9
+ #
10
+ # @abstract
11
+ # @return [Object]
12
+ def start; end
13
+
14
+ # Commits the given transaction
15
+ #
16
+ # @param [Object] transaction
17
+ # @return [undefined]
18
+ def commit(transaction); end
19
+
20
+ # Rolls back the given transaction
21
+ #
22
+ # @param [Object] transaction
23
+ # @return [undefined]
24
+ def rollback(transaction); end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,178 @@
1
+ module Synapse
2
+ module UnitOfWork
3
+ # Default implementation of a unit of work
4
+ class UnitOfWork < NestableUnitOfWork
5
+ # @param [UnitOfWorkProvider] provider
6
+ # @return [undefined]
7
+ def initialize(provider)
8
+ super
9
+
10
+ @aggregates = Hash.new
11
+ @events = Hash.new
12
+ @listeners = UnitOfWorkListenerCollection.new
13
+ end
14
+
15
+ # Returns true if this unit of work is bound to a transaction
16
+ # @return [Boolean]
17
+ def transactional?
18
+ !!@transaction_manager
19
+ end
20
+
21
+ # Registers a listener that is notified of state changes in this unit of work
22
+ #
23
+ # @param [UnitOfWorkListener] listener
24
+ # @return [undefined]
25
+ def register_listener(listener)
26
+ @listeners.push listener
27
+ end
28
+
29
+ # Registers an aggregate with this unit of work
30
+ #
31
+ # This unit of work adds an event listener to the aggregate so that any events generated
32
+ # are published to the given event bus when this unit of work is committed.
33
+ #
34
+ # The provided storage listener is used to commit the aggregate to its respective
35
+ # underlying storage mechanism.
36
+ #
37
+ # If there is already an aggregate registered with this unit of work of the same type
38
+ # and with the same identifier, that aggregate will be returned instead of the given
39
+ # aggregate.
40
+ #
41
+ # @param [AggregateRoot] aggregate
42
+ # @param [EventBus] event_bus
43
+ # @param [StorageListener] storage_listener
44
+ # @return [AggregateRoot]
45
+ def register_aggregate(aggregate, event_bus, storage_listener)
46
+ similar = find_similar_aggregate aggregate
47
+ if similar
48
+ return similar
49
+ end
50
+
51
+ aggregate.add_registration_listener do |event|
52
+ publish_event event, event_bus
53
+ end
54
+
55
+ @aggregates.store aggregate, storage_listener
56
+
57
+ aggregate
58
+ end
59
+
60
+ # Buffers an event for publication to the given event bus until this unit of work is
61
+ # committed
62
+ #
63
+ # @param [EventMessage] event
64
+ # @param [EventBus] event_bus
65
+ # @return [EventMessage] The event that will be published to the event bus
66
+ def publish_event(event, event_bus)
67
+ event = @listeners.on_event_registered self, event
68
+
69
+ begin
70
+ events = @events.fetch event_bus
71
+ rescue KeyError
72
+ events = @events.store event_bus, Array.new
73
+ end
74
+
75
+ events.push event
76
+ event
77
+ end
78
+
79
+ # Sets the transaction manager that will be used by this unit of work
80
+ #
81
+ # @raise [RuntimeError] If unit of work has been started
82
+ # @param [TransactionManager] transaction_manager
83
+ # @return [undefined]
84
+ def transaction_manager=(transaction_manager)
85
+ if started?
86
+ raise 'Transaction manager not permitted to change after unit of work has started'
87
+ end
88
+
89
+ @transaction_manager = transaction_manager
90
+ end
91
+
92
+ protected
93
+
94
+ # @return [undefined]
95
+ def perform_commit
96
+ publish_events
97
+ commit_inner_units
98
+
99
+ if transactional?
100
+ @listeners.on_prepare_transaction_commit self, @transaction
101
+ @transaction_manager.commit @transaction
102
+ end
103
+
104
+ @listeners.after_commit self
105
+ end
106
+
107
+ # @param [Error] cause
108
+ # @return [undefined]
109
+ def perform_rollback(cause = nil)
110
+ @aggregates.clear
111
+ @events.clear
112
+
113
+ begin
114
+ if @transaction
115
+ @transaction_manager.rollback @transaction
116
+ end
117
+ ensure
118
+ @listeners.on_rollback self, cause
119
+ end
120
+ end
121
+
122
+ # @return [undefined]
123
+ def notify_cleanup
124
+ @listeners.on_cleanup self
125
+ end
126
+
127
+ # @return [undefined]
128
+ def notify_prepare_commit
129
+ @listeners.on_prepare_commit self, @aggregates.keys, @events
130
+ end
131
+
132
+ # @return [undefined]
133
+ def perform_start
134
+ if transactional?
135
+ @transaction = @transaction_manager.start
136
+ end
137
+
138
+ @listeners.on_start self
139
+ end
140
+
141
+ # @return [undefined]
142
+ def store_aggregates
143
+ @aggregates.each_pair do |aggregate, storage_listener|
144
+ storage_listener.store aggregate
145
+ end
146
+ @aggregates.clear
147
+ end
148
+
149
+ private
150
+
151
+ # Checks if an aggregate of the same type and identifier as the given aggregate has been
152
+ # previously registered with this unit work. If one is found, it is returned.
153
+ #
154
+ # @param [AggregateRoot] aggregate
155
+ # @return [AggregateRoot] Returns nil if no similar aggregate was found
156
+ def find_similar_aggregate(aggregate)
157
+ @aggregates.each_key do |candidate|
158
+ if aggregate.class === candidate and aggregate.id == candidate.id
159
+ return candidate
160
+ end
161
+ end
162
+
163
+ return
164
+ end
165
+
166
+ # Continually publishes all buffered events to their respective event buses until all
167
+ # events have been published
168
+ def publish_events
169
+ until @events.empty?
170
+ @events.keys.each do |event_bus|
171
+ events = @events.delete event_bus
172
+ event_bus.publish events
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end