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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/realm-core.rb +11 -0
  3. data/lib/realm.rb +20 -0
  4. data/lib/realm/action_handler.rb +84 -0
  5. data/lib/realm/action_handler/result.rb +32 -0
  6. data/lib/realm/builder.rb +84 -0
  7. data/lib/realm/command_handler.rb +18 -0
  8. data/lib/realm/config.rb +56 -0
  9. data/lib/realm/container.rb +65 -0
  10. data/lib/realm/context.rb +35 -0
  11. data/lib/realm/dependency.rb +22 -0
  12. data/lib/realm/dispatcher.rb +66 -0
  13. data/lib/realm/domain_resolver.rb +53 -0
  14. data/lib/realm/error.rb +62 -0
  15. data/lib/realm/event.rb +55 -0
  16. data/lib/realm/event_factory.rb +49 -0
  17. data/lib/realm/event_handler.rb +94 -0
  18. data/lib/realm/event_router.rb +91 -0
  19. data/lib/realm/event_router/gateway.rb +50 -0
  20. data/lib/realm/event_router/internal_loop_gateway.rb +48 -0
  21. data/lib/realm/health_status.rb +44 -0
  22. data/lib/realm/mixins/aggregate_member.rb +25 -0
  23. data/lib/realm/mixins/context_injection.rb +47 -0
  24. data/lib/realm/mixins/controller.rb +50 -0
  25. data/lib/realm/mixins/decorator.rb +33 -0
  26. data/lib/realm/mixins/dependency_injection.rb +50 -0
  27. data/lib/realm/mixins/reactive.rb +30 -0
  28. data/lib/realm/mixins/repository_helper.rb +41 -0
  29. data/lib/realm/multi_worker.rb +30 -0
  30. data/lib/realm/persistence.rb +51 -0
  31. data/lib/realm/persistence/repository_query_handler_adapter.rb +21 -0
  32. data/lib/realm/plugin.rb +17 -0
  33. data/lib/realm/query_handler.rb +6 -0
  34. data/lib/realm/runtime.rb +51 -0
  35. data/lib/realm/runtime/session.rb +31 -0
  36. data/lib/realm/types.rb +9 -0
  37. metadata +36 -3
  38. data/README.md +0 -40
  39. 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
@@ -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,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Realm
4
+ class QueryHandler < Realm::ActionHandler
5
+ end
6
+ 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
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-types'
4
+
5
+ module Realm
6
+ module Types
7
+ include Dry.Types()
8
+ end
9
+ end
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.3
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
- - README.md
174
- - Rakefile
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'