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,31 @@
1
+ module Synapse
2
+ module Domain
3
+
4
+ class Person
5
+ include AggregateRoot
6
+
7
+ attr_reader :id, :name
8
+
9
+ def initialize(id, name)
10
+ @id, @name = id, name
11
+ end
12
+
13
+ def change_name(name)
14
+ @name = name
15
+ publish_event NameChangedEvent.new(id, name)
16
+ end
17
+
18
+ def delete
19
+ mark_deleted
20
+ end
21
+ end
22
+
23
+ class NameChangedEvent
24
+ attr_reader :id, :name
25
+ def initialize(id, name)
26
+ @id, @name = id, name
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,61 @@
1
+ require 'test_helper'
2
+
3
+ module Synapse
4
+ module Domain
5
+ class DomainEventMessageTest < Test::Unit::TestCase
6
+ def setup
7
+ @payload = OpenStruct.new
8
+ @aggregate_id = 123
9
+ @sequence_number = 1
10
+
11
+ @message = DomainEventMessage.build do |b|
12
+ b.payload = @payload
13
+ b.aggregate_id = @aggregate_id
14
+ b.sequence_number = @sequence_number
15
+ end
16
+ end
17
+
18
+ def test_initialize
19
+ # ensure empty fields were populated with default values
20
+ assert @message.id
21
+ assert @message.metadata
22
+ assert @message.timestamp
23
+
24
+ # ensure other fields were populated
25
+ assert_equal @payload, @message.payload
26
+ assert_equal @payload.class, @message.payload_type
27
+ assert_equal @aggregate_id, @message.aggregate_id
28
+ assert_equal @sequence_number, @message.sequence_number
29
+ end
30
+
31
+ def test_and_metadata
32
+ additional_metadata_a = { foo: 'bar' }
33
+ additional_metadata_b = { baz: 'qux' }
34
+
35
+ merged = @message.and_metadata additional_metadata_a
36
+ merged = merged.and_metadata additional_metadata_b
37
+
38
+ # Ensure everything was populated in the duplicate message
39
+ assert_equal @message.id, merged.id
40
+ assert_equal @message.payload, merged.payload
41
+ assert_equal @message.timestamp, merged.timestamp
42
+ assert_equal @message.aggregate_id, merged.aggregate_id
43
+ assert_equal @message.sequence_number, merged.sequence_number
44
+
45
+ # Now ensure that metadata was merged, not replaced
46
+ assert_equal additional_metadata_a.merge(additional_metadata_b), merged.metadata
47
+ end
48
+
49
+ def test_with_metadata
50
+ additional_metadata_a = { foo: 'bar' }
51
+ additional_metadata_b = { baz: 'qux' }
52
+
53
+ merged = @message.with_metadata additional_metadata_a
54
+ merged = merged.with_metadata additional_metadata_b
55
+
56
+ # Ensure that the metadata was replaced
57
+ assert_equal additional_metadata_b, merged.metadata
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,35 @@
1
+ require 'test_helper'
2
+
3
+ module Synapse
4
+ module Domain
5
+ class SimpleDomainEventStreamTest < Test::Unit::TestCase
6
+
7
+ def setup
8
+ @events = Array.new
9
+ @events.push DomainEventMessage.build
10
+ @events.push DomainEventMessage.build
11
+
12
+ @stream = SimpleDomainEventStream.new @events
13
+ end
14
+
15
+ def test_peek
16
+ assert_same @events.at(0), @stream.peek
17
+ assert_same @events.at(0), @stream.peek
18
+ end
19
+
20
+ def test_end_of_stream
21
+ @stream.next_event
22
+ @stream.next_event
23
+
24
+ assert_raise EndOfStreamError do
25
+ @stream.next_event
26
+ end
27
+
28
+ assert_raise EndOfStreamError do
29
+ @stream.peek
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+
3
+ module Synapse
4
+ class DuplicationRecorderTest < Test::Unit::TestCase
5
+ def setup
6
+ @recorder = DuplicationRecorder.new
7
+ @message = Message.build
8
+ end
9
+
10
+ def test_record
11
+ refute @recorder.recorded? @message
12
+
13
+ @recorder.record @message
14
+ assert @recorder.recorded? @message
15
+
16
+ assert_raise DuplicationError do
17
+ @recorder.record @message
18
+ end
19
+ end
20
+
21
+ def test_forget
22
+ @recorder.record @message
23
+ @recorder.forget @message
24
+
25
+ refute @recorder.recorded? @message
26
+ end
27
+
28
+ def test_forget_older_than
29
+ @recorder.record @message
30
+
31
+ threshold = 60 * 20 # 20 minutes
32
+
33
+ Timecop.freeze(Time.now + 3600) do
34
+ @recorder.forget_older_than Time.now - threshold
35
+ end
36
+
37
+ refute @recorder.recorded? @message
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,46 @@
1
+ require 'test_helper'
2
+
3
+ module Synapse
4
+ module EventBus
5
+
6
+ class WiringEventListenerTest < Test::Unit::TestCase
7
+ def test_notify
8
+ listener = ExampleWiringEventListener.new
9
+
10
+ event = Domain::EventMessage.build do |builder|
11
+ builder.payload = TestEvent.new
12
+ end
13
+
14
+ listener.notify event
15
+
16
+ assert listener.handled
17
+
18
+ event = Domain::EventMessage.build do |builder|
19
+ builder.payload = TestSubEvent.new
20
+ end
21
+
22
+ listener.notify event
23
+
24
+ assert listener.sub_handled
25
+ end
26
+ end
27
+
28
+ class TestEvent; end
29
+ class TestSubEvent < TestEvent; end
30
+
31
+ class ExampleWiringEventListener
32
+ include WiringEventListener
33
+
34
+ attr_accessor :handled, :sub_handled
35
+
36
+ wire TestEvent do |event|
37
+ @handled = true
38
+ end
39
+
40
+ wire TestSubEvent do |event|
41
+ @sub_handled = true
42
+ end
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,28 @@
1
+ require 'test_helper'
2
+
3
+ module Synapse
4
+ module EventSourcing
5
+
6
+ class GenericAggregateFactoryTest < Test::Unit::TestCase
7
+ def test_create_aggregate
8
+ factory = GenericAggregateFactory.new StubAggregate
9
+
10
+ event = Domain::DomainEventMessage.build do |m|
11
+ m.payload = StubCreatedEvent.new 123
12
+ end
13
+
14
+ snapshot = StubAggregate.new 123
15
+ snapshot_event = Domain::DomainEventMessage.build do |m|
16
+ m.payload = snapshot
17
+ end
18
+
19
+ aggregate_a = factory.create_aggregate(123, snapshot_event)
20
+ aggregate_b = factory.create_aggregate(123, event)
21
+
22
+ assert aggregate_a.equal? snapshot
23
+ assert aggregate_b.is_a? StubAggregate
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,76 @@
1
+ require 'test_helper'
2
+ require 'event_sourcing/fixtures'
3
+
4
+ module Synapse
5
+ module EventSourcing
6
+
7
+ class AggregateRootTest < Test::Unit::TestCase
8
+ def test_apply
9
+ stub = StubAggregate.new 123
10
+ stub.change_something
11
+ stub.change_something
12
+
13
+ assert_equal 123, stub.id
14
+ assert_equal 3, stub.uncommitted_event_count
15
+
16
+ assert_nil stub.version
17
+
18
+ stub.mark_committed
19
+
20
+ assert_equal 2, stub.version
21
+ end
22
+
23
+ def test_new_from_stream
24
+ events = Array.new
25
+ events.push create_event(123, 0, StubCreatedEvent.new(123))
26
+ events.push create_event(123, 1, StubChangedEvent.new)
27
+ events.push create_event(123, 2, StubChangedEvent.new)
28
+
29
+ stream = Domain::SimpleDomainEventStream.new events
30
+
31
+ aggregate = StubAggregate.new_from_stream stream
32
+
33
+ assert_equal 123, aggregate.id
34
+ assert_equal 2, aggregate.version
35
+ end
36
+
37
+ def test_initialize_fails_after_initialization
38
+ aggregate = StubAggregate.new 123
39
+
40
+ assert_raise RuntimeError do
41
+ aggregate.initialize_from_stream Domain::SimpleDomainEventStream.new
42
+ end
43
+ end
44
+
45
+ def test_child_entities
46
+ stub_entity_a = StubEntity.new
47
+ stub_entity_b = StubEntity.new
48
+
49
+ aggregate = StubAggregate.new 123
50
+ aggregate.stub_entity = stub_entity_a
51
+ aggregate.change_something
52
+
53
+ stub_entity_a.change_something
54
+
55
+ aggregate.stub_entities << stub_entity_b
56
+
57
+ aggregate.change_something
58
+
59
+ assert_equal 4, aggregate.event_count
60
+ assert_equal 3, stub_entity_a.event_count
61
+ assert_equal 1, stub_entity_b.event_count
62
+ end
63
+
64
+ private
65
+
66
+ def create_event(aggregate_id, seq, payload)
67
+ Domain::DomainEventMessage.build do |m|
68
+ m.aggregate_id = aggregate_id
69
+ m.sequence_number = seq
70
+ m.payload = payload
71
+ end
72
+ end
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,34 @@
1
+ require 'test_helper'
2
+ require 'event_sourcing/fixtures'
3
+
4
+ module Synapse
5
+ module EventSourcing
6
+
7
+ class EntityTest < Test::Unit::TestCase
8
+ def test_aggregate_root
9
+ entity = StubEntity.new
10
+ aggregate_a = StubAggregate.new 123
11
+ aggregate_b = StubAggregate.new 123
12
+
13
+ assert_raise RuntimeError do
14
+ entity.change_something
15
+ end
16
+
17
+ entity.aggregate_root = aggregate_a
18
+
19
+ assert_raise RuntimeError do
20
+ entity.aggregate_root = aggregate_b
21
+ end
22
+ end
23
+
24
+ def test_apply
25
+ entity = StubEntity.new
26
+
27
+ assert_raise RuntimeError do
28
+ entity.change_something
29
+ end
30
+ end
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,85 @@
1
+ module Synapse
2
+ module EventSourcing
3
+
4
+ class IncompatibleStubAggregate
5
+ include AggregateRoot
6
+
7
+ def change_something
8
+ apply StubChangedEvent.new
9
+ end
10
+ end
11
+
12
+ class StubAggregate
13
+ include AggregateRoot
14
+
15
+ attr_accessor :stub_entity, :stub_entities, :event_count
16
+ child_entity :stub_entity, :stub_entities
17
+
18
+ def initialize(id)
19
+ pre_initialize
20
+ apply StubCreatedEvent.new id
21
+ end
22
+
23
+ def change_something
24
+ apply StubChangedEvent.new
25
+ end
26
+
27
+ def delete_me
28
+ apply StubDeletedEvent.new
29
+ end
30
+
31
+ protected
32
+
33
+ def pre_initialize
34
+ @event_count = 0
35
+ @stub_entities = Array.new
36
+ end
37
+
38
+ def handle_event(event)
39
+ payload = event.payload
40
+ type = event.payload_type
41
+
42
+ if type.eql? StubCreatedEvent
43
+ @id = payload.id
44
+ elsif type.eql? StubDeletedEvent
45
+ mark_deleted
46
+ end
47
+
48
+ @event_count = @event_count.next
49
+ end
50
+ end
51
+
52
+ class StubEntity
53
+ include Entity
54
+
55
+ attr_reader :event_count
56
+
57
+ def initialize
58
+ @event_count = 0
59
+ end
60
+
61
+ def change_something
62
+ apply StubChangedEvent.new
63
+ end
64
+
65
+ protected
66
+
67
+ def handle_event(event)
68
+ @event_count = @event_count.next
69
+ end
70
+ end
71
+
72
+ class StubCreatedEvent
73
+ attr_reader :id
74
+
75
+ def initialize(id)
76
+ @id = id
77
+ end
78
+ end
79
+
80
+ class StubDeletedEvent; end
81
+
82
+ class StubChangedEvent; end
83
+
84
+ end
85
+ end