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