synapse-core 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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,109 @@
1
+ module Synapse
2
+ module Repository
3
+ # Represents a mechanism for loading and storing aggregates
4
+ # @abstract
5
+ class Repository
6
+ # @return [EventBus]
7
+ attr_accessor :event_bus
8
+
9
+ # @return [UnitOfWorkProvider]
10
+ attr_accessor :unit_provider
11
+
12
+ # Loads an aggregate with the given aggregate identifier
13
+ #
14
+ # If an expected version is specified and the aggregate's actual version doesn't equal the
15
+ # expected version, the implementation can choose to do one of the following:
16
+ #
17
+ # - Raise an exception immediately
18
+ # - Raise an exception at any other time while the aggregate is registered with the current
19
+ # unit of work.
20
+ #
21
+ # @abstract
22
+ # @raise [AggregateNotFoundError]
23
+ # If the aggregate with the given identifier could not be found
24
+ # @raise [ConflictingModificationError]
25
+ # If the expected version doesn't match the aggregate's actual version
26
+ # @param [Object] aggregate_id
27
+ # @param [Integer] expected_version If this is nil, no version validation is performed
28
+ # @return [AggregateRoot]
29
+ def load(aggregate_id, expected_version = nil); end
30
+
31
+ # Adds a new, unmanaged aggregate to the repository
32
+ #
33
+ # This method will not force the repository to save the aggregate immediately. Instead, it is
34
+ # registered with the current unit of work. To force storage of an aggregate, commit the
35
+ # current unit of work.
36
+ #
37
+ # @abstract
38
+ # @raise [ArgumentError] If the version of the aggregate is not null
39
+ # @param [AggregateRoot] aggregate
40
+ # @return [undefined]
41
+ def add(aggregate); end
42
+
43
+ protected
44
+
45
+ # Returns the type of aggregate that this repository handles
46
+ #
47
+ # @abstract
48
+ # @return [Class]
49
+ def aggregate_type; end
50
+
51
+ # Returns the listener that handles aggregate storage
52
+ #
53
+ # @abstract
54
+ # @return [StorageListener]
55
+ def storage_listener; end
56
+
57
+ # Asserts that an aggregate being added is compatible with this repository and is newly
58
+ # created
59
+ #
60
+ # @raise [ArgumentError] If aggregate is not of the correct type
61
+ # @raise [ArgumentError] If aggregate has a version number
62
+ # @param [AggregateRoot] aggregate
63
+ # @return [undefined]
64
+ def assert_compatible(aggregate)
65
+ unless aggregate.is_a? aggregate_type
66
+ raise ArgumentError, 'Incompatible aggregate type'
67
+ end
68
+
69
+ if aggregate.version
70
+ raise ArgumentError, 'Only newly created aggregates may be added'
71
+ end
72
+ end
73
+
74
+ # Asserts that a loaded aggregate has the expected version
75
+ #
76
+ # @raise [ConflictingAggregateVersionError]
77
+ # If aggregate version is later than the expected version
78
+ # @param [AggregateRoot] aggregate
79
+ # @param [Integer] expected_version
80
+ # @return [undefined]
81
+ def assert_version_expected(aggregate, expected_version)
82
+ if expected_version and aggregate.version and aggregate.version > expected_version
83
+ raise ConflictingAggregateVersionError.new aggregate, expected_version
84
+ end
85
+ end
86
+
87
+ # Registers the given aggregate with the current unit of work
88
+ #
89
+ # @param [AggregateRoot] aggregate
90
+ # @return [undefined]
91
+ def register_aggregate(aggregate)
92
+ current_unit.register_aggregate aggregate, @event_bus, storage_listener
93
+ end
94
+
95
+ # Registers the given unit of work listener with the current unit of work
96
+ #
97
+ # @param [UnitOfWorkListener] listener
98
+ # @return [undefined]
99
+ def register_listener(listener)
100
+ current_unit.register_listener listener
101
+ end
102
+
103
+ # @return [UnitOfWork]
104
+ def current_unit
105
+ @unit_provider.current
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,39 @@
1
+ module Synapse
2
+ module Serialization
3
+ # Represents a mechanism for converting content of one type to another type for the purposes
4
+ # of serialization, deserialization and upcasting.
5
+ module Converter
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :source_type
10
+ class_attribute :target_type
11
+ end
12
+
13
+ module ClassMethods
14
+ # @param [Class] source_type
15
+ # @param [Class] target_type
16
+ # @return [undefined]
17
+ def converts(source_type, target_type)
18
+ self.source_type = source_type
19
+ self.target_type = target_type
20
+ end
21
+ end
22
+
23
+ def initialize(options = {})
24
+ @options = options
25
+ end
26
+
27
+ # @param [SerializedObject] original
28
+ # @return [SerializedObject]
29
+ def convert(original)
30
+ SerializedObject.new(convert_content(original.content), target_type, original.type)
31
+ end
32
+
33
+ # @abstract
34
+ # @param [Object] original
35
+ # @return [Object]
36
+ def convert_content(original); end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,45 @@
1
+ module Synapse
2
+ module Serialization
3
+ # Converter implementation that chains together two or more converters
4
+ class ConverterChain
5
+ include Converter
6
+
7
+ # @return [Array<Converter>]
8
+ attr_reader :delegates
9
+
10
+ # @return [Class]
11
+ attr_reader :source_type
12
+
13
+ # @return [Class]
14
+ attr_reader :target_type
15
+
16
+ # @param [Array<Converter>] delegates
17
+ # @return [undefined]
18
+ def initialize(delegates)
19
+ @delegates = delegates
20
+ @source_type = delegates.first.source_type
21
+ @target_type = delegates.last.target_type
22
+ end
23
+
24
+ # @param [SerializedObject] original
25
+ # @return [SerializedObject]
26
+ def convert(original)
27
+ intermediate = original
28
+ @delegates.each do |delegate|
29
+ intermediate = delegate.convert intermediate
30
+ end
31
+ intermediate
32
+ end
33
+
34
+ # @param [Object] original
35
+ # @return [Object]
36
+ def convert_content(original)
37
+ intermediate = original
38
+ @delegates.each do |delegate|
39
+ intermediate = delegate.convert_content intermediate
40
+ end
41
+ intermediate
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,68 @@
1
+ module Synapse
2
+ module Serialization
3
+ # Represents a mechanism for storing and retrieving converters capable of converting content
4
+ # of one type to another type, for the purpose of serialization and upcasting.
5
+ class ConverterFactory
6
+ attr_reader :converters
7
+
8
+ def initialize
9
+ @converters = Array.new
10
+ end
11
+
12
+ # Adds the given converter to this converter factory
13
+ #
14
+ # @param [Converter] converter
15
+ # @return [undefined]
16
+ def register(converter)
17
+ @converters.push converter
18
+ end
19
+
20
+ # Convenience method for converting a given serialized object to the given target type
21
+ #
22
+ # @param [SerializedObject] serialized_object
23
+ # @param [Class] target_type
24
+ # @return [SerializedObject]
25
+ def convert(serialized_object, target_type)
26
+ converter = converter serialized_object.content_type, target_type
27
+ converter.convert serialized_object
28
+ end
29
+
30
+ # Returns a converter that is capable of converting content of the given source type to
31
+ # the given target type, if one exists.
32
+ #
33
+ # @raise [ConversionError] If no converter is capable of performing the conversion
34
+ # @param [Class] source_type
35
+ # @param [Class] target_type
36
+ # @return [Converter]
37
+ def converter(source_type, target_type)
38
+ if source_type == target_type
39
+ return IdentityConverter.new source_type
40
+ end
41
+
42
+ @converters.each do |converter|
43
+ if converter.source_type == source_type && converter.target_type == target_type
44
+ return converter
45
+ end
46
+ end
47
+
48
+ raise ConversionError, 'No converter capable of [%s] -> [%s]' % [source_type, target_type]
49
+ end
50
+
51
+ # Returns true if this factory contains a converter capable of converting content from the
52
+ # given source type to the given target type.
53
+ #
54
+ # @param [Class] source_type
55
+ # @param [Class] target_type
56
+ # @return [Boolean]
57
+ def has_converter?(source_type, target_type)
58
+ if source_type == target_type
59
+ return true
60
+ end
61
+
62
+ @converters.any? do |converter|
63
+ converter.source_type == source_type && converter.target_type == target_type
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,29 @@
1
+ module Synapse
2
+ module Serialization
3
+ # Implementation of a converter that does no conversion
4
+ class IdentityConverter
5
+ include Converter
6
+
7
+ attr_reader :source_type
8
+ attr_reader :target_type
9
+
10
+ # @param [Class] type
11
+ # @return [undefined]
12
+ def initialize(type)
13
+ @source_type = @target_type = type
14
+ end
15
+
16
+ # @param [SerializedObject] original
17
+ # @return [SerializedObject]
18
+ def convert(original)
19
+ original
20
+ end
21
+
22
+ # @param [Object] original
23
+ # @return [Object]
24
+ def convert_content(original)
25
+ original
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ require 'json'
2
+
3
+ module Synapse
4
+ module Serialization
5
+ # Converter that converts a JSON string into a Ruby data structure
6
+ class JsonToObjectConverter
7
+ include Converter
8
+
9
+ converts String, Object
10
+
11
+ # @param [Object] original
12
+ # @return [Object]
13
+ def convert_content(original)
14
+ JSON.parse original, @options
15
+ end
16
+ end
17
+
18
+ # Converter that converts a Ruby data structure into a JSON string
19
+ class ObjectToJsonConverter
20
+ include Converter
21
+
22
+ converts Object, String
23
+
24
+ # @param [Object] original
25
+ # @return [Object]
26
+ def convert_content(original)
27
+ JSON.generate original, @options
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ require 'ox'
2
+
3
+ module Synapse
4
+ module Serialization
5
+ # Converter that converts an Ox document into an XML string
6
+ class OxDocumentToXmlConverter
7
+ include Converter
8
+
9
+ converts Ox::Document, String
10
+
11
+ # @param [Object] original
12
+ # @return [Object]
13
+ def convert_content(original)
14
+ Ox.dump original, @options
15
+ end
16
+ end
17
+
18
+ # Converter that converts an XML string into an Ox document
19
+ class XmlToOxDocumentConverter
20
+ include Converter
21
+
22
+ converts String, Ox::Document
23
+
24
+ # @param [Object] original
25
+ # @return [Object]
26
+ def convert_content(original)
27
+ Ox.load original, @options
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,12 @@
1
+ module Synapse
2
+ module Serialization
3
+ # Raised when conversion between two types is not supported
4
+ class ConversionError < NonTransientError; end
5
+
6
+ # Raised when an error occurs during serialization
7
+ class SerializationError < NonTransientError; end
8
+
9
+ # Raised when a serialized type can't be found in the current environment
10
+ class UnknownSerializedTypeError < SerializationError; end
11
+ end
12
+ end
@@ -0,0 +1,61 @@
1
+ module Synapse
2
+ module Serialization
3
+ # Provides a generic lazy deserializing object
4
+ class LazyObject
5
+ # @return [SerializedObject]
6
+ attr_reader :serialized_object
7
+
8
+ # @return [Serializer]
9
+ attr_reader :serializer
10
+
11
+ # @return [Class]
12
+ attr_reader :type
13
+
14
+ # @param [SerializedObject] serialized_object
15
+ # @param [Serializer] serializer
16
+ # @return [undefined]
17
+ def initialize(serialized_object, serializer)
18
+ @serialized_object = serialized_object
19
+ @serializer = serializer
20
+ @type = serializer.class_for serialized_object.type
21
+ end
22
+
23
+ # Returns the deserialized version of the contained serialized object
24
+ # @return [Object]
25
+ def deserialized
26
+ @deserialized ||= @serializer.deserialize @serialized_object
27
+ end
28
+
29
+ # Returns true if this object has been deserialized already
30
+ # @return [Boolean]
31
+ def deserialized?
32
+ !!@deserialized
33
+ end
34
+ end
35
+
36
+ # Deserialized object that has (nearly) the same interface as a lazy object
37
+ class DeserializedObject
38
+ # @return [Object]
39
+ attr_reader :deserialized
40
+
41
+ # @return [Serializer] This will always be nil
42
+ attr_reader :serializer
43
+
44
+ # @return [Class]
45
+ attr_reader :type
46
+
47
+ # @param [Object] deserialized
48
+ # @return [undefined]
49
+ def initialize(deserialized)
50
+ @deserialized = deserialized
51
+ @type = deserialized.class
52
+ end
53
+
54
+ # Returns true if this object has been deserialized already; always true
55
+ # @return [Boolean]
56
+ def deserialized?
57
+ true
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,25 @@
1
+ module Synapse
2
+ module Serialization
3
+ # Describes the properties that a serialized domain event should have
4
+ # @abstract
5
+ class SerializedDomainEventData
6
+ # @return [String] Identifier of the serialized event
7
+ def id; end
8
+
9
+ # @return [SerializedObject] Serialized metadata of the serialized event
10
+ def metadata; end
11
+
12
+ # @return [SerializedObject] Serialized payload of the serialized event
13
+ def payload; end
14
+
15
+ # @return [Time] Timestamp of the serialized event
16
+ def timestamp; end
17
+
18
+ # @return [Object] Identifier of the aggregate that the event was applied to
19
+ def aggregate_id; end
20
+
21
+ # @return [Integer] Sequence number of the event in the aggregate
22
+ def sequence_number; end
23
+ end
24
+ end
25
+ end