realm-core 0.7.3 → 0.7.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Build status](https://badge.buildkite.com/346cce75f6c31e0a41bb98b198e85eb6b722243624459fad9c.svg)](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
|
-
![Service layers](https://confluence-connect.gliffy.net/embed/image/d02d04b1-5e40-415f-b7ba-3a631efa9bf3.png?utm_medium=live&utm_source=custom)
|
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
|
-
![Service external components](https://confluence-connect.gliffy.net/embed/image/c593fcc2-304e-47c3-8e3c-b0cc09e0ed54.png?utm_medium=live&utm_source=custom)
|
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'
|