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,127 @@
1
+ module Synapse
2
+ module Domain
3
+ # Container that tracks uncommitted events published by an aggregate root and its child entities
4
+ # @api private
5
+ class EventContainer
6
+ # @return [Object] The identifier of the aggregate being tracked
7
+ attr_reader :aggregate_id
8
+
9
+ # @return [Integer] The sequence number of the last committed event
10
+ attr_reader :last_committed_sequence_number
11
+
12
+ # Initializes this event container
13
+ #
14
+ # @param [Object] aggregate_id
15
+ # The identifier of the aggregate being tracked
16
+ #
17
+ # @return [undefined]
18
+ def initialize(aggregate_id)
19
+ @aggregate_id = aggregate_id
20
+ @events = Array.new
21
+ @listeners = Array.new
22
+ end
23
+
24
+ # Registers an event published by the aggregate to this container
25
+ #
26
+ # During this process, a domain event message is created. Event registration listeners can
27
+ # choose to modify or replace the message before it is committed.
28
+ #
29
+ # @param [Object] payload
30
+ # Payload of the message; the actual event object
31
+ #
32
+ # @param [Hash] metadata
33
+ # Metadata associated with the event
34
+ #
35
+ # @return [DomainEventMessage] The event that will be committed
36
+ def register_event(payload, metadata)
37
+ event = DomainEventMessage.build do |b|
38
+ b.aggregate_id = @aggregate_id
39
+ b.sequence_number = next_sequence_number
40
+ b.metadata = metadata
41
+ b.payload = payload
42
+ end
43
+
44
+ @listeners.each do |listener|
45
+ event = listener.call event
46
+ end
47
+
48
+ @last_sequence_number = event.sequence_number
49
+ @events.push event
50
+
51
+ event
52
+ end
53
+
54
+ # Adds an event registration listener to this container
55
+ #
56
+ # If the listener is added after events have already registered with the container, it will
57
+ # be called with a backlog of events to process.
58
+ #
59
+ # @param [#call] listener
60
+ # @return [undefined]
61
+ def add_registration_listener(listener)
62
+ @listeners.push listener
63
+
64
+ @events.map! do |event|
65
+ listener.call event
66
+ end
67
+ end
68
+
69
+ # Sets the last committed sequence number for the container
70
+ #
71
+ # @raise [RuntimeError] If events have already been registered to the container
72
+ # @param [Integer] last_known
73
+ # @return [undefined]
74
+ def initialize_sequence_number(last_known)
75
+ unless @events.empty?
76
+ raise 'Sequence number must be set before events are registered'
77
+ end
78
+
79
+ @last_committed_sequence_number = last_known
80
+ end
81
+
82
+ # Returns the sequence number of the last event known by this container
83
+ # @return [Integer]
84
+ def last_sequence_number
85
+ if @events.empty?
86
+ return @last_committed_sequence_number
87
+ end
88
+
89
+ unless @last_sequence_number
90
+ @last_sequence_number = @events.last.sequence_number
91
+ end
92
+
93
+ @last_sequence_number
94
+ end
95
+
96
+ # Updates the last committed sequence number and clears any uncommitted events and any
97
+ # event registration listeners
98
+ #
99
+ # @return [undefined]
100
+ def mark_committed
101
+ @last_committed_sequence_number = @last_sequence_number
102
+ @events.clear
103
+ @listeners.clear
104
+ end
105
+
106
+ # Returns an event stream containing the uncommitted events in this container
107
+ # @return [DomainEventStream]
108
+ def to_stream
109
+ SimpleDomainEventStream.new @events
110
+ end
111
+
112
+ # Returns the number of uncommitted events in this container
113
+ # @return [Integer]
114
+ def size
115
+ @events.size
116
+ end
117
+
118
+ private
119
+
120
+ # Returns the next sequence number to use for registered events
121
+ # @return [Integer]
122
+ def next_sequence_number
123
+ last_sequence_number ? last_sequence_number.next : 0
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,82 @@
1
+ module Synapse
2
+ module Domain
3
+ # Represents the occurance of an event in the application that may be of some importance
4
+ # to another component of the application. It contains the relevant data for other
5
+ # components to act upon.
6
+ class EventMessage < Message
7
+ # The timestamp of when the event was reported
8
+ # @return [Time]
9
+ attr_reader :timestamp
10
+
11
+ # @param [String] id
12
+ # @param [Hash] metadata
13
+ # @param [Object] payload
14
+ # @param [Time] timestamp
15
+ # @return [undefined]
16
+ def initialize(id, metadata, payload, timestamp)
17
+ super id, metadata, payload
18
+ @timestamp = timestamp
19
+ end
20
+
21
+ # @return [Class]
22
+ def self.builder
23
+ EventMessageBuilder
24
+ end
25
+
26
+ protected
27
+
28
+ # @param [EventMessageBuilder] builder
29
+ # @param [Hash] metadata
30
+ # @return [undefined]
31
+ def build_duplicate(builder, metadata)
32
+ super
33
+ builder.timestamp = @timestamp
34
+ end
35
+ end
36
+
37
+ # Message that contains a domain event as a payload that represents a state change in the domain.
38
+ #
39
+ # In contrast to a regular event message, this type of message contains the identifier of the
40
+ # aggregate that reported it. It also contains a sequence number that allows the messages to be placed
41
+ # in the order they were reported.
42
+ class DomainEventMessage < EventMessage
43
+ # The identifier of the aggregate that reported the event
44
+ # @return [Object]
45
+ attr_reader :aggregate_id
46
+
47
+ # The sequence number of the event in the order of generation
48
+ # @return [Integer]
49
+ attr_reader :sequence_number
50
+
51
+ # @param [String] id
52
+ # @param [Hash] metadata
53
+ # @param [Object] payload
54
+ # @param [Time] timestamp
55
+ # @param [Object] aggregate_id
56
+ # @param [Integer] sequence_number
57
+ # @return [undefined]
58
+ def initialize(id, metadata, payload, timestamp, aggregate_id, sequence_number)
59
+ super id, metadata, payload, timestamp
60
+
61
+ @aggregate_id = aggregate_id
62
+ @sequence_number = sequence_number
63
+ end
64
+
65
+ # @return [Class]
66
+ def self.builder
67
+ DomainEventMessageBuilder
68
+ end
69
+
70
+ protected
71
+
72
+ # @param [DomainEventMessageBuilder] builder
73
+ # @param [Hash] metadata
74
+ # @return [undefined]
75
+ def build_duplicate(builder, metadata)
76
+ super
77
+ builder.aggregate_id = @aggregate_id
78
+ builder.sequence_number = @sequence_number
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,34 @@
1
+ module Synapse
2
+ module Domain
3
+ # Message builder capable of producing EventMessage instances
4
+ class EventMessageBuilder < MessageBuilder
5
+ # @return [Time]
6
+ attr_accessor :timestamp
7
+
8
+ # @return [EventMessage]
9
+ def build
10
+ EventMessage.new @id, @metadata, @payload, @timestamp
11
+ end
12
+
13
+ # @return [undefined]
14
+ def populate_defaults
15
+ super
16
+ @timestamp ||= Time.now
17
+ end
18
+ end
19
+
20
+ # Message builder capable of producing DomainEventMessage instances
21
+ class DomainEventMessageBuilder < EventMessageBuilder
22
+ # @return [Object]
23
+ attr_accessor :aggregate_id
24
+
25
+ # @return [Integer]
26
+ attr_accessor :sequence_number
27
+
28
+ # @return [DomainEventMessage]
29
+ def build
30
+ DomainEventMessage.new @id, @metadata, @payload, @timestamp, @aggregate_id, @sequence_number
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,108 @@
1
+ module Synapse
2
+ module Domain
3
+ # Represents a historical stream of domain events in chronological order
4
+ #
5
+ # @example
6
+ # stream = InMemoryDomainEventStream.new events
7
+ # until stream.end?
8
+ # puts stream.next_event
9
+ # end
10
+ #
11
+ # @example
12
+ # stream = InMemoryDomainEventStream.new events
13
+ # stream.each do |event|
14
+ # puts event
15
+ # end
16
+ #
17
+ # @abstract
18
+ class DomainEventStream
19
+ # Returns true if the end of the stream has been reached
20
+ #
21
+ # @abstract
22
+ # @return [Boolean]
23
+ def end?
24
+ true
25
+ end
26
+
27
+ # Returns the next event in the stream and moves the stream's pointer forward
28
+ #
29
+ # @abstract
30
+ # @return [DomainEventMessage]
31
+ def next_event; end
32
+
33
+ # Returns the next event in the stream without moving the stream's pointer forward
34
+ #
35
+ # @abstract
36
+ # @return [DomainEventMessage]
37
+ def peek; end
38
+
39
+ # Yields the next domain events in the stream until the end of the stream has been reached
40
+ #
41
+ # @yield [DomainEventMessage] The next event in the event stream
42
+ # @return [undefined]
43
+ def each
44
+ until end?
45
+ yield next_event
46
+ end
47
+ end
48
+
49
+ # Returns the domain events in this stream as an array
50
+ # @return [Array<DomainEventMessage>]
51
+ def to_a
52
+ events = Array.new
53
+ each do |event|
54
+ events.push event
55
+ end
56
+
57
+ events
58
+ end
59
+
60
+ protected
61
+
62
+ def assert_valid
63
+ if end?
64
+ raise EndOfStreamError
65
+ end
66
+ end
67
+ end
68
+
69
+ # Raised when the end of a domain event stream has been reached
70
+ class EndOfStreamError < NonTransientError; end
71
+
72
+ # Implementation of a domain event stream that holds a stream of events in memory
73
+ class SimpleDomainEventStream < DomainEventStream
74
+ def initialize(*events)
75
+ @events = events.flatten
76
+ @next_index = 0
77
+ end
78
+
79
+ # Returns true if the end of the stream has been reached
80
+ # @return [Boolean]
81
+ def end?
82
+ @next_index >= @events.size
83
+ end
84
+
85
+ # Returns the next event in the stream and moves the stream's pointer forward
86
+ #
87
+ # @raise [EndOfStreamError] If the end of the stream has been reached
88
+ # @return [DomainEventMessage]
89
+ def next_event
90
+ assert_valid
91
+
92
+ event = @events.at @next_index
93
+ @next_index += 1
94
+
95
+ event
96
+ end
97
+
98
+ # Returns the next event in the stream without moving the stream's pointer forward
99
+ #
100
+ # @raise [EndOfStreamError] If the end of the stream has been reached
101
+ # @return [DomainEventMessage]
102
+ def peek
103
+ assert_valid
104
+ @events.at @next_index
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,60 @@
1
+ module Synapse
2
+ # Records messages as they are sent to a bus so that duplicates can be tracked and prevented.
3
+ # Inspired by the de-duplication manager from Lokad.CQRS
4
+ #
5
+ # This implementation is thread-safe
6
+ class DuplicationRecorder
7
+ def initialize
8
+ @recorded = Hash.new
9
+ @lock = Mutex.new
10
+ end
11
+
12
+ # Records the given message so that duplicates can be ignored
13
+ #
14
+ # @raise [DuplicationError] If a duplicate message has been detected
15
+ # @param [Message] message
16
+ # @return [undefined]
17
+ def record(message)
18
+ @lock.synchronize do
19
+ if @recorded.has_key? message.id
20
+ raise DuplicationError
21
+ end
22
+
23
+ @recorded.store message.id, Time.now
24
+ end
25
+ end
26
+
27
+ # Returns true if the given message has been recorded
28
+ #
29
+ # @param [Message] message
30
+ # @return [Boolean]
31
+ def recorded?(message)
32
+ @recorded.has_key? message.id
33
+ end
34
+
35
+ # Forgets the given message
36
+ #
37
+ # @param [Message] message
38
+ # @return [undefined]
39
+ def forget(message)
40
+ @lock.synchronize do
41
+ @recorded.delete message.id
42
+ end
43
+ end
44
+
45
+ # Cleans up messages that are older than the given timestamp
46
+ #
47
+ # @param [Time] threshold
48
+ # @return [undefined]
49
+ def forget_older_than(threshold)
50
+ @lock.synchronize do
51
+ @recorded.delete_if do |message_id, timestamp|
52
+ timestamp <= threshold
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ # Raised when a duplicate message has been detected by the duplication recorder
59
+ class DuplicationError < NonTransientError; end
60
+ end
@@ -0,0 +1,13 @@
1
+ module Synapse
2
+ # Base exception for all Synapse framework-related errors
3
+ class SynapseError < RuntimeError; end
4
+
5
+ # Raised when an error has occured that resulted from misconfiguration
6
+ class ConfigurationError < SynapseError; end
7
+
8
+ # Raised when an error has occured that cannot be resolved with intervention
9
+ class NonTransientError < SynapseError; end
10
+
11
+ # Raised when an error has occured that might be resolved by retrying the operation
12
+ class TransientError < SynapseError; end
13
+ end
@@ -0,0 +1,40 @@
1
+ module Synapse
2
+ module EventBus
3
+ # Represents a mechanism for event listeners to subscribe to events and for event publishers
4
+ # to dispatch their events to any interested parties.
5
+ #
6
+ # Implementations may or may not dispatch the events to listeners in the dispatching thread.
7
+ #
8
+ # @abstract
9
+ class EventBus
10
+ # Publishes one or more events to any listeners subscribed to this event bus
11
+ #
12
+ # Implementations may treat the given events as a single batch and distribute them as such
13
+ # to all subscribed event listeners.
14
+ #
15
+ # @abstract
16
+ # @param [EventMessage...] events
17
+ # @return [undefined]
18
+ def publish(*events); end
19
+
20
+ # Subscribes the given listener to this event bus
21
+ #
22
+ # @abstract
23
+ # @raise [SubscriptionFailedError] If subscription of an event listener failed
24
+ # @param [EventListener] listener
25
+ # @return [undefined]
26
+ def subscribe(listener); end
27
+
28
+ # Unsubscribes the given listener from this event bus
29
+ #
30
+ # @abstract
31
+ # @param [EventListener] listener
32
+ # @return [undefined]
33
+ def unsubscribe(listener); end
34
+ end
35
+
36
+ # Raised when the subscription of an event listener has not succeeded. Generally, this means that
37
+ # some precondition set by an event bus implementation for the listener have not been met.
38
+ class SubscriptionFailedError < NonTransientError; end
39
+ end
40
+ end