Skip to content

Commit

Permalink
Merge pull request #50 from Sandthorn/move_serialization_to_driver
Browse files Browse the repository at this point in the history
Move serialization to driver
  • Loading branch information
hallgren committed Feb 11, 2016
2 parents 11d4037 + 85a7543 commit 6aa2ae6
Show file tree
Hide file tree
Showing 10 changed files with 48 additions and 166 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
source 'https://rubygems.org'

# Specify your gem's dependencies in sandthorn.gemspec
gemspec
gemspec
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,20 +182,21 @@ Sandthorn.configure do |conf|
end
```

## Data serialization / deserialization
## Data serialization

Its possible to configure how events and snapshots are serialized / deserialized. The default are YAML but can be overloaded in the configure block.
Its possible to configure how events and snapshots are serialized. Since version `0.11.0` of Sandthorn the serialization of events and snapshots is the responsibility of the driver. This means drivers can have different serialization mechanism, independent of each other.

The default serializer of the Sequel driver is YAML.

Here's how to use the Oj gem to serialize data in the Sequel driver:

```ruby
Sandthorn.configure do |conf|
conf.serializer = Proc.new { |data| Oj::dump(data) }
conf.deserializer = Proc.new { |data| Oj::load(data) }
conf.snapshot_serializer = Proc.new { |data| Oj::dump(data) }
conf.snapshot_deserializer = Proc.new { |data| Oj::load(data) }
end
oj_driver = SandthornDriverSequel.driver_from_connection(connection: Sequel.sqlite) { |conf|
conf.event_serializer = Proc.new { |data| Oj::dump(data) }
conf.event_deserializer = Proc.new { |data| Oj::load(data) }
}
```


# Usage

## Aggregate Root
Expand Down
71 changes: 3 additions & 68 deletions lib/sandthorn.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module Sandthorn
class << self
extend Forwardable

def_delegators :configuration, :event_stores, :serialize, :deserialize, :serialize_snapshot, :deserialize_snapshot
def_delegators :configuration, :event_stores

def default_event_store
event_stores.default_store
Expand All @@ -28,8 +28,6 @@ def configuration
@configuration ||= Configuration.new
end



def generate_aggregate_id
SecureRandom.uuid
end
Expand All @@ -46,12 +44,8 @@ def get_aggregate aggregate_id, aggregate_type
event_store_for(aggregate_type).get_aggregate_events_from_snapshot aggregate_id
end

def save_snapshot(
aggregate_type: missing_key(:aggregate_type),
aggregate_snapshot: missing_key(:aggregate_snapshot),
aggregate_id: missing_key(:aggregate_id)
)
event_store_for(aggregate_type).save_snapshot(aggregate_snapshot, aggregate_id)
def save_snapshot(aggregate)
event_store_for(aggregate.class).save_snapshot(aggregate)
end

def get_aggregate_list_by_type aggregate_type
Expand All @@ -62,8 +56,6 @@ def get_events event_store: :default, aggregate_types: [], take: 0, after_sequen
event_store = find_event_store(event_store)
events = event_store.get_events aggregate_types: aggregate_types, take: take, after_sequence_number: after_sequence_number
events.map do |event|
event[:event_args] = deserialize event[:event_data]
event.delete(:event_data)
Event.new(event)
end
end
Expand Down Expand Up @@ -111,67 +103,10 @@ def event_store=(store)
@event_stores = EventStores.new(store)
end

def serializer=(block)
@serializer = block if block.is_a? Proc
end

def deserializer=(block)
@deserializer = block if block.is_a? Proc
end

def serializer
@serializer || default_serializer
end

def deserializer
@deserializer || default_deserializer
end

def default_serializer
-> (data) { YAML.dump(data) }
end

def default_deserializer
-> (data) { YAML.load(data) }
end

def serialize(data)
serializer.call(data)
end

def deserialize(data)
deserializer.call(data)
end

def snapshot_serializer=(block)
@snapshot_serializer = block if block.is_a? Proc
end

def snapshot_deserializer=(block)
@snapshot_deserializer = block if block.is_a? Proc
end

def snapshot_serializer
@snapshot_serializer || default_serializer
end

def snapshot_deserializer
@snapshot_deserializer || default_deserializer
end

def serialize_snapshot(data)
snapshot_serializer.call(data)
end

def deserialize_snapshot data
snapshot_deserializer.call(data)
end

def map_types= data
@event_stores.map_types data
end


alias_method :event_stores=, :event_store=
end
end
Expand Down
49 changes: 15 additions & 34 deletions lib/sandthorn/aggregate_root_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module Base
attr_reader :aggregate_trace_information

alias :id :aggregate_id
alias :aggregate_version :aggregate_current_event_version


def aggregate_base_initialize
Expand All @@ -19,12 +20,7 @@ def aggregate_base_initialize
end

def save
aggregate_events.each do |event|
event[:event_data] = Sandthorn.serialize event[:event_args]
event[:event_args] = nil #Not send extra data over the wire
end

unless aggregate_events.empty?
if aggregate_events.any?
Sandthorn.save_events(
aggregate_events,
aggregate_id,
Expand Down Expand Up @@ -90,26 +86,17 @@ def aggregate_find aggregate_id
unless events && !events.empty?
raise Sandthorn::Errors::AggregateNotFound
end

if first_event_snapshot?(events)
transformed_snapshot_event = events.first.merge(event_args: Sandthorn.deserialize_snapshot(events.first[:event_data]))
events.shift
end

transformed_events = events.map do |e|
e.merge(event_args: Sandthorn.deserialize(e[:event_data]))
end
aggregate_build ([transformed_snapshot_event] + transformed_events).compact
aggregate_build events
end

def new *args, &block

aggregate = allocate
aggregate = create_new_empty_aggregate()
aggregate.aggregate_base_initialize
aggregate.aggregate_initialize

aggregate.default_attributes
aggregate.send :initialize, *args, &block
aggregate.send :initialize, *args, &block
aggregate.send :set_aggregate_id, Sandthorn.generate_aggregate_id

aggregate.aggregate_trace @@aggregate_trace_information do |aggr|
Expand All @@ -119,27 +106,26 @@ def new *args, &block

end



def aggregate_build events
current_aggregate_version = 0

if first_event_snapshot?(events)
aggregate = start_build_from_snapshot events
current_aggregate_version = aggregate.aggregate_originating_version
aggregate = events.first[:aggregate]
events.shift
else
aggregate = create_new_empty_aggregate
end

if events.any?
current_aggregate_version = events.last[:aggregate_version]
aggregate.send :set_orginating_aggregate_version!, current_aggregate_version
aggregate.send :set_current_aggregate_version!, current_aggregate_version
end

attributes = build_instance_vars_from_events events
current_aggregate_version = events.last[:aggregate_version] unless events.empty?
aggregate.send :clear_aggregate_events

aggregate.default_attributes
aggregate.send :set_orginating_aggregate_version!, current_aggregate_version
aggregate.send :set_current_aggregate_version!, current_aggregate_version
aggregate.send :aggregate_initialize

aggregate.send :set_instance_variables!, attributes
aggregate
end
Expand All @@ -159,7 +145,6 @@ def events(*event_names)
def build_instance_vars_from_events events
events.each_with_object({}) do |event, instance_vars|
event_args = event[:event_args]
event_name = event[:event_name]
attribute_deltas = event_args[:attribute_deltas]
unless attribute_deltas.nil?
deltas = attribute_deltas.each_with_object({}) do |delta, acc|
Expand All @@ -171,11 +156,7 @@ def build_instance_vars_from_events events
end

def first_event_snapshot? events
events.first[:event_name].to_sym == :aggregate_set_from_snapshot
end

def start_build_from_snapshot events
snapshot = events.first[:event_args][0]
events.first[:aggregate]
end

def create_new_empty_aggregate
Expand Down
31 changes: 1 addition & 30 deletions lib/sandthorn/aggregate_root_snapshot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,39 +13,10 @@ def aggregate_snapshot!
raise Errors::SnapshotError,
"Can't take snapshot on object with unsaved events"
end

@aggregate_snapshot = {
event_name: "aggregate_set_from_snapshot",
event_args: [self],
aggregate_version: @aggregate_current_event_version
}
end

def save_snapshot
unless aggregate_snapshot
raise Errors::SnapshotError, "No snapshot has been created!"
end
@aggregate_snapshot[:event_data] = Sandthorn.serialize_snapshot @aggregate_snapshot[:event_args]
@aggregate_snapshot[:event_args] = nil
Sandthorn.save_snapshot(aggregate_snapshot: @aggregate_snapshot, aggregate_id: @aggregate_id, aggregate_type: self.class)
@aggregate_snapshot = nil
end
private
def aggregate_create_event_when_extended
self.aggregate_snapshot!
vars = extract_relevant_aggregate_instance_variables
vars.each do |var_name|
value = instance_variable_get var_name
dump = Marshal.dump(value)
store_aggregate_instance_variable var_name, dump
end

@aggregate_snapshot[:event_data] = Sandthorn
.serialize_snapshot aggregate_snapshot[:event_args]

@aggregate_snapshot[:event_args] = nil
Sandthorn.save_snapshot aggregate_snapshot, aggregate_id
@aggregate_snapshot = nil
Sandthorn.save_snapshot(self)
end
end
end
2 changes: 1 addition & 1 deletion lib/sandthorn/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Sandthorn
VERSION = "0.10.3"
VERSION = "0.11.0"
end
2 changes: 1 addition & 1 deletion sandthorn.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "autotest-standalone"
spec.add_development_dependency "sqlite3"
spec.add_development_dependency "coveralls"
spec.add_development_dependency "sandthorn_driver_sequel", "~> 2.0"
spec.add_development_dependency "sandthorn_driver_sequel", ">= 3.0"
end
8 changes: 4 additions & 4 deletions spec/aggregate_root_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def no_state_change_only_empty_event
it "should set the values" do
expect(subject.name).to eql "Mogge"
expect(subject.sex).to eql "hen"
expect{subject.writer}.to raise_error
expect{subject.writer}.to raise_error NoMethodError
end
end

Expand Down Expand Up @@ -117,12 +117,12 @@ def no_state_change_only_empty_event

context "when changing writer (attr_writer)" do
it "should raise error" do
expect{dirty_object.change_writer "new_writer"}.to raise_error
expect{dirty_object.change_writer "new_writer"}.to raise_error NameError
end
end

context "save" do
it "should not have events on aggregete after save" do
it "should not have events on aggregate after save" do
expect(dirty_object.save.aggregate_events.length).to eql 0
end

Expand All @@ -147,7 +147,7 @@ def no_state_change_only_empty_event
end

it "should raise error if trying to find id that not exist" do
expect{DirtyClass.find("666")}.to raise_error
expect{DirtyClass.find("666")}.to raise_error Sandthorn::Errors::AggregateNotFound
end
end

Expand Down
15 changes: 3 additions & 12 deletions spec/aggregate_snapshot_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -185,17 +185,10 @@ def a_test_account
expect(@account.unpaid_interest_balance).to be > 1000
end

it 'should store snapshot data in aggregate_snapshot' do
expect(@account.aggregate_snapshot).to be_a(Hash)
end

it 'should store aggregate_version in aggregate_snapshot' do
expect(@account.aggregate_snapshot[:aggregate_version]).to eql(@original_account.aggregate_current_event_version)
end

it 'should be able to load up from snapshot' do

events = [@account.aggregate_snapshot]
events = [aggregate: @account]
loaded = BankAccount.aggregate_build events

expect(loaded.balance).to eql(@original_account.balance)
Expand Down Expand Up @@ -227,9 +220,7 @@ def a_test_account

describe 'when saving to repository' do
let(:account) {a_test_account.extend Sandthorn::AggregateRootSnapshot}
it 'should raise an error if trying to save before creating a snapshot' do
expect(lambda {account.save_snapshot}).to raise_error (Sandthorn::Errors::SnapshotError)
end

it 'should not raise an error if snapshot was created' do
account.save
account.aggregate_snapshot!
Expand All @@ -243,7 +234,7 @@ def a_test_account
end

it 'should raise error if trying to create snapshot before events are saved on object' do
expect(lambda {account.aggregate_snapshot!}).to raise_error
expect(lambda {account.aggregate_snapshot!}).to raise_error Sandthorn::Errors::SnapshotError
end

it 'should not raise an error if trying to create snapshot on object when events are saved' do
Expand Down
Loading

0 comments on commit 6aa2ae6

Please sign in to comment.