synapse-core 0.5.1 → 0.5.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 (101) hide show
  1. checksums.yaml +7 -0
  2. data/lib/synapse.rb +1 -1
  3. data/lib/synapse/command.rb +1 -1
  4. data/lib/synapse/command/command_handler.rb +1 -1
  5. data/lib/synapse/command/mapping.rb +71 -0
  6. data/lib/synapse/command/message.rb +0 -16
  7. data/lib/synapse/command/simple_command_bus.rb +5 -2
  8. data/lib/synapse/common/message.rb +16 -0
  9. data/lib/synapse/configuration.rb +1 -18
  10. data/lib/synapse/configuration/component/command_bus.rb +7 -24
  11. data/lib/synapse/configuration/component/command_bus/simple_command_bus.rb +31 -7
  12. data/lib/synapse/configuration/component/event_bus.rb +3 -8
  13. data/lib/synapse/configuration/component/event_sourcing.rb +11 -8
  14. data/lib/synapse/configuration/component/event_sourcing/aggregate_snapshot_taker.rb +48 -0
  15. data/lib/synapse/configuration/component/event_sourcing/interval_snapshot_policy.rb +33 -0
  16. data/lib/synapse/configuration/component/event_sourcing/repository.rb +36 -1
  17. data/lib/synapse/configuration/component/repository.rb +4 -8
  18. data/lib/synapse/configuration/component/serialization.rb +5 -16
  19. data/lib/synapse/configuration/component/uow.rb +3 -8
  20. data/lib/synapse/configuration/component/upcasting.rb +3 -8
  21. data/lib/synapse/configuration/container_builder.rb +29 -2
  22. data/lib/synapse/configuration/definition_builder.rb +47 -23
  23. data/lib/synapse/domain/message.rb +0 -16
  24. data/lib/synapse/event_bus.rb +1 -1
  25. data/lib/synapse/event_bus/event_listener.rb +1 -1
  26. data/lib/synapse/event_bus/mapping.rb +47 -0
  27. data/lib/synapse/event_sourcing.rb +3 -2
  28. data/lib/synapse/event_sourcing/aggregate_factory.rb +4 -3
  29. data/lib/synapse/event_sourcing/aggregate_root.rb +17 -0
  30. data/lib/synapse/event_sourcing/conflict_resolver.rb +3 -0
  31. data/lib/synapse/event_sourcing/member.rb +34 -6
  32. data/lib/synapse/event_sourcing/repository.rb +17 -0
  33. data/lib/synapse/event_sourcing/snapshot/aggregate_taker.rb +38 -0
  34. data/lib/synapse/event_sourcing/snapshot/policy.rb +27 -0
  35. data/lib/synapse/event_sourcing/snapshot/taker.rb +2 -37
  36. data/lib/synapse/event_sourcing/snapshot/unit_listener.rb +26 -0
  37. data/lib/synapse/event_sourcing/stream_decorator.rb +8 -6
  38. data/lib/synapse/event_store/errors.rb +2 -2
  39. data/lib/synapse/mapping.rb +2 -0
  40. data/lib/synapse/mapping/mapper.rb +75 -0
  41. data/lib/synapse/{wiring/wire.rb → mapping/mapping.rb} +8 -8
  42. data/lib/synapse/process_manager.rb +2 -2
  43. data/lib/synapse/process_manager/mapping/process.rb +44 -0
  44. data/lib/synapse/process_manager/{wiring → mapping}/process_manager.rb +13 -13
  45. data/lib/synapse/process_manager/process.rb +3 -3
  46. data/lib/synapse/repository/locking.rb +14 -8
  47. data/lib/synapse/upcasting/upcaster_chain.rb +2 -2
  48. data/lib/synapse/version.rb +1 -1
  49. data/test/command/{wiring_test.rb → mapping_test.rb} +11 -11
  50. data/test/configuration/component/command_bus/simple_command_bus_test.rb +30 -0
  51. data/test/configuration/component/event_bus/simple_event_bus_test.rb +2 -2
  52. data/test/configuration/component/event_sourcing/repository_test.rb +71 -0
  53. data/test/configuration/component/repository/simple_repository_test.rb +35 -0
  54. data/test/configuration/component/upcasting/upcaster_chain_test.rb +29 -0
  55. data/test/configuration/container_builder_test.rb +4 -6
  56. data/test/event_bus/{wiring_test.rb → mapping_test.rb} +6 -6
  57. data/test/event_sourcing/aggregate_factory_test.rb +5 -1
  58. data/test/event_sourcing/aggregate_root_test.rb +1 -0
  59. data/test/event_sourcing/fixtures.rb +21 -21
  60. data/test/event_sourcing/repository_test.rb +10 -0
  61. data/test/event_sourcing/snapshot/aggregate_taker_test.rb +1 -1
  62. data/test/event_sourcing/snapshot/interval_policy_test.rb +24 -0
  63. data/test/process_manager/{wiring → mapping}/fixtures.rb +7 -8
  64. data/test/process_manager/{wiring → mapping}/process_manager_test.rb +6 -6
  65. data/test/process_manager/{wiring → mapping}/process_test.rb +3 -3
  66. data/test/serialization/converter/chain_test.rb +2 -2
  67. data/test/serialization/converter/factory_test.rb +2 -2
  68. data/test/serialization/converter/identity_test.rb +1 -1
  69. data/test/serialization/converter/json_test.rb +2 -2
  70. data/test/serialization/converter/ox_test.rb +2 -2
  71. data/test/serialization/lazy_object_test.rb +1 -1
  72. data/test/serialization/message/metadata_test.rb +1 -1
  73. data/test/serialization/message/serialization_aware_message_test.rb +5 -5
  74. data/test/serialization/message/serialized_message_builder_test.rb +1 -1
  75. data/test/serialization/message/serialized_message_test.rb +5 -5
  76. data/test/serialization/message/serializer_test.rb +2 -2
  77. data/test/serialization/revision_resolver_test.rb +1 -1
  78. data/test/serialization/serialized_object_test.rb +2 -2
  79. data/test/serialization/serialized_type_test.rb +2 -2
  80. data/test/serialization/serializer/marshal_test.rb +1 -1
  81. data/test/serialization/serializer/oj_test.rb +1 -1
  82. data/test/serialization/serializer/ox_test.rb +2 -2
  83. data/test/serialization/serializer_test.rb +1 -1
  84. data/test/uow/factory_test.rb +1 -1
  85. data/test/uow/outer_commit_listener_test.rb +4 -4
  86. data/test/uow/provider_test.rb +5 -5
  87. data/test/uow/uow_test.rb +19 -17
  88. data/test/upcasting/chain_test.rb +1 -1
  89. data/test/upcasting/data_test.rb +3 -1
  90. metadata +30 -37
  91. data/lib/synapse/command/wiring.rb +0 -47
  92. data/lib/synapse/event_bus/wiring.rb +0 -20
  93. data/lib/synapse/event_sourcing/snapshot/count_stream.rb +0 -86
  94. data/lib/synapse/event_sourcing/snapshot/count_trigger.rb +0 -91
  95. data/lib/synapse/process_manager/wiring/process.rb +0 -27
  96. data/lib/synapse/wiring.rb +0 -3
  97. data/lib/synapse/wiring/message_wiring.rb +0 -76
  98. data/lib/synapse/wiring/wire_registry.rb +0 -61
  99. data/test/event_sourcing/snapshot/integration_test.rb +0 -65
  100. data/test/wiring/wire_registry_test.rb +0 -60
  101. data/test/wiring/wire_test.rb +0 -51
@@ -1,47 +0,0 @@
1
- module Synapse
2
- module Command
3
- # Mixin for a command handler that wishes to use the wiring DSL
4
- module WiringCommandHandler
5
- extend ActiveSupport::Concern
6
- include CommandHandler
7
- include Wiring::MessageWiring
8
-
9
- included do
10
- self.wire_registry = Wiring::WireRegistry.new false
11
- end
12
-
13
- # @param [CommandMessage] command
14
- # @param [UnitOfWork] current_unit Current unit of work
15
- # @return [Object] The result of handling the given command
16
- def handle(command, current_unit)
17
- wire = wire_registry.wire_for command.payload_type
18
-
19
- unless wire
20
- raise ArgumentError, 'Not capable of handling [%s] commands' % command.payload_type
21
- end
22
-
23
- invoke_wire command, wire
24
- end
25
-
26
- # Subscribes this handler to the given command bus for any types that have been wired
27
- #
28
- # @param [CommandBus] command_bus
29
- # @return [undefined]
30
- def subscribe(command_bus)
31
- self.wire_registry.each_type do |type|
32
- command_bus.subscribe type, self
33
- end
34
- end
35
-
36
- # Unsubscribes this handler from the given command bus for any types that have been wired
37
- #
38
- # @param [CommandBus] command_bus
39
- # @return [undefined]
40
- def unsubscribe(command_bus)
41
- self.wire_registry.each_type do |type|
42
- command_bus.unsubscribe type, self
43
- end
44
- end
45
- end # WiringCommandHandler
46
- end # Command
47
- end
@@ -1,20 +0,0 @@
1
- module Synapse
2
- module EventBus
3
- # Mixin for an event listener that wishes to use the wiring DSL
4
- module WiringEventListener
5
- extend ActiveSupport::Concern
6
- include EventListener
7
- include Wiring::MessageWiring
8
-
9
- # @param [EventMessage] event
10
- # @return [undefined]
11
- def notify(event)
12
- wire = wire_registry.wire_for event.payload_type
13
-
14
- return unless wire
15
-
16
- invoke_wire event, wire
17
- end
18
- end
19
- end
20
- end
@@ -1,86 +0,0 @@
1
- module Synapse
2
- module EventSourcing
3
- # Event stream decorator that simply counts each event that is retrieved from a delegate stream
4
- class CountingEventStream < Domain::DomainEventStream
5
- extend Forwardable
6
-
7
- # @param [DomainEventStream] delegate
8
- # @param [Atomic] counter
9
- # @return [undefined]
10
- def initialize(delegate, counter)
11
- @delegate = delegate
12
- @counter = counter
13
- end
14
-
15
- # @return [DomainEventMessage]
16
- def next_event
17
- next_event = @delegate.next_event
18
-
19
- @counter.update do |value|
20
- value = value.next
21
- end
22
-
23
- next_event
24
- end
25
-
26
- # Delegate un-decorated domain event stream operations
27
- def_delegators :@delegate, :end?, :peek
28
- end
29
-
30
- # Event stream decorator that counts each event retrieved from the delegate stream and
31
- # registers a listener with the current unit of work that can trigger a snapshot after the
32
- # unit of work has been cleaned up
33
- class TriggeringEventStream < CountingEventStream
34
- # @param [DomainEventStream] delegate
35
- # @param [Atomic] counter
36
- # @param [String] type_identifier
37
- # @param [Object] aggregate_id
38
- # @param [EventCountSnapshotTrigger] trigger
39
- # @return [undefined]
40
- def initialize(delegate, counter, type_identifier, aggregate_id, trigger)
41
- super delegate, counter
42
-
43
- @type_identifier = type_identifier
44
- @aggregate_id = aggregate_id
45
- @trigger = trigger
46
- end
47
-
48
- # @return [Boolean]
49
- def end?
50
- the_end = @delegate.end?
51
-
52
- if the_end
53
- listener = SnapshotUnitOfWorkListener.new @type_identifier, @aggregate_id, @counter, @trigger
54
-
55
- unit = @trigger.unit_provider.current
56
- unit.register_listener listener
57
-
58
- @trigger.clear_counter @aggregate_id
59
- end
60
-
61
- the_end
62
- end
63
- end
64
-
65
- # Unit of work listener that is used to trigger snapshots after a unit of work has been cleaned up
66
- class SnapshotUnitOfWorkListener < UnitOfWork::UnitOfWorkListener
67
- # @param [String] type_identifier
68
- # @param [Object] aggregate_id
69
- # @param [Atomic] counter
70
- # @param [EventCountSnapshotTrigger] trigger
71
- # @return [undefined]
72
- def initialize(type_identifier, aggregate_id, counter, trigger)
73
- @type_identifier = type_identifier
74
- @aggregate_id = aggregate_id
75
- @trigger = trigger
76
- @counter = counter
77
- end
78
-
79
- # @param [UnitOfWork] unit
80
- # @return [undefined]
81
- def on_cleanup(unit)
82
- @trigger.trigger_snapshot @type_identifier, @aggregate_id, @counter
83
- end
84
- end
85
- end
86
- end
@@ -1,91 +0,0 @@
1
- require 'atomic'
2
-
3
- module Synapse
4
- module EventSourcing
5
- # Snapshot trigger that counts the number of events between snapshots to decide when to
6
- # schedule the next snapshot
7
- class EventCountSnapshotTrigger < EventStreamDecorator
8
- # Default threshold for a snapshot to be scheduled
9
- # @return [Integer]
10
- DEFAULT_THRESHOLD = 50
11
-
12
- # @return [SnapshotTaker]
13
- attr_reader :snapshot_taker
14
-
15
- # @return [Integer] The number of events between snapshots
16
- attr_accessor :threshold
17
-
18
- # @return [UnitOfWorkProvider]
19
- attr_reader :unit_provider
20
-
21
- # @param [SnapshotTaker] snapshot_taker
22
- # @param [UnitOfWorkProvider] unit_provider
23
- # @return [undefined]
24
- def initialize(snapshot_taker, unit_provider)
25
- @counters = Hash.new
26
- @lock = Mutex.new
27
- @logger = Logging.logger[self.class]
28
- @threshold = DEFAULT_THRESHOLD
29
-
30
- @snapshot_taker = snapshot_taker
31
- @unit_provider = unit_provider
32
- end
33
-
34
- # If the event threshold has been reached for the aggregate with the given identifier, this
35
- # will cause a snapshot to be scheduled
36
- #
37
- # @param [String] type_identifier
38
- # @param [Object] aggregate_id
39
- # @param [Atomic] counter
40
- # @return [undefined]
41
- def trigger_snapshot(type_identifier, aggregate_id, counter)
42
- if counter.value > @threshold
43
- @logger.info 'Snapshot threshold reached for [%s] [%s]' % [type_identifier, aggregate_id]
44
-
45
- @snapshot_taker.schedule_snapshot type_identifier, aggregate_id
46
- counter.value = 1
47
- end
48
- end
49
-
50
- # @param [String] type_identifier
51
- # @param [Object] aggregate_id
52
- # @param [DomainEventStream] stream
53
- # @return [DomainEventStream]
54
- def decorate_for_read(type_identifier, aggregate_id, stream)
55
- CountingEventStream.new(stream, counter_for(aggregate_id))
56
- end
57
-
58
- # @param [String] type_identifier
59
- # @param [AggregateRoot] aggregate
60
- # @param [DomainEventStream] stream
61
- # @return [DomainEventStream]
62
- def decorate_for_append(type_identifier, aggregate, stream)
63
- TriggeringEventStream.new(stream, counter_for(aggregate.id), type_identifier, aggregate.id, self)
64
- end
65
-
66
- # Returns the event counter for the aggregate with the given identifier
67
- #
68
- # @param [Object] aggregate_id
69
- # @return [Atomic]
70
- def counter_for(aggregate_id)
71
- @lock.synchronize do
72
- if @counters.has_key? aggregate_id
73
- @counters.fetch aggregate_id
74
- else
75
- @counters.store aggregate_id, Atomic.new(0)
76
- end
77
- end
78
- end
79
-
80
- # Clears the event counter for the aggregate with the given identifier
81
- #
82
- # @param [Object] aggregate_id
83
- # @return [undefined]
84
- def clear_counter(aggregate_id)
85
- @lock.synchronize do
86
- @counters.delete aggregate_id
87
- end
88
- end
89
- end
90
- end
91
- end
@@ -1,27 +0,0 @@
1
- module Synapse
2
- module ProcessManager
3
- # Process that has the wiring DSL built-in
4
- #
5
- # @example
6
- # class OrderProcess < WiringProcess
7
- # wire OrderCreatedEvent, correlate: :order_id, start: true, to: :on_create
8
- # wire OrderFinishedEvent, correlate: :order_id, finish: true, to: :on_finish
9
- # end
10
- class WiringProcess < Process
11
- include Wiring::MessageWiring
12
-
13
- # @param [EventMessage] event
14
- # @return [undefined]
15
- def handle(event)
16
- return unless @active
17
-
18
- wire = wire_registry.wire_for event.payload_type
19
-
20
- return unless wire
21
-
22
- invoke_wire event, wire
23
- finish if wire.options[:finish]
24
- end
25
- end # WiringProcess
26
- end # ProcessManager
27
- end
@@ -1,3 +0,0 @@
1
- require 'synapse/wiring/message_wiring'
2
- require 'synapse/wiring/wire'
3
- require 'synapse/wiring/wire_registry'
@@ -1,76 +0,0 @@
1
- module Synapse
2
- module Wiring
3
- # Base mixin that make it easier to wire handlers to their respective types
4
- #
5
- # It is recommended to use mixins more specific to the component being implemented, like
6
- # wiring command handlers, event listeners, event-sourced members, or processes.
7
- #
8
- # @abstract
9
- module MessageWiring
10
- extend ActiveSupport::Concern
11
-
12
- included do
13
- # @return [WireRegistry]
14
- class_attribute :wire_registry
15
-
16
- # By default, the wire registry allows duplicates
17
- self.wire_registry = Wiring::WireRegistry.new true
18
- end
19
-
20
- module ClassMethods
21
- # Wires a message handler to messages with payload of the given type
22
- #
23
- # @example
24
- # wire CashWithdrawnEvent do |event|
25
- # # do something with the event
26
- # end
27
- #
28
- # @example
29
- # wire CashWithdrawnEvent :to => :on_withdraw
30
- #
31
- # def on_withdraw(event)
32
- # # do something with the event
33
- # end
34
- #
35
- # Certain components that use message handling have different options that can be set
36
- # on wires, like wiring processes.
37
- #
38
- # @example
39
- # wire SellTransactionStartedEvent, :start => true, :correlate => :transaction_id do
40
- # # do something with the event
41
- # end
42
- #
43
- # @api public
44
- # @param [Class] type
45
- # @param [Object...] args
46
- # @param [Proc] block
47
- # @return [undefined]
48
- def wire(type, *args, &block)
49
- options = args.extract_options!
50
-
51
- to = options.delete :to
52
- unless to
53
- unless block
54
- raise ArgumentError, 'Expected block or option :to'
55
- end
56
-
57
- to = block
58
- end
59
-
60
- wire = Wire.new type, options, to
61
-
62
- self.wire_registry.register wire
63
- end
64
- end
65
-
66
- protected
67
-
68
- # @param [Message] message
69
- # @param [Wire] wire
70
- # @return [Object] Result of the handler invocation
71
- def invoke_wire(message, wire)
72
- wire.invoke self, message.payload
73
- end
74
- end
75
- end
76
- end
@@ -1,61 +0,0 @@
1
- module Synapse
2
- module Wiring
3
- class WireRegistry
4
- # @param [Boolean] duplicates_allowed
5
- # @return [undefined]
6
- def initialize(duplicates_allowed)
7
- @duplicates_allowed = duplicates_allowed
8
- @wires = Array.new
9
- end
10
-
11
- # Yields the type that each wire is registered for
12
- #
13
- # @yield [Class]
14
- # @return [undefined]
15
- def each_type
16
- @wires.each do |wire|
17
- yield wire.type
18
- end
19
- end
20
-
21
- # @raise [DuplicateWireError] If duplicates aren't allowed and another wire exists that
22
- # wires the exact same type as the given wire
23
- # @param [Wire] wire
24
- # @return [undefined]
25
- def register(wire)
26
- unless @duplicates_allowed
27
- if @wires.include? wire
28
- raise DuplicateWireError
29
- end
30
- end
31
-
32
- @wires.push wire
33
- @wires.sort!
34
- end
35
-
36
- # Retrieves the most specific wire for a given type, if any
37
- #
38
- # @param [Class] target_type
39
- # @return [Wire]
40
- def wire_for(target_type)
41
- @wires.find do |wire|
42
- wire.type >= target_type
43
- end
44
- end
45
-
46
- # Retrieves any wires for a given type, regardless of specificity
47
- #
48
- # @param [Class] target_type
49
- # @return [Array]
50
- def wires_for(target_type)
51
- @wires.find_all do |wire|
52
- wire.type >= target_type
53
- end
54
- end
55
- end
56
-
57
- # Raised if a wire registry doesn't allow duplicates and an attempt is made to wire the same
58
- # type to multiple handlers
59
- class DuplicateWireError < NonTransientError; end
60
- end
61
- end
@@ -1,65 +0,0 @@
1
- require 'test_helper'
2
- require 'event_sourcing/fixtures'
3
-
4
- module Synapse
5
- module EventSourcing
6
-
7
- class SnapshotIntegrationTest < Test::Unit::TestCase
8
- def test_integration
9
- unit_provider = Object.new
10
- unit = Object.new
11
-
12
- mock(unit_provider).current do
13
- unit
14
- end
15
-
16
- listener = nil
17
-
18
- mock(unit).register_listener(is_a(SnapshotUnitOfWorkListener)) do |l|
19
- listener = l
20
- end
21
-
22
- event_store = Object.new
23
-
24
- aggregate_id = 123
25
- type_identifier = Object.to_s
26
-
27
- aggregate = Object.new
28
- mock(aggregate).id.any_times do
29
- aggregate_id
30
- end
31
-
32
- snapshot_taker = Object.new
33
- snapshot_trigger = EventCountSnapshotTrigger.new snapshot_taker, unit_provider
34
- snapshot_trigger.threshold = 5
35
-
36
- read_stream = create_stream 3
37
- append_stream = create_stream 3
38
-
39
- decorated_read_stream = snapshot_trigger.decorate_for_read type_identifier, aggregate_id, read_stream
40
- decorated_read_stream.to_a # Causes stream to iterate over all events
41
-
42
- decorated_append_stream = snapshot_trigger.decorate_for_append type_identifier, aggregate, append_stream
43
- decorated_append_stream.to_a # Ditto
44
-
45
- # At this point, there is a snapshot unit of work listener added
46
- # Let's "cleanup" the unit of work and check if the snapshot is triggered
47
-
48
- mock(snapshot_taker).schedule_snapshot type_identifier, aggregate_id
49
-
50
- listener.on_cleanup unit
51
- end
52
-
53
- def create_stream(size)
54
- events = Array.new
55
-
56
- size.times do
57
- events.push Domain::DomainEventMessage.build
58
- end
59
-
60
- Domain::SimpleDomainEventStream.new events
61
- end
62
- end
63
-
64
- end
65
- end