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,83 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Partitioning
|
3
|
+
# Implementation of a message unpacker that unpacks messages that come off the wire in the
|
4
|
+
# format described by JsonMessagePacker
|
5
|
+
class JsonMessageUnpacker < MessageUnpacker
|
6
|
+
# @param [Serializer] serializer
|
7
|
+
# @return [undefined]
|
8
|
+
def initialize(serializer)
|
9
|
+
@serializer = serializer
|
10
|
+
end
|
11
|
+
|
12
|
+
# @param [String] packed
|
13
|
+
# @return [Message]
|
14
|
+
def unpack_message(message)
|
15
|
+
packed = JSON.load message
|
16
|
+
packed.symbolize_keys!
|
17
|
+
|
18
|
+
message_type = packed.fetch(:message_type).to_sym
|
19
|
+
builder = builder_for(message_type).new
|
20
|
+
|
21
|
+
builder.id = packed.fetch :id
|
22
|
+
builder.metadata = deserialize_metadata packed
|
23
|
+
builder.payload = deserialize_payload packed
|
24
|
+
|
25
|
+
if [:event, :domain_event].include? message_type
|
26
|
+
timestamp = packed.fetch :timestamp
|
27
|
+
builder.timestamp = Time.at timestamp
|
28
|
+
end
|
29
|
+
|
30
|
+
if :domain_event == message_type
|
31
|
+
builder.aggregate_id = packed.fetch :aggregate_id
|
32
|
+
builder.sequence_number = packed.fetch :sequence_number
|
33
|
+
end
|
34
|
+
|
35
|
+
builder.build
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Deserializes the metadata of the given packed message
|
41
|
+
#
|
42
|
+
# @param [Hash] packed
|
43
|
+
# @return [Hash]
|
44
|
+
def deserialize_metadata(packed)
|
45
|
+
content = packed.fetch :metadata
|
46
|
+
@serializer.deserialize Serialization::SerializedMetadata.new content, content.class
|
47
|
+
end
|
48
|
+
|
49
|
+
# Deserializes the payload of the given packed message
|
50
|
+
#
|
51
|
+
# @param [Hash] packed
|
52
|
+
# @return [Object]
|
53
|
+
def deserialize_payload(packed)
|
54
|
+
content = packed.fetch :payload
|
55
|
+
name = packed.fetch :payload_type
|
56
|
+
revision = packed.fetch :payload_revision
|
57
|
+
|
58
|
+
serialized_type = Serialization::SerializedType.new name, revision
|
59
|
+
serialized_object = Serialization::SerializedObject.new content, content.class, serialized_type
|
60
|
+
|
61
|
+
@serializer.deserialize serialized_object
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the builder type for the given message type
|
65
|
+
#
|
66
|
+
# @raise [ArgumentError] If message type isn't supported by this unpacker
|
67
|
+
# @param [Symbol] type
|
68
|
+
# @return [Class]
|
69
|
+
def builder_for(type)
|
70
|
+
case type
|
71
|
+
when :command
|
72
|
+
Command::CommandMessage.builder
|
73
|
+
when :domain_event
|
74
|
+
Domain::DomainEventMessage.builder
|
75
|
+
when :event
|
76
|
+
Domain::EventMessage.builder
|
77
|
+
else
|
78
|
+
raise ArgumentError, 'Unknown message type'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end # JsonMessageUnpacker
|
82
|
+
end # Partitioning
|
83
|
+
end # Synapse
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Partitioning
|
3
|
+
# Represents a mechanism for packing a message into a string so that it can be either stored
|
4
|
+
# in a file or passed over a transport mechanism to be unpacked at the receiver
|
5
|
+
#
|
6
|
+
# This implementation simply returns the message as-is
|
7
|
+
class MessagePacker
|
8
|
+
# @param [Message] unpacked
|
9
|
+
# @return [String]
|
10
|
+
def pack_message(unpacked)
|
11
|
+
unpacked
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Represents a mechanism for unpacking a message that has been packed for storage in a file
|
16
|
+
# or transport from a producer
|
17
|
+
#
|
18
|
+
# This implementation simply returns the message as-is
|
19
|
+
class MessageUnpacker
|
20
|
+
# @param [String] packed
|
21
|
+
# @return [Message]
|
22
|
+
def unpack_message(packed)
|
23
|
+
packed
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Partitioning
|
3
|
+
# Represents a mechanism for taking messages off a queue and handling the acknowledgement
|
4
|
+
# or rejection of each message
|
5
|
+
#
|
6
|
+
# @abstract
|
7
|
+
class QueueReader
|
8
|
+
# Subscribes the given handler to the queue
|
9
|
+
#
|
10
|
+
# Depending on the implementation, this method may or may not return immediately. It should
|
11
|
+
# be assumed that the method will block until a message is received and then will go back
|
12
|
+
# to blocking after the given callback is invoked.
|
13
|
+
#
|
14
|
+
# @abstract
|
15
|
+
# @yield [MessageReceipt] Receipt of the message taken off the queue
|
16
|
+
# @return [undefined]
|
17
|
+
def subscribe(&handler); end
|
18
|
+
|
19
|
+
# Acknowledges the message, removing it from the original queue
|
20
|
+
#
|
21
|
+
# @param [MessageReceipt] receipt
|
22
|
+
# @return [undefined]
|
23
|
+
def ack_message(receipt); end
|
24
|
+
|
25
|
+
# Attempts to notify the original queue that the message was not processed
|
26
|
+
#
|
27
|
+
# @param [MessageReceipt] receipt
|
28
|
+
# @return [undefined]
|
29
|
+
def nack_message(receipt); end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Partitioning
|
3
|
+
# Represents a mechanism for writing packed messages to a queue
|
4
|
+
# @abstract
|
5
|
+
class QueueWriter
|
6
|
+
# Enqueues the given message
|
7
|
+
#
|
8
|
+
# Depending on the implementation, this method may or may not block until the message has
|
9
|
+
# been enqueued.
|
10
|
+
#
|
11
|
+
# @param [Object] packed
|
12
|
+
# @param [Message] unpacked
|
13
|
+
# @return [undefined]
|
14
|
+
def put_message(packed, unpacked); end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Partitioning
|
3
|
+
extend ActiveSupport::Autoload
|
4
|
+
|
5
|
+
# Optional queues
|
6
|
+
autoload :AMQP
|
7
|
+
|
8
|
+
autoload :MemoryQueueReader
|
9
|
+
autoload :MemoryQueueWriter
|
10
|
+
|
11
|
+
# Optional message packing
|
12
|
+
autoload :JsonMessagePacker, 'synapse/partitioning/packing/json_packer'
|
13
|
+
autoload :JsonMessageUnpacker, 'synapse/partitioning/packing/json_unpacker'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'synapse/partitioning/message_receipt'
|
18
|
+
require 'synapse/partitioning/packing'
|
19
|
+
require 'synapse/partitioning/queue_reader'
|
20
|
+
require 'synapse/partitioning/queue_writer'
|
@@ -26,10 +26,9 @@ module Synapse
|
|
26
26
|
begin
|
27
27
|
aggregate = perform_load aggregate_id, expected_version
|
28
28
|
|
29
|
-
aggregate
|
30
|
-
|
31
|
-
|
32
|
-
aggregate
|
29
|
+
register_aggregate(aggregate).tap do
|
30
|
+
register_listener LockCleaningUnitOfWorkListener.new aggregate_id, @lock_manager
|
31
|
+
end
|
33
32
|
rescue
|
34
33
|
@logger.debug 'Excepton raised while loading an aggregate, releasing lock'
|
35
34
|
|
@@ -70,10 +69,6 @@ module Synapse
|
|
70
69
|
# @param [Integer] expected_version
|
71
70
|
# @return [AggregateRoot]
|
72
71
|
def perform_load(aggregate_id, expected_version); end
|
73
|
-
|
74
|
-
def logger
|
75
|
-
|
76
|
-
end
|
77
72
|
end
|
78
73
|
|
79
74
|
# Unit of work listener that releases the lock on an aggregate when the unit of work
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'bson'
|
2
|
+
|
3
|
+
module Synapse
|
4
|
+
module Serialization
|
5
|
+
# Converter that converts an ordered hash from BSON into a regular Ruby hash
|
6
|
+
class OrderedHashToHashConverter
|
7
|
+
include Converter
|
8
|
+
|
9
|
+
converts BSON::OrderedHash, Hash
|
10
|
+
|
11
|
+
# @param [Object] original
|
12
|
+
# @return [Object]
|
13
|
+
def convert_content(original)
|
14
|
+
converted = Hash.new
|
15
|
+
|
16
|
+
original.each do |key, value|
|
17
|
+
if value.is_a? BSON::OrderedHash
|
18
|
+
value = convert_content value
|
19
|
+
end
|
20
|
+
|
21
|
+
converted[key] = value
|
22
|
+
end
|
23
|
+
|
24
|
+
converted
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Serialization
|
3
|
+
# Implementation of a serializer that uses the hashing features of ActiveModel and Virtus
|
4
|
+
#
|
5
|
+
# If the object being serialized or deserialized is a hash, it will be untouched
|
6
|
+
#
|
7
|
+
# Objects being serialized by this implementation must meet the following requirements:
|
8
|
+
# * Respond to #attributes and #attributes=
|
9
|
+
# * #attributes= must work without the deserializer calling #initialize
|
10
|
+
#
|
11
|
+
# If either one of these are not met, the serializer may fail instantly or the deserialized
|
12
|
+
# object could be put in a bad state
|
13
|
+
class AttributeSerializer < Serializer
|
14
|
+
# This serializer doesn't provide any configuration options
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
# @param [Object] content
|
19
|
+
# @return [Object]
|
20
|
+
def perform_serialize(content)
|
21
|
+
if content.is_a? Hash
|
22
|
+
content
|
23
|
+
else
|
24
|
+
content.attributes
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param [Object] content
|
29
|
+
# @param [Class] type
|
30
|
+
# @return [Object]
|
31
|
+
def perform_deserialize(content, type)
|
32
|
+
if Hash == type
|
33
|
+
content
|
34
|
+
else
|
35
|
+
# Allocate the target object but don't call #initialize
|
36
|
+
object = type.allocate
|
37
|
+
object.attributes = content
|
38
|
+
object
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [Class]
|
43
|
+
def native_content_type
|
44
|
+
Hash
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -36,7 +36,7 @@ module Synapse
|
|
36
36
|
# @param [Class] representation_type
|
37
37
|
# @return [Boolean]
|
38
38
|
def can_serialize_to?(representation_type)
|
39
|
-
converter_factory.has_converter?(
|
39
|
+
converter_factory.has_converter?(native_content_type, representation_type)
|
40
40
|
end
|
41
41
|
|
42
42
|
# @param [SerializedType] serialized_type
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Synapse
|
2
|
+
module Serialization
|
3
|
+
extend ActiveSupport::Autoload
|
4
|
+
|
5
|
+
# Optional converters
|
6
|
+
autoload_at 'synapse/serialization/converter/json' do
|
7
|
+
autoload :JsonToObjectConverter
|
8
|
+
autoload :ObjectToJsonConverter
|
9
|
+
end
|
10
|
+
|
11
|
+
autoload_at 'synapse/serialization/converter/ox' do
|
12
|
+
autoload :XmlToOxDocumentConverter
|
13
|
+
autoload :OxDocumentToXmlConverter
|
14
|
+
end
|
15
|
+
|
16
|
+
autoload :OrderedHashToHashConverter, 'synapse/serialization/converter/bson'
|
17
|
+
|
18
|
+
# Optional serializers
|
19
|
+
autoload :AttributeSerializer, 'synapse/serialization/serializer/attribute'
|
20
|
+
autoload :OjSerializer, 'synapse/serialization/serializer/oj'
|
21
|
+
autoload :OxSerializer, 'synapse/serialization/serializer/ox'
|
22
|
+
autoload :MarshalSerializer, 'synapse/serialization/serializer/marshal'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'synapse/serialization/converter'
|
27
|
+
require 'synapse/serialization/converter/chain'
|
28
|
+
require 'synapse/serialization/converter/factory'
|
29
|
+
require 'synapse/serialization/converter/identity'
|
30
|
+
|
31
|
+
require 'synapse/serialization/errors'
|
32
|
+
|
33
|
+
require 'synapse/serialization/lazy_object'
|
34
|
+
require 'synapse/serialization/revision_resolver'
|
35
|
+
require 'synapse/serialization/serialized_object'
|
36
|
+
require 'synapse/serialization/serialized_type'
|
37
|
+
require 'synapse/serialization/serializer'
|
38
|
+
|
39
|
+
require 'synapse/serialization/message/data'
|
40
|
+
require 'synapse/serialization/message/metadata'
|
41
|
+
require 'synapse/serialization/message/serialization_aware'
|
42
|
+
require 'synapse/serialization/message/serialization_aware_message'
|
43
|
+
require 'synapse/serialization/message/serialized_message_builder'
|
44
|
+
require 'synapse/serialization/message/serialized_message'
|
45
|
+
require 'synapse/serialization/message/serialized_object_cache'
|
46
|
+
require 'synapse/serialization/message/serializer'
|
data/lib/synapse/uow/factory.rb
CHANGED
@@ -15,13 +15,13 @@ module Synapse
|
|
15
15
|
# @return [UnitOfWork]
|
16
16
|
def create
|
17
17
|
unit = UnitOfWork.new @provider
|
18
|
+
unit.tap do
|
19
|
+
if @transaction_manager
|
20
|
+
unit.transaction_manager = @transaction_manager
|
21
|
+
end
|
18
22
|
|
19
|
-
|
20
|
-
unit.transaction_manager = @transaction_manager
|
23
|
+
unit.start
|
21
24
|
end
|
22
|
-
|
23
|
-
unit.start
|
24
|
-
unit
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
data/lib/synapse/uow/uow.rb
CHANGED
@@ -48,13 +48,13 @@ module Synapse
|
|
48
48
|
return similar
|
49
49
|
end
|
50
50
|
|
51
|
-
aggregate.
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
@aggregates.store aggregate, storage_listener
|
51
|
+
aggregate.tap do
|
52
|
+
aggregate.add_registration_listener do |event|
|
53
|
+
publish_event event, event_bus
|
54
|
+
end
|
56
55
|
|
57
|
-
|
56
|
+
@aggregates.store aggregate, storage_listener
|
57
|
+
end
|
58
58
|
end
|
59
59
|
|
60
60
|
# Buffers an event for publication to the given event bus until this unit of work is
|
@@ -65,15 +65,15 @@ module Synapse
|
|
65
65
|
# @return [EventMessage] The event that will be published to the event bus
|
66
66
|
def publish_event(event, event_bus)
|
67
67
|
event = @listeners.on_event_registered self, event
|
68
|
+
event.tap do
|
69
|
+
begin
|
70
|
+
events = @events.fetch event_bus
|
71
|
+
rescue KeyError
|
72
|
+
events = @events.store event_bus, Array.new
|
73
|
+
end
|
68
74
|
|
69
|
-
|
70
|
-
events = @events.fetch event_bus
|
71
|
-
rescue KeyError
|
72
|
-
events = @events.store event_bus, Array.new
|
75
|
+
events.push event
|
73
76
|
end
|
74
|
-
|
75
|
-
events.push event
|
76
|
-
event
|
77
77
|
end
|
78
78
|
|
79
79
|
# Sets the transaction manager that will be used by this unit of work
|
data/lib/synapse/uow.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'synapse/uow/factory'
|
2
|
+
require 'synapse/uow/listener'
|
3
|
+
require 'synapse/uow/listener_collection'
|
4
|
+
require 'synapse/uow/nesting'
|
5
|
+
require 'synapse/uow/provider'
|
6
|
+
require 'synapse/uow/storage_listener'
|
7
|
+
require 'synapse/uow/transaction_manager'
|
8
|
+
require 'synapse/uow/uow'
|
@@ -63,7 +63,7 @@ module Synapse
|
|
63
63
|
|
64
64
|
if upcaster.can_upcast? serialized_type
|
65
65
|
serialized_object = converter_factory.convert serialized_object, upcaster.expected_content_type
|
66
|
-
expected_types = upcaster.upcast_type
|
66
|
+
expected_types = upcaster.upcast_type serialized_type
|
67
67
|
|
68
68
|
upcast_objects.concat(perform_upcast(upcaster, serialized_object, expected_types, upcast_context))
|
69
69
|
else
|
data/lib/synapse/version.rb
CHANGED