realm-core 0.7.3 → 0.7.4
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/lib/realm-core.rb +11 -0
- data/lib/realm.rb +20 -0
- data/lib/realm/action_handler.rb +84 -0
- data/lib/realm/action_handler/result.rb +32 -0
- data/lib/realm/builder.rb +84 -0
- data/lib/realm/command_handler.rb +18 -0
- data/lib/realm/config.rb +56 -0
- data/lib/realm/container.rb +65 -0
- data/lib/realm/context.rb +35 -0
- data/lib/realm/dependency.rb +22 -0
- data/lib/realm/dispatcher.rb +66 -0
- data/lib/realm/domain_resolver.rb +53 -0
- data/lib/realm/error.rb +62 -0
- data/lib/realm/event.rb +55 -0
- data/lib/realm/event_factory.rb +49 -0
- data/lib/realm/event_handler.rb +94 -0
- data/lib/realm/event_router.rb +91 -0
- data/lib/realm/event_router/gateway.rb +50 -0
- data/lib/realm/event_router/internal_loop_gateway.rb +48 -0
- data/lib/realm/health_status.rb +44 -0
- data/lib/realm/mixins/aggregate_member.rb +25 -0
- data/lib/realm/mixins/context_injection.rb +47 -0
- data/lib/realm/mixins/controller.rb +50 -0
- data/lib/realm/mixins/decorator.rb +33 -0
- data/lib/realm/mixins/dependency_injection.rb +50 -0
- data/lib/realm/mixins/reactive.rb +30 -0
- data/lib/realm/mixins/repository_helper.rb +41 -0
- data/lib/realm/multi_worker.rb +30 -0
- data/lib/realm/persistence.rb +51 -0
- data/lib/realm/persistence/repository_query_handler_adapter.rb +21 -0
- data/lib/realm/plugin.rb +17 -0
- data/lib/realm/query_handler.rb +6 -0
- data/lib/realm/runtime.rb +51 -0
- data/lib/realm/runtime/session.rb +31 -0
- data/lib/realm/types.rb +9 -0
- metadata +36 -3
- data/README.md +0 -40
- data/Rakefile +0 -19
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Realm
|
4
|
+
class Persistence
|
5
|
+
class InvalidPersistanceType < Realm::Error; end
|
6
|
+
class Conflict < Realm::Error; end
|
7
|
+
|
8
|
+
class RelationIsReadOnly < Realm::Error
|
9
|
+
def initialize(relation, msg: "Cannot write using read-only relation #{relation.class}")
|
10
|
+
super(msg)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class RepositoryIsReadOnly < Realm::Error
|
15
|
+
def initialize(repo, msg: "Cannot write using read-only repository #{repo.class}")
|
16
|
+
super(msg)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.setup(...)
|
21
|
+
new(...).setup
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(container, repositories)
|
25
|
+
@container = container
|
26
|
+
@repositories = repositories
|
27
|
+
end
|
28
|
+
|
29
|
+
def setup
|
30
|
+
register_repos
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def gateway
|
36
|
+
@gateway ||= @container.resolve('persistence.gateway')
|
37
|
+
end
|
38
|
+
|
39
|
+
def register_repos
|
40
|
+
@repositories.each do |repo_class|
|
41
|
+
@container.register_factory(repo_class, gateway, as: "#{repo_class.name.demodulize.underscore}_repo")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def constantize(*parts)
|
46
|
+
return parts[0] unless parts[0].is_a?(String)
|
47
|
+
|
48
|
+
parts.join('::').safe_constantize
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Realm
|
4
|
+
class Persistence
|
5
|
+
class QueryCannotModifyState < Realm::Error; end
|
6
|
+
|
7
|
+
class RepositoryQueryHandlerAdapter
|
8
|
+
def initialize(repo)
|
9
|
+
@repo = repo.respond_to?(:readonly) ? repo.readonly : repo
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(action:, params: {}, **)
|
13
|
+
raise CannotHandleAction.new(self, action) unless @repo.respond_to?(action)
|
14
|
+
|
15
|
+
@repo.send(action, **params)
|
16
|
+
rescue RepositoryIsReadOnly
|
17
|
+
raise QueryCannotModifyState
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/realm/plugin.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Realm
|
4
|
+
class Plugin
|
5
|
+
class << self
|
6
|
+
def plugin_name(value = :not_provided)
|
7
|
+
@plugin_name = value.to_sym unless value == :not_provided
|
8
|
+
@plugin_name = name.split('::')[-2].underscore.to_sym unless defined?(@plugin_name)
|
9
|
+
@plugin_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def setup(_config, _container)
|
13
|
+
raise NotImplementedError
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Realm
|
4
|
+
class Runtime
|
5
|
+
delegate :query, :run, :run_as_job, :wait_for_jobs, to: :dispatcher
|
6
|
+
delegate :trigger, :add_listener, to: :event_router
|
7
|
+
delegate :[], to: :context
|
8
|
+
attr_reader :container
|
9
|
+
|
10
|
+
def initialize(container = Container.new)
|
11
|
+
@container = Container[container]
|
12
|
+
end
|
13
|
+
|
14
|
+
def context
|
15
|
+
@context ||= Context.new(@container)
|
16
|
+
end
|
17
|
+
|
18
|
+
def session(context = {})
|
19
|
+
context.blank? ? self : Session.new(self, context)
|
20
|
+
end
|
21
|
+
|
22
|
+
def worker(*args)
|
23
|
+
MultiWorker.new(event_router.try(:workers, *args) || [])
|
24
|
+
end
|
25
|
+
|
26
|
+
def health
|
27
|
+
component_statuses = container.each_with_object({}) do |(name, component), map|
|
28
|
+
map[name] = component.health if component.respond_to?(:health) && !component.is_a?(Runtime)
|
29
|
+
end
|
30
|
+
HealthStatus.combine(component_statuses)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get all active messaging queues. For maintenance purpose only.
|
34
|
+
# TODO: Introduce component container and allow to call those method directly on components instead of
|
35
|
+
# polluting runtime
|
36
|
+
# Example: engine.realm.components.find(type: Realm::EventRouter::SNSGateway).try(:active_queues)
|
37
|
+
def active_queues
|
38
|
+
event_router.try(:active_queues) || []
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def dispatcher
|
44
|
+
@dispatcher ||= container.create(Dispatcher, self)
|
45
|
+
end
|
46
|
+
|
47
|
+
def event_router
|
48
|
+
@container[EventRouter]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Realm
|
4
|
+
class Runtime
|
5
|
+
class Session
|
6
|
+
delegate :query, :run, :run_as_job, :wait_for_jobs, to: :dispatcher
|
7
|
+
delegate :add_listener, :trigger, :worker, to: :@runtime
|
8
|
+
delegate :[], to: :context
|
9
|
+
attr_reader :context
|
10
|
+
|
11
|
+
def initialize(runtime, context)
|
12
|
+
@runtime = runtime
|
13
|
+
@context = runtime.context.merge(context)
|
14
|
+
end
|
15
|
+
|
16
|
+
def session(context = {})
|
17
|
+
context.blank? ? self : self.class.new(self, context)
|
18
|
+
end
|
19
|
+
|
20
|
+
def container
|
21
|
+
@runtime.container
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def dispatcher
|
27
|
+
@dispatcher ||= container.create(Dispatcher, self)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/realm/types.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: realm-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- developers@reevoo.com
|
@@ -170,8 +170,41 @@ executables: []
|
|
170
170
|
extensions: []
|
171
171
|
extra_rdoc_files: []
|
172
172
|
files:
|
173
|
-
-
|
174
|
-
-
|
173
|
+
- lib/realm-core.rb
|
174
|
+
- lib/realm.rb
|
175
|
+
- lib/realm/action_handler.rb
|
176
|
+
- lib/realm/action_handler/result.rb
|
177
|
+
- lib/realm/builder.rb
|
178
|
+
- lib/realm/command_handler.rb
|
179
|
+
- lib/realm/config.rb
|
180
|
+
- lib/realm/container.rb
|
181
|
+
- lib/realm/context.rb
|
182
|
+
- lib/realm/dependency.rb
|
183
|
+
- lib/realm/dispatcher.rb
|
184
|
+
- lib/realm/domain_resolver.rb
|
185
|
+
- lib/realm/error.rb
|
186
|
+
- lib/realm/event.rb
|
187
|
+
- lib/realm/event_factory.rb
|
188
|
+
- lib/realm/event_handler.rb
|
189
|
+
- lib/realm/event_router.rb
|
190
|
+
- lib/realm/event_router/gateway.rb
|
191
|
+
- lib/realm/event_router/internal_loop_gateway.rb
|
192
|
+
- lib/realm/health_status.rb
|
193
|
+
- lib/realm/mixins/aggregate_member.rb
|
194
|
+
- lib/realm/mixins/context_injection.rb
|
195
|
+
- lib/realm/mixins/controller.rb
|
196
|
+
- lib/realm/mixins/decorator.rb
|
197
|
+
- lib/realm/mixins/dependency_injection.rb
|
198
|
+
- lib/realm/mixins/reactive.rb
|
199
|
+
- lib/realm/mixins/repository_helper.rb
|
200
|
+
- lib/realm/multi_worker.rb
|
201
|
+
- lib/realm/persistence.rb
|
202
|
+
- lib/realm/persistence/repository_query_handler_adapter.rb
|
203
|
+
- lib/realm/plugin.rb
|
204
|
+
- lib/realm/query_handler.rb
|
205
|
+
- lib/realm/runtime.rb
|
206
|
+
- lib/realm/runtime/session.rb
|
207
|
+
- lib/realm/types.rb
|
175
208
|
homepage:
|
176
209
|
licenses:
|
177
210
|
- MIT
|
data/README.md
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
# Realm
|
2
|
-
|
3
|
-
Domain layer framework following Domain-driven/CQRS design principles.
|
4
|
-
|
5
|
-
[](https://buildkite.com/reevoo/realm)
|
6
|
-
|
7
|
-
## Service layers
|
8
|
-
|
9
|
-
We follow the standard MVC design pattern of Rails but giving the model layer more structure and guidance regarding where
|
10
|
-
to put your code. The model is split into domain layer (using our [Realm](https://github.com/reevoo/smart-mono/tree/master/gems/realm) library)
|
11
|
-
and persistence layer (using [ROM](https://rom-rb.org/) library). The individual components are explained in the following section.
|
12
|
-
|
13
|
-

|
14
|
-
|
15
|
-
Advanced components are shown in lighter color, those will be needed only later on as the service domain logic grows.
|
16
|
-
|
17
|
-
## Model layer components
|
18
|
-
|
19
|
-

|
20
|
-
|
21
|
-
Each service has one **domain** module which consists of multiple [**aggregate**](https://martinfowler.com/bliki/DDD_Aggregate.html) modules.
|
22
|
-
Aggregate is a cluster of domain objects that can be treated as a single unit. The only way for outer world to communicate
|
23
|
-
with aggregate is by **queries** and **commands**. Query exposes aggregate's internal state and command changes it.
|
24
|
-
The state of an aggregate is represented by tree of **entities** with one being the aggregate root and zero or more dependent
|
25
|
-
entities with *belongs_to* relation to the root entity. The state of an aggregate (entity tree) is persisted
|
26
|
-
and retrieved by **repository**. There is generally one repository per aggregate unless we split the read/write
|
27
|
-
(query/command) persistence model for that particular domain. The repository uses **relations** to access the database
|
28
|
-
tables. Each relation class represents one table.
|
29
|
-
|
30
|
-
|
31
|
-
## Where to put my code as it grows?
|
32
|
-
|
33
|
-
TODO
|
34
|
-
|
35
|
-
|
36
|
-
## Roadmap
|
37
|
-
|
38
|
-
- [ ] Support Ruby 3
|
39
|
-
- [ ] Make it work outside of Rails engines
|
40
|
-
- [ ] Support multiple persistence gateways in one runtime
|
data/Rakefile
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
begin
|
4
|
-
require 'bundler/setup'
|
5
|
-
rescue LoadError
|
6
|
-
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
-
end
|
8
|
-
|
9
|
-
require 'rdoc/task'
|
10
|
-
|
11
|
-
RDoc::Task.new(:rdoc) do |rdoc|
|
12
|
-
rdoc.rdoc_dir = 'rdoc'
|
13
|
-
rdoc.title = 'Realm'
|
14
|
-
rdoc.options << '--line-numbers'
|
15
|
-
rdoc.rdoc_files.include('README.md')
|
16
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
17
|
-
end
|
18
|
-
|
19
|
-
require 'bundler/gem_tasks'
|