synapse-core 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
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