sandthorn_driver_sequel 1.1.0 → 2.0.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.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/Guardfile +7 -0
- data/lib/sandthorn_driver_sequel/access/aggregate_access.rb +50 -0
- data/lib/sandthorn_driver_sequel/access/event_access.rb +81 -0
- data/lib/sandthorn_driver_sequel/access/snapshot_access.rb +87 -0
- data/lib/sandthorn_driver_sequel/access.rb +20 -0
- data/lib/sandthorn_driver_sequel/errors.rb +47 -5
- data/lib/sandthorn_driver_sequel/event_query.rb +90 -0
- data/lib/sandthorn_driver_sequel/event_store.rb +90 -153
- data/lib/sandthorn_driver_sequel/event_store_context.rb +1 -0
- data/lib/sandthorn_driver_sequel/migration.rb +9 -1
- data/lib/sandthorn_driver_sequel/old_event_store.rb +228 -0
- data/lib/sandthorn_driver_sequel/sequel_driver.rb +8 -25
- data/lib/sandthorn_driver_sequel/storage.rb +46 -0
- data/lib/sandthorn_driver_sequel/utilities/array.rb +13 -0
- data/lib/sandthorn_driver_sequel/utilities.rb +1 -0
- data/lib/sandthorn_driver_sequel/version.rb +1 -1
- data/lib/sandthorn_driver_sequel/wrappers/event_wrapper.rb +12 -0
- data/lib/sandthorn_driver_sequel/wrappers/snapshot_wrapper.rb +11 -0
- data/lib/sandthorn_driver_sequel/wrappers.rb +2 -0
- data/lib/sandthorn_driver_sequel.rb +5 -0
- data/sandthorn_driver_sequel.gemspec +2 -2
- data/spec/aggregate_access_spec.rb +97 -0
- data/spec/asking_for_aggregates_to_snapshot_spec.rb +7 -4
- data/spec/driver_interface_spec.rb +23 -40
- data/spec/event_access_spec.rb +96 -0
- data/spec/event_store_with_context_spec.rb +4 -4
- data/spec/get_events_spec.rb +20 -13
- data/spec/migration_specifying_domain_spec.rb +10 -10
- data/spec/saving_events_spec.rb +42 -39
- data/spec/saving_snapshot_spec.rb +7 -7
- data/spec/snapshot_access_spec.rb +119 -0
- data/spec/spec_helper.rb +0 -4
- data/spec/storage_spec.rb +66 -0
- metadata +39 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6df7059570ed2a75376a49b5568cde3dcda5abef
|
4
|
+
data.tar.gz: d0df6fa0c71d2388f31897fcb4c1918ecbe6bdd2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cff7f24db48fe2ec230c54526f3231fff0c2f7cd94ed12b585807cfa06084cba5a7c4d7d63b670816844ba8b5f6a3114a6d755bb0b16b6cb2e3336ea9c68236c
|
7
|
+
data.tar.gz: 2592b3457a5c030517d968302aa4c716af3cd58fa3080c1378960a066a40fbf28000e963ab22d75c0fbf600acd3b4f75799ceea63d3a01f1c7d90d59927b5ea4
|
data/.gitignore
CHANGED
data/Guardfile
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module SandthornDriverSequel
|
2
|
+
class AggregateAccess < Access::Base
|
3
|
+
|
4
|
+
def find_or_register(aggregate_id, aggregate_type)
|
5
|
+
if aggregate = find_by_aggregate_id(aggregate_id)
|
6
|
+
aggregate
|
7
|
+
else
|
8
|
+
register_aggregate(aggregate_id, aggregate_type)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Create a database row for an aggregate.
|
13
|
+
# Return the row.
|
14
|
+
def register_aggregate(aggregate_id, aggregate_type)
|
15
|
+
id = storage.aggregates.insert(aggregate_id: aggregate_id, aggregate_type: aggregate_type.to_s)
|
16
|
+
find(id)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Find by database table id.
|
20
|
+
def find(id)
|
21
|
+
storage.aggregates[id]
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_by_aggregate_id(aggregate_id)
|
25
|
+
storage.aggregates.first(aggregate_id: aggregate_id)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Throws an error if the aggregate isn't registered.
|
29
|
+
def find_by_aggregate_id!(aggregate_id)
|
30
|
+
aggregate = find_by_aggregate_id(aggregate_id)
|
31
|
+
raise Errors::NoAggregateError, aggregate_id unless aggregate
|
32
|
+
aggregate
|
33
|
+
end
|
34
|
+
|
35
|
+
def aggregate_types
|
36
|
+
storage.aggregates.select(:aggregate_type).distinct.select_map(:aggregate_type)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns aggregate ids.
|
40
|
+
# @param aggregate_type, optional,
|
41
|
+
def aggregate_ids(aggregate_type: nil)
|
42
|
+
aggs = storage.aggregates
|
43
|
+
if aggregate_type
|
44
|
+
aggs = aggs.where(aggregate_type: aggregate_type.to_s)
|
45
|
+
end
|
46
|
+
aggs.order(:id).select_map(:aggregate_id)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module SandthornDriverSequel
|
2
|
+
class EventAccess < Access::Base
|
3
|
+
# = EventAccess
|
4
|
+
# Reads and writes events.
|
5
|
+
|
6
|
+
def store_events(aggregate, events)
|
7
|
+
events = Utilities.array_wrap(events)
|
8
|
+
timestamp = Time.now.utc
|
9
|
+
events.each do |event|
|
10
|
+
store_event(aggregate, timestamp, event)
|
11
|
+
end
|
12
|
+
aggregate.save
|
13
|
+
end
|
14
|
+
|
15
|
+
def find_events_by_aggregate_id(aggregate_id)
|
16
|
+
aggregate_version = Sequel.qualify(storage.events_table_name, :aggregate_version)
|
17
|
+
wrap(storage.events
|
18
|
+
.join(storage.aggregates, id: :aggregate_table_id)
|
19
|
+
.where(aggregate_id: aggregate_id)
|
20
|
+
.select(
|
21
|
+
:sequence_number,
|
22
|
+
:aggregate_id,
|
23
|
+
:aggregate_table_id,
|
24
|
+
aggregate_version,
|
25
|
+
:event_name,
|
26
|
+
:event_data,
|
27
|
+
:timestamp)
|
28
|
+
.all)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns events that occurred after the given snapshot
|
32
|
+
def after_snapshot(snapshot)
|
33
|
+
_aggregate_version = snapshot.aggregate_version
|
34
|
+
aggregate_table_id = snapshot.aggregate_table_id
|
35
|
+
wrap(storage.events
|
36
|
+
.where(aggregate_table_id: aggregate_table_id)
|
37
|
+
.where { aggregate_version > _aggregate_version }.all)
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_events(*args)
|
41
|
+
query_builder = EventQuery.new(storage)
|
42
|
+
query_builder.build(*args)
|
43
|
+
wrap(query_builder.events)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def wrap(arg)
|
49
|
+
events = Utilities.array_wrap(arg)
|
50
|
+
events.map { |e| EventWrapper.new(e.values) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def build_event_data(aggregate, timestamp, event)
|
54
|
+
{
|
55
|
+
aggregate_table_id: aggregate.id,
|
56
|
+
aggregate_version: event.aggregate_version,
|
57
|
+
event_name: event.event_name,
|
58
|
+
event_data: event.event_data,
|
59
|
+
timestamp: timestamp
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
def check_versions!(aggregate, event)
|
64
|
+
version = aggregate.aggregate_version
|
65
|
+
if version != event[:aggregate_version]
|
66
|
+
raise Errors::ConcurrencyError.new(event, aggregate)
|
67
|
+
end
|
68
|
+
rescue TypeError
|
69
|
+
raise Errors::EventFormatError, "Event has wrong format: #{event.inspect}"
|
70
|
+
end
|
71
|
+
|
72
|
+
def store_event(aggregate, timestamp, event)
|
73
|
+
event = EventWrapper.new(event)
|
74
|
+
aggregate.aggregate_version += 1
|
75
|
+
check_versions!(aggregate, event)
|
76
|
+
data = build_event_data(aggregate, timestamp, event)
|
77
|
+
storage.events.insert(data)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module SandthornDriverSequel
|
2
|
+
class SnapshotAccess < Access::Base
|
3
|
+
|
4
|
+
def find_by_aggregate_id(aggregate_id)
|
5
|
+
aggregate = aggregates.find_by_aggregate_id(aggregate_id)
|
6
|
+
storage.snapshots.first(aggregate_table_id: aggregate.id)
|
7
|
+
end
|
8
|
+
|
9
|
+
def find(snapshot_id)
|
10
|
+
storage.snapshots[snapshot_id]
|
11
|
+
end
|
12
|
+
|
13
|
+
def record_snapshot(aggregate_id, snapshot_data)
|
14
|
+
aggregate = aggregates.find_by_aggregate_id!(aggregate_id)
|
15
|
+
previous_snapshot = find_by_aggregate_id(aggregate_id)
|
16
|
+
if perform_snapshot?(aggregate, previous_snapshot)
|
17
|
+
perform_snapshot(aggregate, previous_snapshot, snapshot_data)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def obsolete(aggregate_types: [], max_event_distance: 100)
|
22
|
+
aggregate_types.map!(&:to_s)
|
23
|
+
snapshot_version = Sequel.qualify(storage.snapshots_table_name, :aggregate_version)
|
24
|
+
aggregate_version = Sequel.qualify(storage.aggregates_table_name, :aggregate_version)
|
25
|
+
query = storage.aggregates.left_outer_join(storage.snapshots, aggregate_table_id: :id)
|
26
|
+
query = query.select { (aggregate_version - snapshot_version).as(distance) }
|
27
|
+
query = query.select_append(:aggregate_id, :aggregate_type)
|
28
|
+
query = query.where { (aggregate_version - coalesce(snapshot_version, 0)) > max_event_distance }
|
29
|
+
if aggregate_types.any?
|
30
|
+
query = query.where(aggregate_type: aggregate_types)
|
31
|
+
end
|
32
|
+
query.all
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def aggregates
|
38
|
+
@aggregates ||= AggregateAccess.new(storage)
|
39
|
+
end
|
40
|
+
|
41
|
+
def perform_snapshot?(aggregate, snapshot)
|
42
|
+
return true if snapshot.nil?
|
43
|
+
snapshot = SnapshotWrapper.new(snapshot)
|
44
|
+
aggregate.aggregate_version > snapshot.aggregate_version
|
45
|
+
end
|
46
|
+
|
47
|
+
def perform_snapshot(aggregate, snapshot, snapshot_data)
|
48
|
+
check_snapshot_version!(aggregate, snapshot_data)
|
49
|
+
if valid_snapshot?(snapshot)
|
50
|
+
update_snapshot(snapshot, snapshot_data)
|
51
|
+
else
|
52
|
+
insert_snapshot(aggregate, snapshot_data)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def insert_snapshot(aggregate, snapshot_data)
|
57
|
+
data = build_snapshot(snapshot_data)
|
58
|
+
data[:aggregate_table_id] = aggregate.id
|
59
|
+
storage.snapshots.insert(data)
|
60
|
+
end
|
61
|
+
|
62
|
+
def build_snapshot(snapshot_data)
|
63
|
+
snapshot_data = SnapshotWrapper.new(snapshot_data)
|
64
|
+
{
|
65
|
+
snapshot_data: snapshot_data.data,
|
66
|
+
aggregate_version: snapshot_data.aggregate_version
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def valid_snapshot?(snapshot)
|
71
|
+
snapshot && snapshot[:snapshot_data]
|
72
|
+
end
|
73
|
+
|
74
|
+
def update_snapshot(snapshot, snapshot_data)
|
75
|
+
data = build_snapshot(snapshot_data)
|
76
|
+
storage.snapshots.where(id: snapshot.id).update(data)
|
77
|
+
end
|
78
|
+
|
79
|
+
def check_snapshot_version!(aggregate, snapshot_data)
|
80
|
+
snapshot = SnapshotWrapper.new(snapshot_data)
|
81
|
+
if aggregate.aggregate_version < snapshot.aggregate_version
|
82
|
+
raise Errors::WrongSnapshotVersionError.new(aggregate, snapshot.aggregate_version)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module SandthornDriverSequel
|
2
|
+
module Access
|
3
|
+
class Base
|
4
|
+
# = Access::Base
|
5
|
+
# Inheriting classes use +storage+ to provide access to a
|
6
|
+
# particular database model/table.
|
7
|
+
def initialize(storage)
|
8
|
+
@storage = storage
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
attr_reader :storage
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
require "sandthorn_driver_sequel/access/aggregate_access"
|
19
|
+
require "sandthorn_driver_sequel/access/event_access"
|
20
|
+
require "sandthorn_driver_sequel/access/snapshot_access"
|
@@ -1,7 +1,49 @@
|
|
1
1
|
module SandthornDriverSequel::Errors
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
Error = Class.new(StandardError)
|
3
|
+
InternalError = Class.new(Error)
|
4
|
+
NoAggregateError = Class.new(Error)
|
5
|
+
EventFormatError = Class.new(Error)
|
6
|
+
|
7
|
+
class ConcurrencyError < Error
|
8
|
+
attr_reader :event, :aggregate
|
9
|
+
def initialize(event, aggregate)
|
10
|
+
@event = event
|
11
|
+
@aggregate = aggregate
|
12
|
+
super(create_message)
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_message
|
16
|
+
"#{aggregate.aggregate_type} with id #{aggregate.aggregate_id}: " +
|
17
|
+
"expected event with version #{aggregate.aggregate_version}, but got #{event.aggregate_version}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class WrongAggregateVersionError < Error;
|
22
|
+
def initialize(aggregate, version)
|
23
|
+
@aggregate = aggregate
|
24
|
+
@version = version
|
25
|
+
super(create_message)
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_message
|
29
|
+
"#{@aggregate[:aggregate_type]} with id #{@aggregate[:aggregate_id]}" +
|
30
|
+
" should be at version #{@version}" +
|
31
|
+
" but was #{@aggregate[:aggregate_version]} in the event store."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class WrongSnapshotVersionError < Error
|
36
|
+
attr_reader :aggregate, :version
|
37
|
+
def initialize(aggregate, version)
|
38
|
+
@aggregate = aggregate
|
39
|
+
@version = version
|
40
|
+
super(create_message)
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_message
|
44
|
+
"#{aggregate[:aggregate_type]} with id #{aggregate[:aggregate_id]}: tried to save snapshot with version "+
|
45
|
+
"#{version}, but current version is at #{aggregate[:aggregate_version]}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
7
49
|
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module SandthornDriverSequel
|
2
|
+
class EventQuery
|
3
|
+
def initialize(storage)
|
4
|
+
@storage = storage
|
5
|
+
end
|
6
|
+
|
7
|
+
def build(
|
8
|
+
aggregate_types: [],
|
9
|
+
take: 0,
|
10
|
+
after_sequence_number: 0,
|
11
|
+
include_events: [],
|
12
|
+
exclude_events: [])
|
13
|
+
|
14
|
+
aggregate_types.map!(&:to_s)
|
15
|
+
include_events.map!(&:to_s)
|
16
|
+
exclude_events.map!(&:to_s)
|
17
|
+
|
18
|
+
query = storage.events
|
19
|
+
query = add_aggregate_types(query, aggregate_types)
|
20
|
+
query = add_sequence_number(query, after_sequence_number)
|
21
|
+
query = add_included_events(query, include_events)
|
22
|
+
query = add_excluded_events(query, exclude_events)
|
23
|
+
query = add_select(query)
|
24
|
+
query = add_limit(query, take)
|
25
|
+
@query = query.order(:sequence_number)
|
26
|
+
end
|
27
|
+
|
28
|
+
def events
|
29
|
+
@query.all
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :storage
|
35
|
+
|
36
|
+
def add_limit(query, take)
|
37
|
+
if take > 0
|
38
|
+
query.limit(take)
|
39
|
+
else
|
40
|
+
query
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_select(query)
|
45
|
+
query.select(*select_columns)
|
46
|
+
end
|
47
|
+
|
48
|
+
def select_columns
|
49
|
+
rel = Sequel.qualify(storage.events_table_name, :aggregate_version)
|
50
|
+
[
|
51
|
+
:aggregate_type,
|
52
|
+
rel,
|
53
|
+
:aggregate_id,
|
54
|
+
:sequence_number,
|
55
|
+
:event_name,
|
56
|
+
:event_data,
|
57
|
+
:timestamp
|
58
|
+
]
|
59
|
+
end
|
60
|
+
|
61
|
+
def add_excluded_events(query, exclude_events)
|
62
|
+
if exclude_events.any?
|
63
|
+
query.exclude(event_name: exclude_events)
|
64
|
+
else
|
65
|
+
query
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def add_included_events(query, include_events)
|
70
|
+
if include_events.any?
|
71
|
+
query.where(event_name: include_events)
|
72
|
+
else
|
73
|
+
query
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def add_sequence_number(query, after_sequence_number)
|
78
|
+
query.where { sequence_number > after_sequence_number }
|
79
|
+
end
|
80
|
+
|
81
|
+
def add_aggregate_types(query, aggregate_types)
|
82
|
+
if aggregate_types.any?
|
83
|
+
query.join(storage.aggregates, id: :aggregate_table_id, aggregate_type: aggregate_types)
|
84
|
+
else
|
85
|
+
query.join(storage.aggregates, id: :aggregate_table_id)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
@@ -1,201 +1,138 @@
|
|
1
|
-
require 'sandthorn_driver_sequel/sequel_driver'
|
2
|
-
|
3
1
|
module SandthornDriverSequel
|
4
2
|
class EventStore
|
5
3
|
include EventStoreContext
|
4
|
+
|
6
5
|
attr_reader :driver, :context, :url
|
6
|
+
|
7
7
|
def initialize url: nil, context: nil
|
8
8
|
@driver = SequelDriver.new url: url
|
9
9
|
@context = context
|
10
10
|
@url = url
|
11
11
|
end
|
12
12
|
|
13
|
-
def save_events
|
14
|
-
current_aggregate_version = originating_aggregate_version
|
15
|
-
aggregate_type = class_name.first.to_s
|
13
|
+
def save_events events, aggregate_id, class_name
|
16
14
|
driver.execute_in_transaction do |db|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
timestamp = Time.now.utc
|
29
|
-
aggregate_events.each do |event|
|
30
|
-
current_aggregate_version += 1
|
31
|
-
if current_aggregate_version != event[:aggregate_version]
|
32
|
-
error_message = "#{aggregate_type} with id #{aggregate_id}: expected event with version #{current_aggregate_version}, but got #{event[:aggregate_version]}"
|
33
|
-
raise SandthornDriverSequel::Errors::ConcurrencyError.new(error_message)
|
34
|
-
end
|
35
|
-
to_insert = ({aggregate_table_id: pk_id, aggregate_version: event[:aggregate_version], event_name: event[:event_name], event_data: event[:event_data], timestamp: timestamp})
|
36
|
-
db[events_table_name].insert(to_insert)
|
37
|
-
end
|
38
|
-
db[aggregates_table_name].where(id: pk_id).update(aggregate_version: current_aggregate_version)
|
15
|
+
aggregates = get_aggregate_access(db)
|
16
|
+
event_access = get_event_access(db)
|
17
|
+
aggregate = aggregates.find_or_register(aggregate_id, class_name)
|
18
|
+
event_access.store_events(aggregate, events)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_aggregate_events(aggregate_id)
|
23
|
+
driver.execute do |db|
|
24
|
+
events = get_event_access(db)
|
25
|
+
events.find_events_by_aggregate_id(aggregate_id)
|
39
26
|
end
|
40
27
|
end
|
41
28
|
|
42
|
-
def save_snapshot aggregate_snapshot, aggregate_id
|
43
|
-
#ar_snapshot.event_name = snapshot[:event_name]
|
44
|
-
#ar_snapshot.event_data = snapshot[:event_data]
|
45
|
-
#ar_snapshot.aggregate_version = snapshot[:aggregate_version]
|
46
|
-
#ar_snapshot.aggregate_id = aggregate_id
|
29
|
+
def save_snapshot aggregate_snapshot, aggregate_id
|
47
30
|
driver.execute_in_transaction do |db|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
31
|
+
snapshot_access = get_snapshot_access(db)
|
32
|
+
snapshot_access.record_snapshot(aggregate_id, aggregate_snapshot)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# If the aggregate has a snapshot, return events starting from the snapshots.
|
37
|
+
# Otherwise, return all events.
|
38
|
+
# TODO: needs a better name
|
39
|
+
def get_aggregate_events_from_snapshot(aggregate_id)
|
40
|
+
driver.execute do |db|
|
41
|
+
snapshots = get_snapshot_access(db)
|
42
|
+
event_access = get_event_access(db)
|
43
|
+
snapshot = snapshots.find_by_aggregate_id(aggregate_id)
|
44
|
+
if snapshot
|
45
|
+
events = event_access.after_snapshot(snapshot)
|
46
|
+
snapshot_event = build_snapshot_event(snapshot)
|
47
|
+
events.unshift(snapshot_event)
|
60
48
|
else
|
61
|
-
|
62
|
-
db[snapshots_table_name].where(aggregate_table_id: pk_id).update(to_update)
|
49
|
+
event_access.find_events_by_aggregate_id(aggregate_id)
|
63
50
|
end
|
64
51
|
end
|
65
52
|
end
|
66
53
|
|
67
|
-
def
|
68
|
-
|
69
|
-
|
54
|
+
def build_snapshot_event(snapshot)
|
55
|
+
{
|
56
|
+
aggregate_version: snapshot[:aggregate_version],
|
57
|
+
event_data: snapshot[:snapshot_data],
|
58
|
+
event_name: "aggregate_set_from_snapshot"
|
59
|
+
}
|
70
60
|
end
|
71
61
|
|
72
62
|
def get_aggregate aggregate_id, *class_name
|
73
|
-
|
74
|
-
|
75
|
-
after_aggregate_version = snapshot[:aggregate_version] unless snapshot.nil?
|
76
|
-
events = aggregate_events after_aggregate_version: after_aggregate_version, aggregate_id: aggregate_id
|
77
|
-
unless snapshot.nil?
|
78
|
-
snap_event = snapshot
|
79
|
-
snap_event[:event_name] = "aggregate_set_from_snapshot"
|
80
|
-
events = events.unshift(snap_event)
|
81
|
-
end
|
82
|
-
events
|
63
|
+
warn(":get_aggregate is deprecated. Use :get_aggregate_events_from_snapshot")
|
64
|
+
get_aggregate_events_from_snapshot(aggregate_id)
|
83
65
|
end
|
84
|
-
|
85
|
-
|
66
|
+
|
67
|
+
def get_aggregate_ids(aggregate_type: nil)
|
86
68
|
driver.execute do |db|
|
87
|
-
|
69
|
+
access = get_aggregate_access(db)
|
70
|
+
access.aggregate_ids(aggregate_type: aggregate_type)
|
88
71
|
end
|
89
72
|
end
|
90
73
|
|
91
|
-
def
|
92
|
-
|
93
|
-
|
94
|
-
end
|
74
|
+
def get_aggregate_list_by_typename(type)
|
75
|
+
warn(":get_aggregate_list_by_typenames is deprecated. Use :get_aggregate_ids")
|
76
|
+
get_aggregate_ids(type: type)
|
95
77
|
end
|
96
78
|
|
97
|
-
def
|
98
|
-
aggregate_type = class_name.first.to_s
|
79
|
+
def get_all_types
|
99
80
|
driver.execute do |db|
|
100
|
-
|
101
|
-
|
102
|
-
return nil if snap.nil?
|
103
|
-
return {aggregate_version: snap[:aggregate_version], event_data: snap[:snapshot_data]}
|
81
|
+
access = get_aggregate_access(db)
|
82
|
+
access.aggregate_types
|
104
83
|
end
|
105
84
|
end
|
106
85
|
|
107
|
-
def
|
108
|
-
take = args.fetch(:take, 0)
|
109
|
-
aggregate_type = class_name.to_s
|
86
|
+
def get_snapshot aggregate_id
|
110
87
|
driver.execute do |db|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
query = query.select(:aggregate_type, rel, :aggregate_id, :sequence_number, :event_name, :event_data, :timestamp)
|
115
|
-
query = query.limit(take) if take > 0
|
116
|
-
return query.order(:sequence_number).all
|
88
|
+
snapshots = get_snapshot_access(db)
|
89
|
+
snapshot = snapshots.find_by_aggregate_id(aggregate_id)
|
90
|
+
transform_snapshot(snapshot)
|
117
91
|
end
|
118
92
|
end
|
119
|
-
|
120
|
-
|
121
|
-
exclude_events = exclude_events.map { |e| e.to_s }
|
122
|
-
aggregate_types = aggregate_types.map { |e| e.to_s }
|
93
|
+
|
94
|
+
def get_events(*args)
|
123
95
|
driver.execute do |db|
|
124
|
-
|
125
|
-
|
126
|
-
else
|
127
|
-
query = db[events_table_name].join(aggregates_table_name, id: :aggregate_table_id, aggregate_type: aggregate_types)
|
128
|
-
end
|
129
|
-
query = query.where{sequence_number > after_sequence_number}
|
130
|
-
unless include_events.empty?
|
131
|
-
query = query.where(event_name: include_events)
|
132
|
-
end
|
133
|
-
unless exclude_events.empty?
|
134
|
-
query = query.exclude(event_name: exclude_events)
|
135
|
-
end
|
136
|
-
rel = "#{events_table_name}__aggregate_version".to_sym
|
137
|
-
query = query.select(:aggregate_type, rel, :aggregate_id, :sequence_number, :event_name, :event_data, :timestamp)
|
138
|
-
query = query.limit(take) if take > 0
|
139
|
-
return query.order(:sequence_number).all
|
96
|
+
event_access = get_event_access(db)
|
97
|
+
event_access.get_events(*args)
|
140
98
|
end
|
141
99
|
end
|
142
|
-
|
100
|
+
|
101
|
+
def get_new_events_after_event_id_matching_classname event_id, class_name, take: 0
|
102
|
+
get_events(after_sequence_number: event_id, aggregate_types: Utilities.array_wrap(class_name), take: take)
|
103
|
+
end
|
104
|
+
|
105
|
+
def obsolete_snapshots(*args)
|
143
106
|
driver.execute do |db|
|
144
|
-
|
145
|
-
|
146
|
-
query_select = eval("lambda{(#{aggr_rel} - coalesce(#{rel},0)).as(distance)}")
|
147
|
-
query = db[aggregates_table_name].left_outer_join(snapshots_table_name, aggregate_table_id: :id)
|
148
|
-
query = query.select &query_select
|
149
|
-
query = query.select_append(:aggregate_id, :aggregate_type)
|
150
|
-
query_where = eval("lambda{(#{aggr_rel} - coalesce(#{rel},0)) > max_event_distance}")
|
151
|
-
query = query.where &query_where
|
152
|
-
unless class_names.empty?
|
153
|
-
class_names.map! {|c|c.to_s}
|
154
|
-
query = query.where(aggregate_type: class_names)
|
155
|
-
end
|
156
|
-
query.all
|
107
|
+
snapshots = get_snapshot_access(db)
|
108
|
+
snapshots.obsolete(*args)
|
157
109
|
end
|
158
110
|
end
|
111
|
+
|
159
112
|
private
|
160
113
|
|
161
|
-
def
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
# rel = "#{events_table_name}__aggregate_version".to_sym
|
175
|
-
# where_proc = eval("lambda{ #{rel} > after_aggregate_version }")
|
176
|
-
# driver.execute do |db|
|
177
|
-
# query = db[events_table_name].join(aggregates_table_name, id: :aggregate_table_id, aggregate_id: aggregate_id)
|
178
|
-
# query = query.where &where_proc
|
179
|
-
# result = query.select(rel, :aggregate_id, :sequence_number, :event_name, :event_data, :timestamp).order(:sequence_number).all
|
180
|
-
# end
|
181
|
-
# }
|
182
|
-
|
183
|
-
# end
|
184
|
-
# result
|
185
|
-
end
|
186
|
-
def get_current_aggregate_from_aggregates_table aggregate_id, aggregate_type, db
|
187
|
-
aggregate_type = aggregate_type.to_s
|
188
|
-
current_aggregate = db[aggregates_table_name].where(aggregate_id: aggregate_id)
|
189
|
-
if current_aggregate.empty?
|
190
|
-
error_message = "#{aggregate_type} with id #{aggregate_id} was not found in the eventstore."
|
191
|
-
raise SandthornDriverSequel::Errors::NoAggregateError.new(error_message)
|
192
|
-
end
|
193
|
-
current_aggregate.first
|
114
|
+
def transform_snapshot(snapshot)
|
115
|
+
{
|
116
|
+
aggregate_version: snapshot.aggregate_version,
|
117
|
+
event_data: snapshot.snapshot_data
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
def get_aggregate_access(db)
|
122
|
+
AggregateAccess.new(storage(db))
|
123
|
+
end
|
124
|
+
|
125
|
+
def get_event_access(db)
|
126
|
+
EventAccess.new(storage(db))
|
194
127
|
end
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
snap.first
|
128
|
+
|
129
|
+
def get_snapshot_access(db)
|
130
|
+
SnapshotAccess.new(storage(db))
|
199
131
|
end
|
132
|
+
|
133
|
+
def storage(db)
|
134
|
+
Storage.new(db, context)
|
135
|
+
end
|
136
|
+
|
200
137
|
end
|
201
|
-
end
|
138
|
+
end
|