synapse-core 0.1.2 → 0.2.0
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.
- data/lib/synapse/auditing/audit_logger.rb +28 -0
- data/lib/synapse/auditing/data_provider.rb +42 -0
- data/lib/synapse/auditing/dispatch_interceptor.rb +29 -0
- data/lib/synapse/auditing/unit_listener.rb +53 -0
- data/lib/synapse/auditing.rb +4 -0
- data/lib/synapse/command.rb +34 -0
- data/lib/synapse/{duplication.rb → common/duplication.rb} +0 -0
- data/lib/synapse/{errors.rb → common/errors.rb} +0 -0
- data/lib/synapse/{identifier.rb → common/identifier.rb} +2 -1
- data/lib/synapse/{message.rb → common/message.rb} +0 -0
- data/lib/synapse/{message_builder.rb → common/message_builder.rb} +0 -0
- data/lib/synapse/domain.rb +5 -0
- data/lib/synapse/event_bus.rb +5 -0
- data/lib/synapse/event_sourcing/conflict_resolver.rb +4 -6
- data/lib/synapse/event_sourcing/member.rb +11 -2
- data/lib/synapse/event_sourcing/snapshot/taker.rb +0 -18
- data/lib/synapse/event_sourcing.rb +13 -0
- data/lib/synapse/event_store/mongo/cursor_event_stream.rb +3 -3
- data/lib/synapse/event_store/mongo.rb +8 -0
- data/lib/synapse/event_store.rb +11 -0
- data/lib/synapse/partitioning/amqp/amqp_queue_reader.rb +50 -0
- data/lib/synapse/partitioning/amqp/amqp_queue_writer.rb +31 -0
- data/lib/synapse/partitioning/amqp/key_resolver.rb +26 -0
- data/lib/synapse/partitioning/amqp.rb +3 -0
- data/lib/synapse/partitioning/memory_queue_reader.rb +31 -0
- data/lib/synapse/partitioning/memory_queue_writer.rb +19 -0
- data/lib/synapse/partitioning/message_receipt.rb +25 -0
- data/lib/synapse/partitioning/packing/json_packer.rb +93 -0
- data/lib/synapse/partitioning/packing/json_unpacker.rb +83 -0
- data/lib/synapse/partitioning/packing.rb +27 -0
- data/lib/synapse/partitioning/queue_reader.rb +32 -0
- data/lib/synapse/partitioning/queue_writer.rb +17 -0
- data/lib/synapse/partitioning.rb +20 -0
- data/lib/synapse/process_manager.rb +4 -0
- data/lib/synapse/repository/locking.rb +3 -8
- data/lib/synapse/repository.rb +7 -0
- data/lib/synapse/serialization/converter/bson.rb +28 -0
- data/lib/synapse/serialization/serializer/attribute.rb +48 -0
- data/lib/synapse/serialization/serializer.rb +1 -1
- data/lib/synapse/serialization.rb +46 -0
- data/lib/synapse/uow/factory.rb +5 -5
- data/lib/synapse/uow/uow.rb +13 -13
- data/lib/synapse/uow.rb +8 -0
- data/lib/synapse/upcasting/chain.rb +1 -1
- data/lib/synapse/upcasting.rb +5 -0
- data/lib/synapse/version.rb +1 -1
- data/lib/synapse/wiring.rb +3 -0
- data/lib/synapse.rb +26 -338
- data/test/auditing/data_provider_test.rb +30 -0
- data/test/auditing/dispatch_interceptor_test.rb +25 -0
- data/test/auditing/unit_listener_test.rb +70 -0
- data/test/partitioning/memory_test.rb +34 -0
- data/test/partitioning/packing/json_test.rb +61 -0
- data/test/test_ext.rb +14 -0
- data/test/test_helper.rb +4 -0
- metadata +45 -24
- data/test/event_sourcing/snapshot/deferred_taker_test.rb +0 -19
@@ -0,0 +1,28 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Auditing
|
3
|
+
# Represents a mechanism for auditing commands and the events produced by their execution
|
4
|
+
# @abstract
|
5
|
+
class AuditLogger
|
6
|
+
# Called when a command execution was finished successfully
|
7
|
+
#
|
8
|
+
# @abstract
|
9
|
+
# @param [CommandMessage] command
|
10
|
+
# @param [Object] return_value
|
11
|
+
# @param [Array<EventMessage>] events
|
12
|
+
# @return [undefined]
|
13
|
+
def on_success(command, return_value, events); end
|
14
|
+
|
15
|
+
# Called when a command execution results in an exception being raised
|
16
|
+
#
|
17
|
+
# The list of events may not be empty; in this case, some events could have been published
|
18
|
+
# to the event bus and/or appended to the event store.
|
19
|
+
#
|
20
|
+
# @abstract
|
21
|
+
# @param [CommandMessage] command
|
22
|
+
# @param [Exception] exception
|
23
|
+
# @param [Array<EventMessage>] events
|
24
|
+
# @return [undefined]
|
25
|
+
def on_failure(command, exception, events); end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Auditing
|
3
|
+
# Provides relevant information to events for auditing purposes
|
4
|
+
# @abstract
|
5
|
+
class AuditDataProvider
|
6
|
+
# Returns auditing information for the given command
|
7
|
+
#
|
8
|
+
# @abstract
|
9
|
+
# @param [CommandMessage] command
|
10
|
+
# @return [Hash]
|
11
|
+
def provide_data_for(command); end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Implementation of an audit provider that simply audits a command's metadata
|
15
|
+
class CommandMetadataProvider < AuditDataProvider
|
16
|
+
# @param [CommandMessage] command
|
17
|
+
# @return [Hash]
|
18
|
+
def provide_data_for(command)
|
19
|
+
command.metadata
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Implementation of an audit provider that attaches a command's identifier to each event
|
24
|
+
# produced as a result of the execution of that command
|
25
|
+
class CorrelationDataProvider < AuditDataProvider
|
26
|
+
# The default key to use when correlating events with commands
|
27
|
+
DEFAULT_KEY = :command_id
|
28
|
+
|
29
|
+
# @param [Symbol] correlation_key
|
30
|
+
# @return [undefined]
|
31
|
+
def initialize(correlation_key = DEFAULT_KEY)
|
32
|
+
@correlation_key = correlation_key
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param [CommandMessage] command
|
36
|
+
# @return [Hash]
|
37
|
+
def provide_data_for(command)
|
38
|
+
Hash[@correlation_key, command.id]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Auditing
|
3
|
+
class AuditingDispatchInterceptor < Command::DispatchInterceptor
|
4
|
+
# @return [Array<AuditDataProvider>]
|
5
|
+
attr_accessor :data_providers
|
6
|
+
|
7
|
+
# @return [Array<AuditLogger>]
|
8
|
+
attr_accessor :loggers
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@data_providers = Array.new
|
12
|
+
@loggers = Array.new
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [CommandMessage] command
|
16
|
+
# @param [UnitOfWork] unit The current unit of work for this command dispatch
|
17
|
+
# @param [InterceptorChain] chain
|
18
|
+
# @return [Object] The result of the execution of the command
|
19
|
+
def intercept(command, unit, chain)
|
20
|
+
audit_listener = AuditingUnitOfWorkListener.new command, @data_providers, @loggers
|
21
|
+
unit.register_listener audit_listener
|
22
|
+
|
23
|
+
chain.proceed(command).tap do |result|
|
24
|
+
audit_listener.return_value = result
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Auditing
|
3
|
+
class AuditingUnitOfWorkListener < UnitOfWork::UnitOfWorkListener
|
4
|
+
# @return [Array<EventMessage>]
|
5
|
+
attr_reader :recorded_events
|
6
|
+
|
7
|
+
# @return [Object]
|
8
|
+
attr_accessor :return_value
|
9
|
+
|
10
|
+
# @param [CommandMessage] command
|
11
|
+
# @param [Array<AuditDataProvider>] data_providers
|
12
|
+
# @param [Array<AuditLogger>] loggers
|
13
|
+
# @return [undefined]
|
14
|
+
def initialize(command, data_providers, loggers)
|
15
|
+
@command = command
|
16
|
+
@data_providers = data_providers
|
17
|
+
@loggers = loggers
|
18
|
+
@recorded_events = Array.new
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param [UnitOfWork] unit
|
22
|
+
# @param [EventMessage] event
|
23
|
+
# @return [EventMessage]
|
24
|
+
def on_event_registered(unit, event)
|
25
|
+
audit_data = Hash.new
|
26
|
+
@data_providers.each do |provider|
|
27
|
+
audit_data.merge! provider.provide_data_for @command
|
28
|
+
end
|
29
|
+
|
30
|
+
event.and_metadata(audit_data).tap do |e|
|
31
|
+
@recorded_events.push e
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param [UnitOfWork] unit
|
36
|
+
# @return [undefined]
|
37
|
+
def after_commit(unit)
|
38
|
+
@loggers.each do |logger|
|
39
|
+
logger.on_success @command, @return_value, @recorded_events
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @param [UnitOfWork] unit
|
44
|
+
# @param [Error] cause
|
45
|
+
# @return [undefined]
|
46
|
+
def on_rollback(unit, cause = nil)
|
47
|
+
@loggers.each do |logger|
|
48
|
+
logger.on_success @command, cause, @recorded_events
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Command
|
3
|
+
extend ActiveSupport::Autoload
|
4
|
+
|
5
|
+
# Optional filters and interceptors
|
6
|
+
autoload_at 'synapse/command/duplication' do
|
7
|
+
autoload :DuplicationFilter
|
8
|
+
autoload :DuplicationCleanupInterceptor
|
9
|
+
end
|
10
|
+
|
11
|
+
autoload_at 'synapse/command/filters/validation' do
|
12
|
+
autoload :ActiveModelValidationFilter
|
13
|
+
autoload :ActiveModelValidationError
|
14
|
+
end
|
15
|
+
|
16
|
+
autoload_at 'synapse/command/interceptors/serialization' do
|
17
|
+
autoload :SerializationOptimizingInterceptor
|
18
|
+
autoload :SerializationOptimizingListener
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'synapse/command/command_bus'
|
24
|
+
require 'synapse/command/command_callback'
|
25
|
+
require 'synapse/command/command_filter'
|
26
|
+
require 'synapse/command/command_handler'
|
27
|
+
require 'synapse/command/dispatch_interceptor'
|
28
|
+
require 'synapse/command/errors'
|
29
|
+
require 'synapse/command/gateway'
|
30
|
+
require 'synapse/command/interceptor_chain'
|
31
|
+
require 'synapse/command/message'
|
32
|
+
require 'synapse/command/rollback_policy'
|
33
|
+
require 'synapse/command/simple_command_bus'
|
34
|
+
require 'synapse/command/wiring'
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -64,13 +64,11 @@ module Synapse
|
|
64
64
|
|
65
65
|
# @return [DomainEventMessage]
|
66
66
|
def next_event
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
67
|
+
@delegate.next_event.tap do |event|
|
68
|
+
if @expected_version and event.sequence_number > @expected_version
|
69
|
+
@unseen_events.push event
|
70
|
+
end
|
71
71
|
end
|
72
|
-
|
73
|
-
event
|
74
72
|
end
|
75
73
|
|
76
74
|
# Delegators for domain event stream
|
@@ -4,6 +4,11 @@ module Synapse
|
|
4
4
|
# applied to the aggregate
|
5
5
|
module Member
|
6
6
|
extend ActiveSupport::Concern
|
7
|
+
include Wiring::MessageWiring
|
8
|
+
|
9
|
+
included do
|
10
|
+
self.wire_registry = Wiring::WireRegistry.new true
|
11
|
+
end
|
7
12
|
|
8
13
|
module ClassMethods
|
9
14
|
# Registers an instance variable as a child entity
|
@@ -53,10 +58,14 @@ module Synapse
|
|
53
58
|
# If the event is relative to this member, its parameters will be used to change
|
54
59
|
# the state of this member
|
55
60
|
#
|
56
|
-
# @abstract
|
57
61
|
# @param [EventMessage] event
|
58
62
|
# @return [undefined]
|
59
|
-
def handle_event(event)
|
63
|
+
def handle_event(event)
|
64
|
+
wire = self.wire_registry.wire_for event.payload_type
|
65
|
+
if wire
|
66
|
+
invoke_wire event, wire
|
67
|
+
end
|
68
|
+
end
|
60
69
|
|
61
70
|
private
|
62
71
|
|
@@ -17,24 +17,6 @@ module Synapse
|
|
17
17
|
def schedule_snapshot(type_identifier, aggregate_id); end
|
18
18
|
end
|
19
19
|
|
20
|
-
# Snapshot taker that uses EventMachine to defer scheduling of snapshots
|
21
|
-
class DeferredSnapshotTaker < SnapshotTaker
|
22
|
-
# @param [SnapshotTaker] delegate
|
23
|
-
# @return [undefined]
|
24
|
-
def initialize(delegate)
|
25
|
-
@delegate = delegate
|
26
|
-
end
|
27
|
-
|
28
|
-
# @param [String] type_identifier
|
29
|
-
# @param [Object] aggregate_id
|
30
|
-
# @return [undefined]
|
31
|
-
def schedule_snapshot(type_identifier, aggregate_id)
|
32
|
-
EventMachine.defer do
|
33
|
-
@delegate.schedule_snapshot type_identifier, aggregate_id
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
20
|
# Snapshot taker that uses the actual aggregate and its state to create a snapshot event
|
39
21
|
class AggregateSnapshotTaker < SnapshotTaker
|
40
22
|
# @param [SnapshotEventStore] event_store
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'synapse/event_sourcing/aggregate_factory'
|
2
|
+
require 'synapse/event_sourcing/conflict_resolver'
|
3
|
+
require 'synapse/event_sourcing/repository'
|
4
|
+
require 'synapse/event_sourcing/storage_listener'
|
5
|
+
require 'synapse/event_sourcing/stream_decorator'
|
6
|
+
|
7
|
+
require 'synapse/event_sourcing/member'
|
8
|
+
require 'synapse/event_sourcing/aggregate_root'
|
9
|
+
require 'synapse/event_sourcing/entity'
|
10
|
+
|
11
|
+
require 'synapse/event_sourcing/snapshot/count_stream'
|
12
|
+
require 'synapse/event_sourcing/snapshot/count_trigger'
|
13
|
+
require 'synapse/event_sourcing/snapshot/taker'
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'synapse/event_store/mongo/cursor_event_stream'
|
2
|
+
require 'synapse/event_store/mongo/event_store'
|
3
|
+
|
4
|
+
require 'synapse/event_store/mongo/storage_strategy'
|
5
|
+
require 'synapse/event_store/mongo/per_commit_strategy'
|
6
|
+
require 'synapse/event_store/mongo/per_event_strategy'
|
7
|
+
|
8
|
+
require 'synapse/event_store/mongo/template'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Partitioning
|
3
|
+
module AMQP
|
4
|
+
# Implementation of a queue reader that subscribes to an AMQP queue
|
5
|
+
class AMQPQueueReader < QueueReader
|
6
|
+
# The behavior when a message is not acknowledged by a message handler
|
7
|
+
#
|
8
|
+
# When a message is explicitly rejected, this usually indicates that there was an
|
9
|
+
# error while processing the message. When the message is rejected, it can either be
|
10
|
+
# put back on the queue so that it can be retried later, or it can be routed as a dead
|
11
|
+
# letter if using RabbitMQ.
|
12
|
+
#
|
13
|
+
# @see http://www.rabbitmq.com/dlx.html
|
14
|
+
# @return [Boolean] Default value is true
|
15
|
+
attr_accessor :requeue_on_nack
|
16
|
+
|
17
|
+
# @param [AMQP::Queue] queue
|
18
|
+
# @param [AMQP::Channel] channel
|
19
|
+
# @param [MessageUnpacker] unpacker
|
20
|
+
# @return [undefined]
|
21
|
+
def initialize(queue, channel, unpacker)
|
22
|
+
@queue = queue
|
23
|
+
@channel = channel
|
24
|
+
@requeue_on_nack = true
|
25
|
+
end
|
26
|
+
|
27
|
+
# @yield [MessageReceipt] Receipt of the message taken off the queue
|
28
|
+
# @return [undefined]
|
29
|
+
def subscribe(&handler)
|
30
|
+
@queue.subscribe do |headers, packed|
|
31
|
+
receipt = MessageReceipt.new headers.delivery_tag, packed, @queue.name
|
32
|
+
handler.call receipt
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param [MessageReceipt] receipt
|
37
|
+
# @return [undefined]
|
38
|
+
def ack_message(receipt)
|
39
|
+
@channel.acknowledge receipt.tag
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param [MessageReceipt] receipt
|
43
|
+
# @return [undefined]
|
44
|
+
def nack_message(receipt)
|
45
|
+
@channel.reject receipt.tag, @requeue_on_nack
|
46
|
+
end
|
47
|
+
end # AMQPQueueReader
|
48
|
+
end # AMQP
|
49
|
+
end # Partitioning
|
50
|
+
end # Synapse
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Partitioning
|
3
|
+
module AMQP
|
4
|
+
# Implementation of a queue writer that publishes packed messages to an AMQP exchange
|
5
|
+
class AMQPQueueWriter < QueueWriter
|
6
|
+
# @return [Hash] Additional options that will be used when publishing messages
|
7
|
+
attr_accessor :publish_options
|
8
|
+
|
9
|
+
# @param [AMQP::Exchange] exchange
|
10
|
+
# @param [RoutingKeyResolver] key_resolver
|
11
|
+
# @return [undefined]
|
12
|
+
def initialize(exchange, key_resolver)
|
13
|
+
@exchange = exchange
|
14
|
+
@key_resolver = key_resolver
|
15
|
+
@publish_options = Hash.new
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param [Object] packed
|
19
|
+
# @param [Message] unpacked
|
20
|
+
# @return [undefined]
|
21
|
+
def put_message(packed, unpacked)
|
22
|
+
publish_options = {
|
23
|
+
routing_key: @key_resolver.resolve_key(unpacked)
|
24
|
+
}
|
25
|
+
|
26
|
+
@exchange.publish(packed, @publish_options.merge(publish_options))
|
27
|
+
end
|
28
|
+
end # AMQPQueueWriter
|
29
|
+
end # AMQP
|
30
|
+
end # Partitioning
|
31
|
+
end # Synapse
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Partitioning
|
3
|
+
module AMQP
|
4
|
+
# Represents a mechanism for determining the routing key to use when publishing a message
|
5
|
+
class RoutingKeyResolver
|
6
|
+
# Returns the routing key to use when publishing the given message
|
7
|
+
#
|
8
|
+
# @param [Message] message
|
9
|
+
# @return [String]
|
10
|
+
def resolve_key(message); end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Implementation of a routing key resolver that uses the message payload's module name
|
14
|
+
class ModuleRoutingKeyResolver
|
15
|
+
# @param [Message] message
|
16
|
+
# @return [String]
|
17
|
+
def resolve_key(message)
|
18
|
+
type = message.payload_type.to_s
|
19
|
+
type.deconstantize.underscore.tap do |key|
|
20
|
+
key.tr! '/', '.'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end # AMQP
|
25
|
+
end # Partitioning
|
26
|
+
end # Synapse
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Partitioning
|
3
|
+
# Queue reader that dequeues messages from an in-memory Ruby queue
|
4
|
+
class MemoryQueueReader < QueueReader
|
5
|
+
# @param [Queue] queue
|
6
|
+
# @param [String] name Name of the queue being read from
|
7
|
+
# @return [undefined]
|
8
|
+
def initialize(queue, name)
|
9
|
+
@queue = queue
|
10
|
+
@name = name
|
11
|
+
end
|
12
|
+
|
13
|
+
# @yield [MessageReceipt] Receipt of the message taken off the queue
|
14
|
+
# @return [undefined]
|
15
|
+
def subscribe(&handler)
|
16
|
+
loop do
|
17
|
+
packed = @queue.pop
|
18
|
+
|
19
|
+
receipt = MessageReceipt.new 0, packed, @name
|
20
|
+
handler.call receipt
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param [MessageReceipt] receipt
|
25
|
+
# @return [undefined]
|
26
|
+
def nack_message(receipt)
|
27
|
+
@queue.push receipt.packed
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Partitioning
|
3
|
+
# Queue writer that pushes message into an in-memory Ruby queue
|
4
|
+
class MemoryQueueWriter < QueueWriter
|
5
|
+
# @param [Queue] queue
|
6
|
+
# @return [undefined]
|
7
|
+
def initialize(queue)
|
8
|
+
@queue = queue
|
9
|
+
end
|
10
|
+
|
11
|
+
# @param [Object] packed
|
12
|
+
# @param [Message] unpacked
|
13
|
+
# @return [undefined]
|
14
|
+
def put_message(packed, unpacked)
|
15
|
+
@queue.push packed
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Partitioning
|
3
|
+
# Receipt used to track messages taken off of a queue
|
4
|
+
class MessageReceipt
|
5
|
+
# @return [Object] The transport mechanism's record of the message
|
6
|
+
attr_reader :tag
|
7
|
+
|
8
|
+
# @return [Object] The packed message from the transport
|
9
|
+
attr_reader :packed
|
10
|
+
|
11
|
+
# @return [String] The name of the queue the message was read from
|
12
|
+
attr_reader :queue_name
|
13
|
+
|
14
|
+
# @param [Object] tag
|
15
|
+
# @param [Object] packed
|
16
|
+
# @param [String] queue_name
|
17
|
+
# @return [undefined]
|
18
|
+
def initialize(tag, packed, queue_name)
|
19
|
+
@tag = tag
|
20
|
+
@packed = packed
|
21
|
+
@queue_name = queue_name
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Partitioning
|
3
|
+
# Implementation of a message packer that serializes the metadata and payload of any
|
4
|
+
# message and then serializes the entire message so it can go onto the wire.
|
5
|
+
class JsonMessagePacker < MessagePacker
|
6
|
+
# @param [Serializer] serializer
|
7
|
+
# @return [undefined]
|
8
|
+
def initialize(serializer)
|
9
|
+
@serializer = serializer
|
10
|
+
|
11
|
+
@serialization_target = String
|
12
|
+
# Ideally, we want to serialize the metadata and payload to hashes so that we don't
|
13
|
+
# duplicate serialization while serializing the message as a whole to a string
|
14
|
+
if serializer.can_serialize_to? Hash
|
15
|
+
@serialization_target = Hash
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param [Message] unpacked
|
20
|
+
# @return [String]
|
21
|
+
def pack_message(unpacked)
|
22
|
+
message_type = type_for unpacked
|
23
|
+
|
24
|
+
metadata = @serializer.serialize unpacked.metadata, @serialization_target
|
25
|
+
payload = @serializer.serialize unpacked.payload, @serialization_target
|
26
|
+
|
27
|
+
packed = {
|
28
|
+
message_type: message_type,
|
29
|
+
id: unpacked.id,
|
30
|
+
metadata: metadata.content,
|
31
|
+
payload: payload.content,
|
32
|
+
payload_type: payload.type.name,
|
33
|
+
payload_revision: payload.type.revision
|
34
|
+
}
|
35
|
+
|
36
|
+
if [:event, :domain_event].include? message_type
|
37
|
+
pack_event unpacked, packed
|
38
|
+
end
|
39
|
+
|
40
|
+
if :domain_event == message_type
|
41
|
+
pack_domain_event unpacked, packed
|
42
|
+
end
|
43
|
+
|
44
|
+
JSON.dump packed
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Packs additional attributes specific to event messages
|
50
|
+
#
|
51
|
+
# @param [EventMessage] unpacked
|
52
|
+
# @param [Hash] packed
|
53
|
+
# @return [undefined]
|
54
|
+
def pack_event(unpacked, packed)
|
55
|
+
additional = {
|
56
|
+
timestamp: unpacked.timestamp.to_i
|
57
|
+
}
|
58
|
+
packed.merge! additional
|
59
|
+
end
|
60
|
+
|
61
|
+
# Packs additional attributes specific to domain event messages
|
62
|
+
#
|
63
|
+
# @param [DomainEventMessage] unpacked
|
64
|
+
# @param [Hash] packed
|
65
|
+
# @return [undefined]
|
66
|
+
def pack_domain_event(unpacked, packed)
|
67
|
+
additional = {
|
68
|
+
aggregate_id: unpacked.aggregate_id.to_s,
|
69
|
+
sequence_number: unpacked.sequence_number
|
70
|
+
}
|
71
|
+
packed.merge! additional
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns the packed type for the given message
|
75
|
+
#
|
76
|
+
# @raise [ArgumentError] If the given message isn't supported by this packer
|
77
|
+
# @param [Message] unpacked
|
78
|
+
# @return [Symbol]
|
79
|
+
def type_for(unpacked)
|
80
|
+
case unpacked
|
81
|
+
when Command::CommandMessage
|
82
|
+
:command
|
83
|
+
when Domain::DomainEventMessage
|
84
|
+
:domain_event
|
85
|
+
when Domain::EventMessage
|
86
|
+
:event
|
87
|
+
else
|
88
|
+
raise ArgumentError, 'Unknown message type'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end # JsonMessagePacker
|
92
|
+
end # Partitioning
|
93
|
+
end # Synapse
|