sequent 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sequent/core/aggregate_repository.rb +94 -0
  3. data/lib/sequent/core/aggregate_root.rb +87 -0
  4. data/lib/sequent/core/base_command_handler.rb +39 -0
  5. data/lib/sequent/core/base_event_handler.rb +51 -0
  6. data/lib/sequent/core/command.rb +79 -0
  7. data/lib/sequent/core/command_record.rb +26 -0
  8. data/lib/sequent/core/command_service.rb +118 -0
  9. data/lib/sequent/core/core.rb +15 -0
  10. data/lib/sequent/core/event.rb +62 -0
  11. data/lib/sequent/core/event_record.rb +34 -0
  12. data/lib/sequent/core/event_store.rb +110 -0
  13. data/lib/sequent/core/helpers/association_validator.rb +39 -0
  14. data/lib/sequent/core/helpers/attribute_support.rb +207 -0
  15. data/lib/sequent/core/helpers/boolean_support.rb +36 -0
  16. data/lib/sequent/core/helpers/copyable.rb +25 -0
  17. data/lib/sequent/core/helpers/equal_support.rb +41 -0
  18. data/lib/sequent/core/helpers/helpers.rb +9 -0
  19. data/lib/sequent/core/helpers/mergable.rb +21 -0
  20. data/lib/sequent/core/helpers/param_support.rb +80 -0
  21. data/lib/sequent/core/helpers/self_applier.rb +45 -0
  22. data/lib/sequent/core/helpers/string_support.rb +22 -0
  23. data/lib/sequent/core/helpers/uuid_helper.rb +17 -0
  24. data/lib/sequent/core/record_sessions/active_record_session.rb +92 -0
  25. data/lib/sequent/core/record_sessions/record_sessions.rb +2 -0
  26. data/lib/sequent/core/record_sessions/replay_events_session.rb +306 -0
  27. data/lib/sequent/core/tenant_event_store.rb +18 -0
  28. data/lib/sequent/core/transactions/active_record_transaction_provider.rb +16 -0
  29. data/lib/sequent/core/transactions/no_transactions.rb +13 -0
  30. data/lib/sequent/core/transactions/transactions.rb +2 -0
  31. data/lib/sequent/core/value_object.rb +48 -0
  32. data/lib/sequent/migrations/migrate_events.rb +53 -0
  33. data/lib/sequent/migrations/migrations.rb +7 -0
  34. data/lib/sequent/sequent.rb +3 -0
  35. data/lib/sequent/test/command_handler_helpers.rb +101 -0
  36. data/lib/sequent/test/test.rb +1 -0
  37. data/lib/version.rb +3 -0
  38. metadata +38 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ae011c7edda7f721ed6973ee7dc7d34a1bd4bef1
4
- data.tar.gz: 29349622be7751d7d371ad9c56232aaa880e7aa1
3
+ metadata.gz: ffdbe1d8e772c148815f98b0d7baa96f32dd5870
4
+ data.tar.gz: 3b7ed01b18a23c5d7b8522a4b5d0c1af311be02f
5
5
  SHA512:
6
- metadata.gz: 5b832a64d8daaaf0008426f7acd40489934ff95ec666feac04751602305cf404d0b6f2b6a2dc0c89aa504de1bc5bcc468788d22f3f1f3a57240e99658fc01bfb
7
- data.tar.gz: 77e7beb17a0e7341d0cff134c03a6428ceec7fcb64266cf230058c493a37fe766713a2bd23bdf20095eeb14a90ede3cc2c0abfa5a49502e7f149bf213d8f7b04
6
+ metadata.gz: 5ec2840094aa3f915222047623d3f570563efa800736883c9d07b724773b0948b74602d26560337bb20455fde183a066181cbe594959ca52ebd1d73fea6a0f9e
7
+ data.tar.gz: 60ac65d32eda16ce7db21cb51ba0a61b6b741b86636185569b1683c76a983c35d6ca806a744befdd195df997ecb3b8df481fd17a15e312d648a7fa816d3127a7
@@ -0,0 +1,94 @@
1
+ module Sequent
2
+ module Core
3
+ # Repository for aggregates.
4
+ #
5
+ # Implements the Unit-Of-Work and Identity-Map patterns
6
+ # to ensure each aggregate is only loaded once per transaction
7
+ # and that you always get the same aggregate instance back.
8
+ #
9
+ # On commit all aggregates associated with the Unit-Of-Work are
10
+ # queried for uncommitted events. After persisting these events
11
+ # the uncommitted events are cleared from the aggregate.
12
+ #
13
+ # The repository is keeps track of the Unit-Of-Work per thread,
14
+ # so can be shared between threads.
15
+ class AggregateRepository
16
+ # Key used in thread local
17
+ AGGREGATES_KEY = 'Sequent::Core::AggregateRepository::aggregates'.to_sym
18
+
19
+ class NonUniqueAggregateId < Exception
20
+ def initialize(existing, new)
21
+ super "Duplicate aggregate #{new} with same key as existing #{existing}"
22
+ end
23
+ end
24
+
25
+ def initialize(event_store)
26
+ @event_store = event_store
27
+ clear
28
+ end
29
+
30
+ # Adds the given aggregate to the repository (or unit of work).
31
+ #
32
+ # Only when +commit+ is called all aggregates in the unit of work are 'processed'
33
+ # and all uncammited_events are stored in the +event_store+
34
+ #
35
+ def add_aggregate(aggregate)
36
+ if aggregates.has_key?(aggregate.id)
37
+ raise NonUniqueAggregateId.new(aggregate, aggregates[aggregate.id]) unless aggregates[aggregate.id].equal?(aggregate)
38
+ else
39
+ aggregates[aggregate.id] = aggregate
40
+ end
41
+ end
42
+
43
+ # Throws exception if not exists.
44
+ def ensure_exists(aggregate_id, clazz)
45
+ !load_aggregate(aggregate_id, clazz).nil?
46
+ end
47
+
48
+ # Loads aggregate by given id and class
49
+ # Returns the one in the current Unit Of Work otherwise loads it from history.
50
+ #
51
+ # If we implement snapshotting this is the place.
52
+ def load_aggregate(aggregate_id, clazz)
53
+ if aggregates.has_key?(aggregate_id)
54
+ result = aggregates[aggregate_id]
55
+ raise TypeError, "#{result.class} is not a #{clazz}" unless result.is_a?(clazz)
56
+ result
57
+ else
58
+ events = @event_store.load_events(aggregate_id)
59
+ aggregates[aggregate_id] = clazz.load_from_history(events)
60
+ end
61
+ end
62
+
63
+ # Gets all uncommitted_events from the 'registered' aggregates
64
+ # and stores them in the event store.
65
+ # The command is 'attached' for traceability purpose so we can see
66
+ # which command resulted in which events.
67
+ #
68
+ # This is all abstracted away if you use the Sequent::Core::CommandService
69
+ #
70
+ def commit(command)
71
+ all_events = []
72
+ aggregates.each_value { |aggregate| all_events += aggregate.uncommitted_events }
73
+ return if all_events.empty?
74
+ aggregates.each_value { |aggregate| aggregate.clear_events }
75
+ store_events command, all_events
76
+ end
77
+
78
+ # Clears the Unit of Work.
79
+ def clear
80
+ Thread.current[AGGREGATES_KEY] = {}
81
+ end
82
+
83
+ private
84
+
85
+ def aggregates
86
+ Thread.current[AGGREGATES_KEY]
87
+ end
88
+
89
+ def store_events(command, events)
90
+ @event_store.commit_events(command, events)
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,87 @@
1
+ require_relative 'helpers/self_applier'
2
+
3
+ module Sequent
4
+ module Core
5
+ # Base class for all your domain classes.
6
+ #
7
+ # +load_from_history+ functionality to be loaded_from_history, meaning a stream of events.
8
+ #
9
+ class AggregateRoot
10
+ include Helpers::SelfApplier
11
+
12
+ attr_reader :id, :uncommitted_events, :sequence_number
13
+
14
+ def self.load_from_history(events)
15
+ aggregate_root = allocate() # allocate without calling new
16
+ aggregate_root.load_from_history(events)
17
+ aggregate_root
18
+ end
19
+
20
+ def initialize(id)
21
+ @id = id
22
+ @uncommitted_events = []
23
+ @sequence_number = 1
24
+ end
25
+
26
+ def load_from_history(events)
27
+ raise "Empty history" if events.empty?
28
+ @id = events.first.aggregate_id
29
+ @uncommitted_events = []
30
+ @sequence_number = events.size + 1
31
+ events.each { |event| handle_message(event) }
32
+ end
33
+
34
+ def to_s
35
+ "#{self.class.name}: #{@id}"
36
+ end
37
+
38
+
39
+ def clear_events
40
+ uncommitted_events.clear
41
+ end
42
+
43
+ protected
44
+
45
+ def build_event(event, params = {})
46
+ event.new({aggregate_id: @id, sequence_number: @sequence_number}.merge(params))
47
+ end
48
+
49
+ # Provide subclasses nice DSL to 'apply' events via:
50
+ #
51
+ # def send_invoice
52
+ # apply InvoiceSentEvent, send_date: DateTime.now
53
+ # end
54
+ #
55
+ def apply(event, params={})
56
+ event = build_event(event, params) if event.is_a?(Class)
57
+ handle_message(event)
58
+ @uncommitted_events << event
59
+ @sequence_number += 1
60
+ end
61
+ end
62
+
63
+ # You can use this class when running in a multi tenant environment
64
+ # It basically makes sure that the +organization_id+ (the tenant_id for historic reasons)
65
+ # is available for the subclasses
66
+ class TenantAggregateRoot < AggregateRoot
67
+ attr_reader :organization_id
68
+
69
+ def initialize(id, organization_id)
70
+ super(id)
71
+ @organization_id = organization_id
72
+ end
73
+
74
+ def load_from_history(events)
75
+ raise "Empty history" if events.empty?
76
+ @organization_id = events.first.organization_id
77
+ super(events)
78
+ end
79
+
80
+ protected
81
+
82
+ def build_event(event, params = {})
83
+ super(event, {organization_id: @organization_id}.merge(params))
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,39 @@
1
+ require_relative 'helpers/self_applier'
2
+ require_relative 'helpers/uuid_helper'
3
+
4
+ module Sequent
5
+ module Core
6
+ # Base class for command handlers
7
+ # CommandHandlers are responsible for propagating a command to the correct Sequent::Core::AggregateRoot
8
+ # or creating a new one. For example:
9
+ #
10
+ # class InvoiceCommandHandler < Sequent::Core::BaseCommandHandler
11
+ # on CreateInvoiceCommand do |command|
12
+ # repository.add_aggregate Invoice.new(command.aggregate_id)
13
+ # end
14
+ #
15
+ # on PayInvoiceCommanddo |command|
16
+ # do_with_aggregate(command, Invoice) {|invoice|invoice.pay(command.pay_date)}
17
+ # end
18
+ # end
19
+ class BaseCommandHandler
20
+ include Sequent::Core::Helpers::SelfApplier,
21
+ Sequent::Core::Helpers::UuidHelper
22
+
23
+ def initialize(repository)
24
+ @repository = repository
25
+ end
26
+
27
+ protected
28
+ def do_with_aggregate(command, clazz, aggregate_id = nil)
29
+ aggregate = @repository.load_aggregate(aggregate_id.nil? ? command.aggregate_id : aggregate_id, clazz)
30
+ yield aggregate if block_given?
31
+ end
32
+
33
+ protected
34
+ def repository
35
+ @repository
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,51 @@
1
+ require_relative 'helpers/self_applier'
2
+
3
+ module Sequent
4
+ module Core
5
+ # EventHandlers listen to events and handle them according to their responsibility.
6
+ #
7
+ # Examples:
8
+ # * Updating view states
9
+ # * Sending emails
10
+ # * Executing other commands based on events (chainging)
11
+ #
12
+ # Example of updating view state, in this case the InvoiceRecord table representing an Invoice
13
+ #
14
+ # class InvoiceCommandHandler < Sequent::Core::BaseCommandHandler
15
+ # on CreateInvoiceCommand do |command|
16
+ # create_record(
17
+ # InvoiceRecord,
18
+ # recipient: command.recipient,
19
+ # amount: command.amount
20
+ # )
21
+ # end
22
+ # end
23
+ #
24
+ # Please note that the actual storage is abstracted away in the +record_session+. Reason
25
+ # is when replaying the entire event_store our default choice, active_record, is too slow.
26
+ # Also we want to give the opportunity to use other storage mechanisms for the view state.
27
+ # See the +def_delegators+ which method to implement.
28
+ # Due to this abstraction you can not traverse into child objects when using ActiveRecord
29
+ # like you are used to:
30
+ #
31
+ # invoice_record.line_item_records << create_record(LineItemRecord, ...)
32
+ #
33
+ # In this case you should simply do:
34
+ #
35
+ # create_record(LineItemRecord, invoice_id: invoice_record.aggregate_id)
36
+ #
37
+ class BaseEventHandler
38
+ extend Forwardable
39
+ include Helpers::SelfApplier
40
+
41
+ def initialize(record_session = Sequent::Core::RecordSessions::ActiveRecordSession.new)
42
+ @record_session = record_session
43
+ end
44
+
45
+ def_delegators :@record_session, :update_record, :create_record, :create_or_update_record, :get_record!, :get_record,
46
+ :delete_all_records, :update_all_records, :do_with_records, :do_with_record, :delete_record,
47
+ :find_records, :last_record
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,79 @@
1
+ require_relative 'helpers/copyable'
2
+ require_relative 'helpers/attribute_support'
3
+ require_relative 'helpers/uuid_helper'
4
+ require_relative 'helpers/equal_support'
5
+ require_relative 'helpers/param_support'
6
+ require_relative 'helpers/mergable'
7
+
8
+ module Sequent
9
+ module Core
10
+ # Base command
11
+ class BaseCommand
12
+ include ActiveModel::Validations,
13
+ ActiveModel::Serializers::JSON,
14
+ Sequent::Core::Helpers::Copyable,
15
+ Sequent::Core::Helpers::AttributeSupport,
16
+ Sequent::Core::Helpers::UuidHelper,
17
+ Sequent::Core::Helpers::EqualSupport,
18
+ Sequent::Core::Helpers::ParamSupport,
19
+ Sequent::Core::Helpers::Mergable
20
+
21
+ attrs created_at: DateTime
22
+
23
+ self.include_root_in_json = false
24
+
25
+ def initialize(args = {})
26
+ update_all_attributes args
27
+ @created_at = DateTime.now
28
+ end
29
+
30
+ end
31
+
32
+ module UpdateSequenceNumber
33
+ extend ActiveSupport::Concern
34
+ included do
35
+ attrs sequence_number: Integer
36
+ validates_presence_of :sequence_number
37
+ validates_numericality_of :sequence_number, only_integer: true, allow_nil: true, allow_blank: true, greater_than: 0
38
+ end
39
+ end
40
+
41
+ # Most commonly used command
42
+ # Command can be instantiated just by using:
43
+ #
44
+ # Command.new(aggregate_id: "1", user_id: "joe")
45
+ #
46
+ # But the Sequent::Core::Helpers::ParamSupport also enables Commands
47
+ # to be created from a params hash (like the one from Sinatra) as follows:
48
+ #
49
+ # command = Command.from_params(params)
50
+ #
51
+ class Command < BaseCommand
52
+ attrs aggregate_id: String, user_id: String
53
+
54
+ def initialize(args = {})
55
+ raise ArgumentError, "Missing aggregate_id" if args[:aggregate_id].nil?
56
+ super
57
+ end
58
+
59
+ end
60
+
61
+ class UpdateCommand < Command
62
+ include UpdateSequenceNumber
63
+ end
64
+
65
+ class TenantCommand < Command
66
+ attrs organization_id: String
67
+
68
+ def initialize(args = {})
69
+ raise ArgumentError, "Missing organization_id" if args[:organization_id].nil?
70
+ super
71
+ end
72
+ end
73
+
74
+ class UpdateTenantCommand < TenantCommand
75
+ include UpdateSequenceNumber
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,26 @@
1
+ require 'active_record'
2
+ module Sequent
3
+ module Core
4
+ # For storing Sequent::Core::Command in the database using active_record
5
+ class CommandRecord < ActiveRecord::Base
6
+
7
+ self.table_name = "command_records"
8
+
9
+ validates_presence_of :command_type, :command_json
10
+
11
+ def command
12
+ args = JSON.parse(command_json)
13
+ Class.const_get(command_type.to_sym).deserialize_from_json(args)
14
+ end
15
+
16
+ def command=(command)
17
+ self.created_at = command.created_at
18
+ self.aggregate_id = command.aggregate_id if command.respond_to? :aggregate_id
19
+ self.organization_id = command.organization_id if command.respond_to? :organization_id
20
+ self.user_id = command.user_id if command.respond_to? :user_id
21
+ self.command_type = command.class.name
22
+ self.command_json = command.to_json.to_s
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,118 @@
1
+ require_relative 'transactions/no_transactions'
2
+
3
+ module Sequent
4
+ module Core
5
+
6
+ class CommandServiceConfiguration
7
+ attr_accessor :event_store,
8
+ :command_handler_classes,
9
+ :transaction_provider,
10
+ :filters
11
+
12
+ def initialize
13
+ @command_handler_classes = []
14
+ @transaction_provider = Sequent::Core::Transactions::NoTransactions.new
15
+ @filters = []
16
+ end
17
+
18
+ end
19
+
20
+ #
21
+ # Single point in the application where subclasses of Sequent::Core::BaseCommand
22
+ # are executed. This will initiate the entire flow of:
23
+ #
24
+ # * Validate command
25
+ # * Call correct Sequent::Core::BaseCommandHandler
26
+ # * CommandHandler decides which Sequent::Core::AggregateRoot (s) to call
27
+ # * Events are stored in the Sequent::Core::EventStore
28
+ # * Unit of Work is cleared
29
+ #
30
+ class CommandService
31
+
32
+ class << self
33
+ attr_accessor :configuration,
34
+ :instance
35
+ end
36
+
37
+ # Creates a new CommandService and overwrites all existing config.
38
+ # The new CommandService can be retrieved via the +CommandService.instance+ method.
39
+ #
40
+ # If you don't want a singleton you can always instantiate it yourself using the +CommandService.new+.
41
+ def self.configure
42
+ self.configuration = CommandServiceConfiguration.new
43
+ yield(configuration) if block_given?
44
+ self.instance = CommandService.new(configuration)
45
+ end
46
+
47
+ # +DefaultCommandServiceConfiguration+ Configuration class for the CommandService containing:
48
+ #
49
+ # +event_store+ The Sequent::Core::EventStore
50
+ # +command_handler_classes+ Array of BaseCommandHandler classes that need to handle commands
51
+ # +transaction_provider+ How to do transaction management. Defaults to Sequent::Core::Transactions::NoTransactions
52
+ # +filters+ List of filter that respond_to :execute(command). Can be useful to do extra checks (security and such).
53
+ def initialize(configuration = CommandServiceConfiguration.new)
54
+ @event_store = configuration.event_store
55
+ @repository = AggregateRepository.new(configuration.event_store)
56
+ @filters = configuration.filters
57
+ @transaction_provider = configuration.transaction_provider
58
+ @command_handlers = configuration.command_handler_classes.map { |handler| handler.new(@repository) }
59
+ end
60
+
61
+ # Executes the given commands in a single transactional block as implemented by the +transaction_provider+
62
+ #
63
+ # For each command:
64
+ #
65
+ # * All filters are executed. Any exception raised will rollback the transaction and propagate up
66
+ # * If the command is valid all +command_handlers+ that +handles_message?+ is invoked
67
+ # * The +repository+ commits the command and all uncommitted_events resulting from the command
68
+ def execute_commands(*commands)
69
+ begin
70
+ @transaction_provider.transactional do
71
+ commands.each do |command|
72
+ @filters.each { |filter| filter.execute(command) }
73
+
74
+ if command.valid?
75
+ @command_handlers.each do |command_handler|
76
+ command_handler.handle_message command if command_handler.handles_message? command
77
+ end
78
+ end
79
+
80
+ @repository.commit(command)
81
+ raise CommandNotValid.new(command) unless command.validation_errors.empty?
82
+ end
83
+ end
84
+ ensure
85
+ @repository.clear
86
+ end
87
+
88
+ end
89
+
90
+ def remove_event_handler(clazz)
91
+ @event_store.remove_event_handler(clazz)
92
+ end
93
+
94
+ end
95
+
96
+ # Raised when BaseCommand.valid? returns false
97
+ class CommandNotValid < ArgumentError
98
+
99
+ def initialize(command)
100
+ @command = command
101
+ msg = @command.respond_to?(:aggregate_id) ? " #{@command.aggregate_id}" : ""
102
+ super "Invalid command #{@command.class.to_s}#{msg}, errors: #{@command.validation_errors}"
103
+ end
104
+
105
+ def errors(prefix = nil)
106
+ @command.validation_errors(prefix)
107
+ end
108
+
109
+ def errors_with_command_prefix
110
+ errors(@command.class.to_s.underscore)
111
+ end
112
+ end
113
+
114
+ end
115
+ end
116
+
117
+
118
+
@@ -0,0 +1,15 @@
1
+ require_relative 'helpers/helpers'
2
+ require_relative 'record_sessions/record_sessions'
3
+ require_relative 'transactions/transactions'
4
+ require_relative 'aggregate_repository'
5
+ require_relative 'aggregate_root'
6
+ require_relative 'base_command_handler'
7
+ require_relative 'command'
8
+ require_relative 'command_service'
9
+ require_relative 'event'
10
+ require_relative 'value_object'
11
+ require_relative 'base_event_handler'
12
+ require_relative 'event_store'
13
+ require_relative 'tenant_event_store'
14
+ require_relative 'event_record'
15
+ require_relative 'command_record'
@@ -0,0 +1,62 @@
1
+ require 'active_model'
2
+ require_relative 'helpers/string_support'
3
+ require_relative 'helpers/equal_support'
4
+ require_relative 'helpers/attribute_support'
5
+ require_relative 'helpers/copyable'
6
+
7
+ module Sequent
8
+ module Core
9
+ class Event
10
+ include Sequent::Core::Helpers::StringSupport,
11
+ Sequent::Core::Helpers::EqualSupport,
12
+ Sequent::Core::Helpers::AttributeSupport,
13
+ Sequent::Core::Helpers::Copyable,
14
+ ActiveModel::Serializers::JSON
15
+ attrs aggregate_id: String, sequence_number: Integer, created_at: DateTime
16
+ self.include_root_in_json = false
17
+
18
+ def initialize(args = {})
19
+ update_all_attributes args
20
+ raise "Missing aggregate_id" unless @aggregate_id
21
+ raise "Missing sequence_number" unless @sequence_number
22
+ @created_at ||= DateTime.now
23
+ end
24
+
25
+ def payload
26
+ result = {}
27
+ instance_variables.reject { |k| payload_variables.include?(k.to_s) }.each do |k|
28
+ result[k.to_s[1 .. -1].to_sym] = instance_variable_get(k)
29
+ end
30
+ result
31
+ end
32
+ protected
33
+ def payload_variables
34
+ %w{@aggregate_id @sequence_number @created_at @underscored}
35
+ end
36
+
37
+ end
38
+
39
+ class TenantEvent < Event
40
+
41
+ attrs organization_id: String
42
+
43
+ def initialize(args = {})
44
+ super
45
+ raise "Missing organization_id" unless @organization_id
46
+ end
47
+
48
+ protected
49
+ def payload_variables
50
+ super << "@organization_id"
51
+ end
52
+
53
+ end
54
+
55
+ class CreateEvent < TenantEvent
56
+
57
+ end
58
+
59
+ end
60
+ end
61
+
62
+
@@ -0,0 +1,34 @@
1
+ require 'oj'
2
+ require 'active_record'
3
+
4
+ module Sequent
5
+ module Core
6
+ class EventRecord < ActiveRecord::Base
7
+
8
+ self.table_name = "event_records"
9
+
10
+ belongs_to :command_record
11
+
12
+ validates_presence_of :aggregate_id, :sequence_number, :event_type, :event_json
13
+ validates_numericality_of :sequence_number
14
+
15
+
16
+ def event
17
+ payload = Oj.strict_load(event_json)
18
+ Class.const_get(event_type.to_sym).deserialize_from_json(payload)
19
+ end
20
+
21
+ def event=(event)
22
+ self.aggregate_id = event.aggregate_id
23
+ self.sequence_number = event.sequence_number
24
+ self.organization_id = event.organization_id if event.respond_to?(:organization_id)
25
+ self.event_type = event.class.name
26
+ self.created_at = event.created_at
27
+ self.event_json = event.to_json.to_s
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+ end
34
+