dry-facts 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -1
- data/Gemfile.lock +4 -1
- data/README.md +7 -0
- data/ROADMAP.md +33 -20
- data/lib/dry/facts/aggregate.rb +29 -25
- data/lib/dry/facts/command.rb +8 -6
- data/lib/dry/facts/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 35e96fd751117a68e6492f8472722648f80a1cd49fa0830cdba2fb4b579b1fce
|
4
|
+
data.tar.gz: 2e2f7b7374ea7e141df10b332f04e836f2475423995ff0518d6b196bb3b18cb3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d1ee1eef74e2fdb5379777c8cb91b9c3bc6612543736e9687beb7c812b9210eb7c2722ec47a75c965ff2744efc592b643ff2ea46534ef1bbfdfdb9af7128145
|
7
|
+
data.tar.gz: 7eefafc1aa1bf0ce211b752cd6a26028506aaba9739ce51985cd40828ebf3e755dc1ab4f32194c0d33899127f621249944e76b5831e089eb09f78b85de826172
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
dry-facts (0.
|
4
|
+
dry-facts (0.3.0)
|
5
5
|
dry-equalizer (>= 0.0.11)
|
6
6
|
dry-events (>= 0.1.0)
|
7
7
|
dry-struct
|
@@ -66,5 +66,8 @@ DEPENDENCIES
|
|
66
66
|
minitest (>= 5.0)
|
67
67
|
rake (>= 10.0)
|
68
68
|
|
69
|
+
RUBY VERSION
|
70
|
+
ruby 2.5.0p0
|
71
|
+
|
69
72
|
BUNDLED WITH
|
70
73
|
1.16.1
|
data/README.md
CHANGED
@@ -3,6 +3,13 @@
|
|
3
3
|
EventSourcing ruby toolkit, somewhat based on dry-rb primitives.
|
4
4
|
Opinionated. Raw.
|
5
5
|
|
6
|
+
## Rationale
|
7
|
+
|
8
|
+
People dont *know* or *understand* software projects, people navigate themselves through software projects.
|
9
|
+
To effectively navigate through structure and mutable state of big systems - programmers need sub-systems to be isolated.
|
10
|
+
Usable incapsulation - barriers have to be visible and hard to break.
|
11
|
+
Readability matters - declarative readable code wins.
|
12
|
+
|
6
13
|
## Goals
|
7
14
|
|
8
15
|
* General EventSourcing toolkit for Ruby
|
data/ROADMAP.md
CHANGED
@@ -1,33 +1,23 @@
|
|
1
1
|
# Roadmap
|
2
2
|
|
3
|
-
|
4
|
-
rapid application development using EventSourcing as a core architectural
|
5
|
-
pattern.
|
3
|
+
Goal is to build toolkit that would allow rapid application development using EventSourcing as a core architectural pattern.
|
6
4
|
|
7
|
-
Being able to build core behaviors and exposing those through GraphQL to the
|
8
|
-
customers is the basic idea.
|
5
|
+
Being able to build core behaviors and exposing those through GraphQL to the customers is the basic idea.
|
9
6
|
|
10
7
|
Goal is multidimensional:
|
11
|
-
GraphQL
|
12
|
-
|
13
|
-
|
14
|
-
validations,
|
15
|
-
nested transactions,
|
16
|
-
async execution,
|
17
|
-
command composition
|
18
|
-
|
8
|
+
Autogenerated GraphQL schema
|
9
|
+
EventSourcing toolkit
|
10
|
+
Idiomatic, human-friendly DSL
|
19
11
|
|
20
12
|
## Top to bottom vision
|
21
|
-
|
22
|
-
Autogenerated GraphQL used to invoke Commands and Queries, which read from Projections,
|
23
|
-
Aggregates, generate and persist events. Events persisted in EventStore.
|
13
|
+
Autogenerated GraphQL used to invoke Commands and Queries, which read from Projections, Aggregates, generate and persist events. Events persisted in EventStore.
|
24
14
|
|
25
15
|
## Approach
|
26
16
|
|
27
17
|
Minimal prototypes of primitives
|
28
18
|
Minimal prototype of integrated system
|
29
19
|
Minimal prototype of application based on system
|
30
|
-
Refactoring
|
20
|
+
Refactoring-Improvement-Refactoring
|
31
21
|
|
32
22
|
## Challenges
|
33
23
|
|
@@ -35,6 +25,13 @@ Brain split,
|
|
35
25
|
Deliver once,
|
36
26
|
Transactional consistency,
|
37
27
|
Nesting transactions,
|
28
|
+
Persistence,
|
29
|
+
Serialization,
|
30
|
+
Validations,
|
31
|
+
Command composition,
|
32
|
+
Nested transactions,
|
33
|
+
Async execution
|
34
|
+
|
38
35
|
Event/Command/Schema evolution (behavior, naming, structure),
|
39
36
|
Identity (Sequent -- Repository for aggregates.
|
40
37
|
Implements the Unit-Of-Work and Identity-Map patterns to ensure each aggregate is only loaded once per transaction and that you always get the same aggregate instance back.)
|
@@ -46,6 +43,14 @@ No explicit types ?
|
|
46
43
|
|
47
44
|
|
48
45
|
## Checklist
|
46
|
+
* DX
|
47
|
+
- [x] Git Flow
|
48
|
+
- [ ] CI
|
49
|
+
- [ ] Ruby below 2.5.0 (yield_self usage)
|
50
|
+
- [ ] Linter
|
51
|
+
- [ ] Dependency watcher
|
52
|
+
- [ ] Badges
|
53
|
+
|
49
54
|
* Command
|
50
55
|
- [x] Skeleton without any meaningful structure
|
51
56
|
- [x] With inner class input contract
|
@@ -54,6 +59,7 @@ No explicit types ?
|
|
54
59
|
- [x] With explicit method output contract
|
55
60
|
- [x] Without input contract
|
56
61
|
- [x] Without output contract
|
62
|
+
- [ ] Dry-transaction?
|
57
63
|
- [ ] Unified result (Relay?)
|
58
64
|
- [ ] Multistep
|
59
65
|
- [ ] Composition
|
@@ -68,23 +74,27 @@ No explicit types ?
|
|
68
74
|
- [x] In memory store
|
69
75
|
- [ ] Serialization and typecasting?
|
70
76
|
- [x] Aggregates
|
77
|
+
- [ ] Snapshots
|
71
78
|
- [ ] Causality
|
72
79
|
- [ ] Correlations
|
73
80
|
- [ ] Transactions
|
74
|
-
- [ ] Snapshots
|
75
81
|
- [ ] Projections
|
76
82
|
- [ ] Versioning
|
77
83
|
- [ ] Locking
|
84
|
+
- [ ] Blockchain?
|
78
85
|
- [ ] Lifecycle and maintenance tasks
|
79
86
|
- [ ] Sequel store
|
80
87
|
- [ ] ActiveRecord store
|
81
88
|
- [ ] Redis store
|
82
89
|
- [ ] Kafka store
|
90
|
+
- [ ] IPFS store?
|
91
|
+
- [ ] IPDB store?
|
83
92
|
- [ ] Docs from comments
|
84
93
|
* Event
|
85
94
|
- [ ] Hash?
|
86
95
|
- [x] Object?
|
87
|
-
- [
|
96
|
+
- [x] Immutability?
|
97
|
+
- [x] Aggregate DSL
|
88
98
|
- [ ] Correlation DSL
|
89
99
|
- [ ] Causation DSL
|
90
100
|
- [ ] Module?
|
@@ -97,6 +107,8 @@ No explicit types ?
|
|
97
107
|
- [x] Class instance
|
98
108
|
- [x] With events
|
99
109
|
- [x] Data transfer DSL
|
110
|
+
- [x] To hash
|
111
|
+
- [ ] Aggregate from namespaces (*::Events* ?)
|
100
112
|
- [ ] Stream DSL
|
101
113
|
- [ ] Projection DSL
|
102
114
|
- [ ] Conversion to Hash
|
@@ -104,6 +116,8 @@ No explicit types ?
|
|
104
116
|
- [ ] With contract?
|
105
117
|
- [ ] Code & test generator
|
106
118
|
- [ ] Docs from comments
|
119
|
+
* Projections
|
120
|
+
- [ ] Skeleton without any functionality
|
107
121
|
* GraphQL
|
108
122
|
- [x] Data type
|
109
123
|
- [x] Query
|
@@ -115,7 +129,6 @@ No explicit types ?
|
|
115
129
|
- [ ] Query generation
|
116
130
|
- [ ] Mutation generation
|
117
131
|
- [ ] Schema from hash?
|
118
|
-
* CI
|
119
132
|
* JRuby tests
|
120
133
|
* OpalRuby tests
|
121
134
|
|
data/lib/dry/facts/aggregate.rb
CHANGED
@@ -12,40 +12,39 @@ module Dry
|
|
12
12
|
attr_accessor :uuid
|
13
13
|
|
14
14
|
class << self
|
15
|
-
def
|
16
|
-
|
17
|
-
|
15
|
+
def aggregate_data_from_event_types *event_klasses
|
16
|
+
event_klasses.each do |klass|
|
17
|
+
define_event_type_handler klass, :_transfer_data_from_event
|
18
|
+
end
|
18
19
|
end
|
19
20
|
|
20
21
|
def build_all_from_events events
|
21
22
|
events
|
22
|
-
.group_by {|e|
|
23
|
-
.map
|
23
|
+
.group_by {|e| e.aggregate_id }
|
24
|
+
.map {|_, g_events| build_one_from_events(g_events) }
|
24
25
|
end
|
25
26
|
|
26
|
-
def
|
27
|
-
|
27
|
+
def build_one_from_events events
|
28
|
+
self
|
29
|
+
.new(events)
|
28
30
|
end
|
29
31
|
|
30
|
-
def
|
31
|
-
@event_handlers
|
32
|
+
def define_event_type_handler klass, method_name
|
33
|
+
@event_handlers ||= {}
|
34
|
+
@event_handlers[klass.name] = method_name
|
32
35
|
end
|
33
36
|
|
34
|
-
def
|
35
|
-
|
37
|
+
def event_handlers
|
38
|
+
@event_handlers
|
36
39
|
end
|
37
40
|
|
38
|
-
def
|
39
|
-
@event_handlers
|
40
|
-
@event_handlers[name] = method_name
|
41
|
+
def event_types
|
42
|
+
@event_handlers.keys
|
41
43
|
end
|
42
44
|
|
43
|
-
def
|
44
|
-
|
45
|
-
define_event_handler klass.name, :_transfer_data_from_event
|
46
|
-
end
|
45
|
+
def find_event_handler(key)
|
46
|
+
(@event_handlers)[key]
|
47
47
|
end
|
48
|
-
|
49
48
|
end
|
50
49
|
|
51
50
|
def initialize(events)
|
@@ -59,9 +58,12 @@ module Dry
|
|
59
58
|
end
|
60
59
|
|
61
60
|
def to_h
|
62
|
-
|
61
|
+
self
|
62
|
+
.instance_variables
|
63
|
+
.map {|ivar_name| ivar_name.to_s.gsub(/@/, '')}
|
64
|
+
.yield_self {|ivars| ivars - ['events', 'uuid']}
|
65
|
+
.reduce(Hash.new) {|memo, obj| memo[obj.to_sym] = self.send(obj); memo }
|
63
66
|
end
|
64
|
-
|
65
67
|
alias_method :to_hash, :to_h
|
66
68
|
|
67
69
|
private
|
@@ -70,20 +72,22 @@ module Dry
|
|
70
72
|
uuid = SecureRandom.uuid,
|
71
73
|
{ id: uuid,
|
72
74
|
uuid: uuid,
|
73
|
-
type: { name:
|
74
|
-
version: '0' } }
|
75
|
+
type: { name: self.class.name } }
|
75
76
|
end
|
76
77
|
|
77
78
|
def _handle_event event
|
78
79
|
if self.uuid != event.metadata[:aggregate_id]
|
79
|
-
fail "Event don't belong to aggregate instance.
|
80
|
+
fail "Event don't belong to aggregate instance. \n
|
81
|
+
Aggregate #{self.inspect} \n
|
82
|
+
Event: #{event.inspect}"
|
80
83
|
end
|
84
|
+
|
81
85
|
handler_name =
|
82
86
|
self
|
83
87
|
.class
|
84
88
|
.find_event_handler(event.metadata[:type][:name])
|
85
89
|
|
86
|
-
self.send(handler_name, event)
|
90
|
+
self.send(handler_name, event) if handler_name
|
87
91
|
end
|
88
92
|
|
89
93
|
def _transfer_data_from_event event
|
data/lib/dry/facts/command.rb
CHANGED
@@ -36,14 +36,14 @@ module Dry
|
|
36
36
|
def define_input_contract &block
|
37
37
|
@input_contract =
|
38
38
|
Class
|
39
|
-
.new(Dry::Struct)
|
39
|
+
.new(Dry::Struct::Value)
|
40
40
|
.yield_self { |k| k.instance_eval(&block) if block }
|
41
41
|
end
|
42
42
|
|
43
43
|
def define_output_contract &block
|
44
44
|
@output_contract =
|
45
45
|
Class
|
46
|
-
.new(Dry::Struct)
|
46
|
+
.new(Dry::Struct::Value)
|
47
47
|
.yield_self { |k| k.instance_eval(&block) if block }
|
48
48
|
end
|
49
49
|
|
@@ -54,7 +54,8 @@ module Dry
|
|
54
54
|
self::InputContract
|
55
55
|
else
|
56
56
|
self.input_contract
|
57
|
-
end
|
57
|
+
end
|
58
|
+
.yield_self {|c| _enforce_contract_if_present(c, input) }
|
58
59
|
end
|
59
60
|
|
60
61
|
def _enforce_output_contract_on output
|
@@ -62,16 +63,17 @@ module Dry
|
|
62
63
|
self::OutputContract
|
63
64
|
else
|
64
65
|
self.output_contract
|
65
|
-
end
|
66
|
+
end
|
67
|
+
.yield_self {|c| _enforce_contract_if_present(c, output) }
|
66
68
|
end
|
67
69
|
|
68
70
|
def _execute_with input
|
69
71
|
self.new.call input
|
70
72
|
end
|
71
73
|
|
72
|
-
def _enforce_contract_if_present
|
74
|
+
def _enforce_contract_if_present contract, data
|
73
75
|
if contract
|
74
|
-
contract.new
|
76
|
+
contract.new **data
|
75
77
|
else
|
76
78
|
data
|
77
79
|
end
|
data/lib/dry/facts/version.rb
CHANGED