From 15a8e9feb6100ed563d80ba968e7464de3fb3602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Tue, 12 May 2020 20:24:30 +0200 Subject: [PATCH 01/35] Wrap EventRepository - connect to multiple DBs --- contrib/connected_active_record/Gemfile | 9 +++ .../connected_active_record.gemspec | 28 +++++++++ .../lib/connected_active_record.rb | 5 ++ .../lib/connected_active_record/repository.rb | 59 +++++++++++++++++++ .../lib/connected_active_record/version.rb | 3 + .../spec/connected_active_record_spec.rb | 14 +++++ .../spec/spec_helper.rb | 2 + 7 files changed, 120 insertions(+) create mode 100644 contrib/connected_active_record/Gemfile create mode 100644 contrib/connected_active_record/connected_active_record.gemspec create mode 100644 contrib/connected_active_record/lib/connected_active_record.rb create mode 100644 contrib/connected_active_record/lib/connected_active_record/repository.rb create mode 100644 contrib/connected_active_record/lib/connected_active_record/version.rb create mode 100644 contrib/connected_active_record/spec/connected_active_record_spec.rb create mode 100644 contrib/connected_active_record/spec/spec_helper.rb diff --git a/contrib/connected_active_record/Gemfile b/contrib/connected_active_record/Gemfile new file mode 100644 index 0000000000..0e24ae532f --- /dev/null +++ b/contrib/connected_active_record/Gemfile @@ -0,0 +1,9 @@ +source "https://rubygems.org" + +git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } + +# Specify your gem's dependencies in connected_active_record.gemspec +gemspec + +gem 'ruby_event_store', path: '../../ruby_event_store' +gem 'rails_event_store_active_record', path: '../../rails_event_store_active_record' \ No newline at end of file diff --git a/contrib/connected_active_record/connected_active_record.gemspec b/contrib/connected_active_record/connected_active_record.gemspec new file mode 100644 index 0000000000..f842800a19 --- /dev/null +++ b/contrib/connected_active_record/connected_active_record.gemspec @@ -0,0 +1,28 @@ +lib = File.expand_path("../lib", __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require "connected_active_record/version" + +Gem::Specification.new do |spec| + spec.name = "connected_active_record" + spec.version = ConnectedActiveRecord::VERSION + spec.authors = ["Mirosław Pragłowski"] + spec.email = ["m@praglowski.com"] + + spec.summary = %q{EventRepository wrapper to allow easy use of + https://guides.rubyonrails.org/active_record_multiple_databases.html} + + spec.add_dependency "activerecord", ">= 6" + spec.add_dependency "ruby_event_store" + spec.add_dependency "rails_event_store_active_record" + + spec.add_development_dependency "rspec-rails" + + # Specify which files should be added to the gem when it is released. + # The `git ls-files -z` loads the files in the RubyGem that have been added into git. + spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do + `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + end + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] +end \ No newline at end of file diff --git a/contrib/connected_active_record/lib/connected_active_record.rb b/contrib/connected_active_record/lib/connected_active_record.rb new file mode 100644 index 0000000000..c7c7865121 --- /dev/null +++ b/contrib/connected_active_record/lib/connected_active_record.rb @@ -0,0 +1,5 @@ +require 'connected_active_record/version' +require 'connected_active_record/repository' + +module ConnectedActiveRecord +end \ No newline at end of file diff --git a/contrib/connected_active_record/lib/connected_active_record/repository.rb b/contrib/connected_active_record/lib/connected_active_record/repository.rb new file mode 100644 index 0000000000..4fbe306449 --- /dev/null +++ b/contrib/connected_active_record/lib/connected_active_record/repository.rb @@ -0,0 +1,59 @@ +require 'active_record' +require 'rails_event_store_active_record' + +module ConnectedActiveRecord + class Repository + def initialize(writing: nil, reading: nil) + @repository = RailsEventStoreActiveRecord::EventRepository.new( + build_base_klass(writing, reading) + ) + end + + def append_to_stream(events, stream, expected_version) + repository.append_to_stream(events, stream, expected_version) + end + + def link_to_stream(event_ids, stream, expected_version) + repository.link_to_stream(event_ids, stream, expected_version) + end + + def delete_stream(stream) + repository.delete_stream(stream) + end + + def has_event?(event_id) + repository.has_event?(event_id) + end + + def last_stream_event(stream) + repository.last_stream_event(stream) + end + + def read(specification) + repository.read(specification) + end + + def count(specification) + repository.count(specification) + end + + def update_messages(messages) + repository.update_messages(messages) + end + + def streams_of(event_id) + repository.streams_of(event_id) + end + + private + attr_reader :repository + + def build_base_klass(writing, reading) + return ActiveRecord::Base if writing.nil? && reading.nil? + Class.new(ActiveRecord::Base) do + self.abstract_class = true + connects_to database: { writing: writing, reading: reading } + end + end + end +end diff --git a/contrib/connected_active_record/lib/connected_active_record/version.rb b/contrib/connected_active_record/lib/connected_active_record/version.rb new file mode 100644 index 0000000000..9dd1c6bb58 --- /dev/null +++ b/contrib/connected_active_record/lib/connected_active_record/version.rb @@ -0,0 +1,3 @@ +module ConnectedActiveRecord + VERSION = "0.0.1" +end \ No newline at end of file diff --git a/contrib/connected_active_record/spec/connected_active_record_spec.rb b/contrib/connected_active_record/spec/connected_active_record_spec.rb new file mode 100644 index 0000000000..3bd2022c73 --- /dev/null +++ b/contrib/connected_active_record/spec/connected_active_record_spec.rb @@ -0,0 +1,14 @@ +require "spec_helper" +require 'ruby_event_store' +require 'ruby_event_store/spec/event_repository_lint' + +module ConnectedActiveRecord + RSpec.describe Repository do + let(:test_race_conditions_any) { false } + let(:test_race_conditions_auto) { false } + let(:test_binary) { false } + let(:test_change) { false } + + it_behaves_like :event_repository, Repository + end +end \ No newline at end of file diff --git a/contrib/connected_active_record/spec/spec_helper.rb b/contrib/connected_active_record/spec/spec_helper.rb new file mode 100644 index 0000000000..1a079c3924 --- /dev/null +++ b/contrib/connected_active_record/spec/spec_helper.rb @@ -0,0 +1,2 @@ +require '../../support/helpers/rspec_defaults' +require 'connected_active_record' \ No newline at end of file From b8f0f121bc7ded31a7f50241ee3d223ba0aa875d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Tue, 19 May 2020 15:03:53 +0200 Subject: [PATCH 02/35] Establish database schema for tests --- .../spec/connected_active_record_spec.rb | 12 ++++++++++++ contrib/connected_active_record/spec/spec_helper.rb | 8 +++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/contrib/connected_active_record/spec/connected_active_record_spec.rb b/contrib/connected_active_record/spec/connected_active_record_spec.rb index 3bd2022c73..d723aaed2f 100644 --- a/contrib/connected_active_record/spec/connected_active_record_spec.rb +++ b/contrib/connected_active_record/spec/connected_active_record_spec.rb @@ -4,6 +4,18 @@ module ConnectedActiveRecord RSpec.describe Repository do + include SchemaHelper + + around(:each) do |example| + begin + establish_database_connection + load_database_schema + example.run + ensure + drop_database + end + end + let(:test_race_conditions_any) { false } let(:test_race_conditions_auto) { false } let(:test_binary) { false } diff --git a/contrib/connected_active_record/spec/spec_helper.rb b/contrib/connected_active_record/spec/spec_helper.rb index 1a079c3924..163bb7a16d 100644 --- a/contrib/connected_active_record/spec/spec_helper.rb +++ b/contrib/connected_active_record/spec/spec_helper.rb @@ -1,2 +1,8 @@ require '../../support/helpers/rspec_defaults' -require 'connected_active_record' \ No newline at end of file +require_relative '../../../support/helpers/schema_helper' +require 'connected_active_record' + +$verbose = ENV.has_key?('VERBOSE') ? true : false +ActiveRecord::Schema.verbose = $verbose + +ENV['DATABASE_URL'] ||= 'sqlite3:db.sqlite3' \ No newline at end of file From c677ed2f437e4a5276df85747d6cc67f282a87a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Tue, 19 May 2020 15:22:35 +0200 Subject: [PATCH 03/35] Require relative --- contrib/connected_active_record/spec/spec_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/connected_active_record/spec/spec_helper.rb b/contrib/connected_active_record/spec/spec_helper.rb index 163bb7a16d..b3aaa77a33 100644 --- a/contrib/connected_active_record/spec/spec_helper.rb +++ b/contrib/connected_active_record/spec/spec_helper.rb @@ -1,4 +1,4 @@ -require '../../support/helpers/rspec_defaults' +require_relative '../../../support/helpers/rspec_defaults' require_relative '../../../support/helpers/schema_helper' require 'connected_active_record' From b44270128e2c1eb7fd7988be430c5ec9abd4c59e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Tue, 19 May 2020 15:41:12 +0200 Subject: [PATCH 04/35] Missing gems --- contrib/connected_active_record/Gemfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contrib/connected_active_record/Gemfile b/contrib/connected_active_record/Gemfile index 0e24ae532f..e44040ada1 100644 --- a/contrib/connected_active_record/Gemfile +++ b/contrib/connected_active_record/Gemfile @@ -6,4 +6,6 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } gemspec gem 'ruby_event_store', path: '../../ruby_event_store' -gem 'rails_event_store_active_record', path: '../../rails_event_store_active_record' \ No newline at end of file +gem 'rails_event_store_active_record', path: '../../rails_event_store_active_record' +gem 'childprocess' +gem 'sqlite3', '1.4.2' \ No newline at end of file From ea69181f6612fe048f66956c738c9b5873172b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Tue, 2 Jun 2020 14:31:43 +0200 Subject: [PATCH 05/35] Parametrize race condition test runs as in RESAR event repository specs --- .../spec/connected_active_record_spec.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contrib/connected_active_record/spec/connected_active_record_spec.rb b/contrib/connected_active_record/spec/connected_active_record_spec.rb index d723aaed2f..68d1899f0c 100644 --- a/contrib/connected_active_record/spec/connected_active_record_spec.rb +++ b/contrib/connected_active_record/spec/connected_active_record_spec.rb @@ -16,11 +16,11 @@ module ConnectedActiveRecord end end - let(:test_race_conditions_any) { false } - let(:test_race_conditions_auto) { false } - let(:test_binary) { false } - let(:test_change) { false } + let(:test_race_conditions_auto) { !ENV['DATABASE_URL'].include?("sqlite") } + let(:test_race_conditions_any) { !ENV['DATABASE_URL'].include?("sqlite") } + let(:test_binary) { true } + let(:test_change) { true } it_behaves_like :event_repository, Repository end -end \ No newline at end of file +end From 8f4671a4b00ae4cce6b6b50499fe861db5c627f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 4 Jun 2020 15:28:09 +0200 Subject: [PATCH 06/35] WIP: testing multiple database event repository --- .../spec/connected_active_record_spec.rb | 80 ++++++++++++++++--- 1 file changed, 68 insertions(+), 12 deletions(-) diff --git a/contrib/connected_active_record/spec/connected_active_record_spec.rb b/contrib/connected_active_record/spec/connected_active_record_spec.rb index 68d1899f0c..f87601f23a 100644 --- a/contrib/connected_active_record/spec/connected_active_record_spec.rb +++ b/contrib/connected_active_record/spec/connected_active_record_spec.rb @@ -6,21 +6,77 @@ module ConnectedActiveRecord RSpec.describe Repository do include SchemaHelper - around(:each) do |example| - begin - establish_database_connection - load_database_schema - example.run - ensure - drop_database + context 'like a repository' do + around(:each) do |example| + begin + establish_database_connection + load_database_schema + example.run + ensure + drop_database + close_database_connection + end end + + let(:test_race_conditions_auto) { !ENV['DATABASE_URL'].include?("sqlite") } + let(:test_race_conditions_any) { !ENV['DATABASE_URL'].include?("sqlite") } + let(:test_binary) { true } + let(:test_change) { true } + + it_behaves_like :event_repository, Repository end - let(:test_race_conditions_auto) { !ENV['DATABASE_URL'].include?("sqlite") } - let(:test_race_conditions_any) { !ENV['DATABASE_URL'].include?("sqlite") } - let(:test_binary) { true } - let(:test_change) { true } + context 'multiple databases' do + class PrimaryApplicationrecord < ActiveRecord::Base + self.abstract_class = true + establish_connection({ + adapter: "sqlite3", + primary: { + database: "primary.db" + }, + secondary: { + database: "secondary.db" + } + }) + connects_to database: { writing: :primary, reading: :primary } + end + + class SecondaryApplicationrecord < ActiveRecord::Base + self.abstract_class = true + establish_connection({ + adapter: "sqlite3", + primary: { + database: "primary.db" + }, + secondary: { + database: "secondary.db" + } + }) + connects_to database: { writing: :secondary, reading: :secondary } + end + + specify "each repository instance could have it's own database" do + mapper = RubyEventStore::Mappers::NullMapper.new + + primary_repository = Repository.new(PrimaryApplicationRecord) + primary_reader = RubyEventStore::SpecificationReader.new(primary_repository, mapper) + primary_repository.append_to_stream( + [primary_event = RubyEventStore::SRecord.new], + RubyEventStore::Stream.new(RubyEventStore::GLOBAL_STREAM), + RubyEventStore::ExpectedVersion.any + ) + + secondary_repository = Repository.new(SecondaryApplicationRecord) + secondary_reader = RubyEventStore::SpecificationReader.new(secondary_repository, mapper) + secondary_repository.append_to_stream( + [secondary_event = RubyEventStore::SRecord.new], + RubyEventStore::Stream.new(RubyEventStore::GLOBAL_STREAM), + RubyEventStore::ExpectedVersion.any + ) - it_behaves_like :event_repository, Repository + read_from_primary = primary_reader.read.to_a + read_from_secondary = secondary_reader.read.to_a + end + end end end From 4ee7e127824211ea5418bbb7cc06460dc712cde8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 4 Jun 2020 15:32:42 +0200 Subject: [PATCH 07/35] Multiple databases sample Rails app - initial commit App generated with RES template with options: ``` rails new sample --template "https://railseventstore.org/new" --skip-spring --skip-listen --skip-bootsnap --skip-git --skip-keeps --skip-action-text --skip-active-storage --skip-action-cable --skip-action-mailer --skip-action-mailbox --skip-sprockets --skip-javascript --skip-turbolinks --skip-webpack-install --skip-yarn --skip-test --skip-system-test ``` --- .../connected_active_record/sample/.gitignore | 20 ++++ .../connected_active_record/sample/Gemfile | 30 ++++++ .../connected_active_record/sample/README.md | 24 +++++ .../connected_active_record/sample/Rakefile | 6 ++ .../sample/app/assets/config/manifest.js | 2 + .../app/assets/stylesheets/application.css | 15 +++ .../app/controllers/application_controller.rb | 2 + .../sample/app/helpers/application_helper.rb | 2 + .../sample/app/jobs/application_job.rb | 7 ++ .../sample/app/models/application_record.rb | 3 + .../app/views/layouts/application.html.erb | 14 +++ .../connected_active_record/sample/config.ru | 5 + .../sample/config/application.rb | 35 +++++++ .../sample/config/boot.rb | 3 + .../sample/config/credentials.yml.enc | 1 + .../sample/config/database.yml | 25 +++++ .../sample/config/environment.rb | 5 + .../sample/config/environments/development.rb | 47 ++++++++++ .../sample/config/environments/production.rb | 92 +++++++++++++++++++ .../sample/config/environments/test.rb | 38 ++++++++ .../application_controller_renderer.rb | 8 ++ .../initializers/backtrace_silencers.rb | 7 ++ .../initializers/content_security_policy.rb | 28 ++++++ .../config/initializers/cookies_serializer.rb | 5 + .../initializers/filter_parameter_logging.rb | 4 + .../sample/config/initializers/inflections.rb | 16 ++++ .../sample/config/initializers/mime_types.rb | 4 + .../config/initializers/rails_event_store.rb | 25 +++++ .../config/initializers/wrap_parameters.rb | 14 +++ .../sample/config/locales/en.yml | 33 +++++++ .../sample/config/puma.rb | 38 ++++++++ .../sample/config/routes.rb | 4 + ...0200604152933_create_event_store_events.rb | 45 +++++++++ .../sample/db/schema.rb | 34 +++++++ .../sample/db/seeds.rb | 7 ++ .../sample/public/404.html | 67 ++++++++++++++ .../sample/public/422.html | 67 ++++++++++++++ .../sample/public/500.html | 66 +++++++++++++ .../public/apple-touch-icon-precomposed.png | 0 .../sample/public/apple-touch-icon.png | 0 .../sample/public/favicon.ico | 0 .../sample/public/robots.txt | 1 + 42 files changed, 849 insertions(+) create mode 100644 contrib/connected_active_record/sample/.gitignore create mode 100644 contrib/connected_active_record/sample/Gemfile create mode 100644 contrib/connected_active_record/sample/README.md create mode 100644 contrib/connected_active_record/sample/Rakefile create mode 100644 contrib/connected_active_record/sample/app/assets/config/manifest.js create mode 100644 contrib/connected_active_record/sample/app/assets/stylesheets/application.css create mode 100644 contrib/connected_active_record/sample/app/controllers/application_controller.rb create mode 100644 contrib/connected_active_record/sample/app/helpers/application_helper.rb create mode 100644 contrib/connected_active_record/sample/app/jobs/application_job.rb create mode 100644 contrib/connected_active_record/sample/app/models/application_record.rb create mode 100644 contrib/connected_active_record/sample/app/views/layouts/application.html.erb create mode 100644 contrib/connected_active_record/sample/config.ru create mode 100644 contrib/connected_active_record/sample/config/application.rb create mode 100644 contrib/connected_active_record/sample/config/boot.rb create mode 100644 contrib/connected_active_record/sample/config/credentials.yml.enc create mode 100644 contrib/connected_active_record/sample/config/database.yml create mode 100644 contrib/connected_active_record/sample/config/environment.rb create mode 100644 contrib/connected_active_record/sample/config/environments/development.rb create mode 100644 contrib/connected_active_record/sample/config/environments/production.rb create mode 100644 contrib/connected_active_record/sample/config/environments/test.rb create mode 100644 contrib/connected_active_record/sample/config/initializers/application_controller_renderer.rb create mode 100644 contrib/connected_active_record/sample/config/initializers/backtrace_silencers.rb create mode 100644 contrib/connected_active_record/sample/config/initializers/content_security_policy.rb create mode 100644 contrib/connected_active_record/sample/config/initializers/cookies_serializer.rb create mode 100644 contrib/connected_active_record/sample/config/initializers/filter_parameter_logging.rb create mode 100644 contrib/connected_active_record/sample/config/initializers/inflections.rb create mode 100644 contrib/connected_active_record/sample/config/initializers/mime_types.rb create mode 100644 contrib/connected_active_record/sample/config/initializers/rails_event_store.rb create mode 100644 contrib/connected_active_record/sample/config/initializers/wrap_parameters.rb create mode 100644 contrib/connected_active_record/sample/config/locales/en.yml create mode 100644 contrib/connected_active_record/sample/config/puma.rb create mode 100644 contrib/connected_active_record/sample/config/routes.rb create mode 100644 contrib/connected_active_record/sample/db/migrate/20200604152933_create_event_store_events.rb create mode 100644 contrib/connected_active_record/sample/db/schema.rb create mode 100644 contrib/connected_active_record/sample/db/seeds.rb create mode 100644 contrib/connected_active_record/sample/public/404.html create mode 100644 contrib/connected_active_record/sample/public/422.html create mode 100644 contrib/connected_active_record/sample/public/500.html create mode 100644 contrib/connected_active_record/sample/public/apple-touch-icon-precomposed.png create mode 100644 contrib/connected_active_record/sample/public/apple-touch-icon.png create mode 100644 contrib/connected_active_record/sample/public/favicon.ico create mode 100644 contrib/connected_active_record/sample/public/robots.txt diff --git a/contrib/connected_active_record/sample/.gitignore b/contrib/connected_active_record/sample/.gitignore new file mode 100644 index 0000000000..c4bd440cfd --- /dev/null +++ b/contrib/connected_active_record/sample/.gitignore @@ -0,0 +1,20 @@ +!/log/.keep +!/storage/.keep +!/tmp/.keep +.byebug_history +.yarn-integrity +/.bundle +/config/master.key +/db/*.sqlite3 +/db/*.sqlite3-journal +/elm-stuff +/log/* +/node_modules +/public/assets +/public/packs +/public/packs-test +/storage/* +/tmp/* +/yarn-error.log +coverage +yarn-debug.log* diff --git a/contrib/connected_active_record/sample/Gemfile b/contrib/connected_active_record/sample/Gemfile new file mode 100644 index 0000000000..9fb20af8a0 --- /dev/null +++ b/contrib/connected_active_record/sample/Gemfile @@ -0,0 +1,30 @@ +source 'https://rubygems.org' +git_source(:github) { |repo| "https://github.com/#{repo}.git" } + +ruby '2.6.6' + +# Bundle edge Rails instead: gem 'rails', github: 'rails/rails' +gem 'rails', '~> 6.0.3', '>= 6.0.3.1' +# Use sqlite3 as the database for Active Record +gem 'sqlite3', '~> 1.4' +# Use Puma as the app server +gem 'puma', '~> 4.1' +# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder +gem 'jbuilder', '~> 2.7' +# Use Active Model has_secure_password +# gem 'bcrypt', '~> 3.1.7' + +group :development, :test do + # Call 'byebug' anywhere in the code to stop execution and get a debugger console + gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] +end + +group :development do + # Access an interactive console on exception pages or by calling 'console' anywhere in the code. + gem 'web-console', '>= 3.3.0' +end + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] + +gem 'rails_event_store', '~> 1.0.0' \ No newline at end of file diff --git a/contrib/connected_active_record/sample/README.md b/contrib/connected_active_record/sample/README.md new file mode 100644 index 0000000000..7db80e4ca1 --- /dev/null +++ b/contrib/connected_active_record/sample/README.md @@ -0,0 +1,24 @@ +# README + +This README would normally document whatever steps are necessary to get the +application up and running. + +Things you may want to cover: + +* Ruby version + +* System dependencies + +* Configuration + +* Database creation + +* Database initialization + +* How to run the test suite + +* Services (job queues, cache servers, search engines, etc.) + +* Deployment instructions + +* ... diff --git a/contrib/connected_active_record/sample/Rakefile b/contrib/connected_active_record/sample/Rakefile new file mode 100644 index 0000000000..e85f913914 --- /dev/null +++ b/contrib/connected_active_record/sample/Rakefile @@ -0,0 +1,6 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require_relative 'config/application' + +Rails.application.load_tasks diff --git a/contrib/connected_active_record/sample/app/assets/config/manifest.js b/contrib/connected_active_record/sample/app/assets/config/manifest.js new file mode 100644 index 0000000000..591819335f --- /dev/null +++ b/contrib/connected_active_record/sample/app/assets/config/manifest.js @@ -0,0 +1,2 @@ +//= link_tree ../images +//= link_directory ../stylesheets .css diff --git a/contrib/connected_active_record/sample/app/assets/stylesheets/application.css b/contrib/connected_active_record/sample/app/assets/stylesheets/application.css new file mode 100644 index 0000000000..d05ea0f511 --- /dev/null +++ b/contrib/connected_active_record/sample/app/assets/stylesheets/application.css @@ -0,0 +1,15 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's + * vendor/assets/stylesheets directory can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS + * files in this directory. Styles in this file should be added after the last require_* statement. + * It is generally better to create a new file per style scope. + * + *= require_tree . + *= require_self + */ diff --git a/contrib/connected_active_record/sample/app/controllers/application_controller.rb b/contrib/connected_active_record/sample/app/controllers/application_controller.rb new file mode 100644 index 0000000000..09705d12ab --- /dev/null +++ b/contrib/connected_active_record/sample/app/controllers/application_controller.rb @@ -0,0 +1,2 @@ +class ApplicationController < ActionController::Base +end diff --git a/contrib/connected_active_record/sample/app/helpers/application_helper.rb b/contrib/connected_active_record/sample/app/helpers/application_helper.rb new file mode 100644 index 0000000000..de6be7945c --- /dev/null +++ b/contrib/connected_active_record/sample/app/helpers/application_helper.rb @@ -0,0 +1,2 @@ +module ApplicationHelper +end diff --git a/contrib/connected_active_record/sample/app/jobs/application_job.rb b/contrib/connected_active_record/sample/app/jobs/application_job.rb new file mode 100644 index 0000000000..d394c3d106 --- /dev/null +++ b/contrib/connected_active_record/sample/app/jobs/application_job.rb @@ -0,0 +1,7 @@ +class ApplicationJob < ActiveJob::Base + # Automatically retry jobs that encountered a deadlock + # retry_on ActiveRecord::Deadlocked + + # Most jobs are safe to ignore if the underlying records are no longer available + # discard_on ActiveJob::DeserializationError +end diff --git a/contrib/connected_active_record/sample/app/models/application_record.rb b/contrib/connected_active_record/sample/app/models/application_record.rb new file mode 100644 index 0000000000..10a4cba84d --- /dev/null +++ b/contrib/connected_active_record/sample/app/models/application_record.rb @@ -0,0 +1,3 @@ +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end diff --git a/contrib/connected_active_record/sample/app/views/layouts/application.html.erb b/contrib/connected_active_record/sample/app/views/layouts/application.html.erb new file mode 100644 index 0000000000..f06473dab7 --- /dev/null +++ b/contrib/connected_active_record/sample/app/views/layouts/application.html.erb @@ -0,0 +1,14 @@ + + + + Sample + <%= csrf_meta_tags %> + <%= csp_meta_tag %> + + <%= stylesheet_link_tag 'application', media: 'all' %> + + + + <%= yield %> + + diff --git a/contrib/connected_active_record/sample/config.ru b/contrib/connected_active_record/sample/config.ru new file mode 100644 index 0000000000..f7ba0b527b --- /dev/null +++ b/contrib/connected_active_record/sample/config.ru @@ -0,0 +1,5 @@ +# This file is used by Rack-based servers to start the application. + +require_relative 'config/environment' + +run Rails.application diff --git a/contrib/connected_active_record/sample/config/application.rb b/contrib/connected_active_record/sample/config/application.rb new file mode 100644 index 0000000000..9628d5486a --- /dev/null +++ b/contrib/connected_active_record/sample/config/application.rb @@ -0,0 +1,35 @@ +require_relative 'boot' + +require "rails" +# Pick the frameworks you want: +require "active_model/railtie" +require "active_job/railtie" +require "active_record/railtie" +# require "active_storage/engine" +require "action_controller/railtie" +# require "action_mailer/railtie" +# require "action_mailbox/engine" +# require "action_text/engine" +require "action_view/railtie" +# require "action_cable/engine" +# require "sprockets/railtie" +# require "rails/test_unit/railtie" + +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(*Rails.groups) + +module Sample + class Application < Rails::Application + # Initialize configuration defaults for originally generated Rails version. + config.load_defaults 6.0 + + # Settings in config/environments/* take precedence over those specified here. + # Application configuration can go into files in config/initializers + # -- all .rb files in that directory are automatically loaded after loading + # the framework and any gems in your application. + + # Don't generate system test files. + config.generators.system_tests = nil + end +end diff --git a/contrib/connected_active_record/sample/config/boot.rb b/contrib/connected_active_record/sample/config/boot.rb new file mode 100644 index 0000000000..30f5120df6 --- /dev/null +++ b/contrib/connected_active_record/sample/config/boot.rb @@ -0,0 +1,3 @@ +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) + +require 'bundler/setup' # Set up gems listed in the Gemfile. diff --git a/contrib/connected_active_record/sample/config/credentials.yml.enc b/contrib/connected_active_record/sample/config/credentials.yml.enc new file mode 100644 index 0000000000..7a43605f93 --- /dev/null +++ b/contrib/connected_active_record/sample/config/credentials.yml.enc @@ -0,0 +1 @@ +d1WzgYIe3R/fpHfTZfMP5wrp9uDbDMQc2SBiBqg5sqbHZKnTNktELx1YrV9xCcJqUa/V7RBEvsfXBAuu/5luTXkK/sKKL81mlcNuy57i09y7LjQ+ojRB6aWpTcquXM8RSDimSmjKQd9QKDNLxRKGK4wHhBWDa4+4urG2n72+6iVH4JvycVFv5Wgzkhe+su22rraZOKg7LLE9Nvrwrwf1Is1mDHJdxTZqnS4JzWljGbCGFO5AXhyEPp/zHJ/B1zooCcEeO/TUVpxU9OB1bjhIslKY0S+JFPsheY56eYv6WOEpt9EZAPNe4ZEj71H131kVZyD9SZajq+bUCJePJuKjMRjL2w/pkrKXXi9yz4W4bDsKvM2Oz4CNOtXePYe9G+YauExNYEO79oghgxz1UFUQEy1WGZfi/pj6I2mj--BY9vm19PlrY42f7d--DMlEvIHGiG+9KuB6ArAYkA== \ No newline at end of file diff --git a/contrib/connected_active_record/sample/config/database.yml b/contrib/connected_active_record/sample/config/database.yml new file mode 100644 index 0000000000..4a8a1b26fe --- /dev/null +++ b/contrib/connected_active_record/sample/config/database.yml @@ -0,0 +1,25 @@ +# SQLite. Versions 3.8.0 and up are supported. +# gem install sqlite3 +# +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem 'sqlite3' +# +default: &default + adapter: sqlite3 + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + timeout: 5000 + +development: + <<: *default + database: db/development.sqlite3 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: db/test.sqlite3 + +production: + <<: *default + database: db/production.sqlite3 diff --git a/contrib/connected_active_record/sample/config/environment.rb b/contrib/connected_active_record/sample/config/environment.rb new file mode 100644 index 0000000000..426333bb46 --- /dev/null +++ b/contrib/connected_active_record/sample/config/environment.rb @@ -0,0 +1,5 @@ +# Load the Rails application. +require_relative 'application' + +# Initialize the Rails application. +Rails.application.initialize! diff --git a/contrib/connected_active_record/sample/config/environments/development.rb b/contrib/connected_active_record/sample/config/environments/development.rb new file mode 100644 index 0000000000..8ea13e444a --- /dev/null +++ b/contrib/connected_active_record/sample/config/environments/development.rb @@ -0,0 +1,47 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # In the development environment your application's code is reloaded on + # every request. This slows down response time but is perfect for development + # since you don't have to restart the web server when you make code changes. + config.cache_classes = false + + # Do not eager load code on boot. + config.eager_load = false + + # Show full error reports. + config.consider_all_requests_local = true + + # Enable/disable caching. By default caching is disabled. + # Run rails dev:cache to toggle caching. + if Rails.root.join('tmp', 'caching-dev.txt').exist? + config.action_controller.perform_caching = true + config.action_controller.enable_fragment_cache_logging = true + + config.cache_store = :memory_store + config.public_file_server.headers = { + 'Cache-Control' => "public, max-age=#{2.days.to_i}" + } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end + + # Print deprecation notices to the Rails logger. + config.active_support.deprecation = :log + + # Raise an error on page load if there are pending migrations. + config.active_record.migration_error = :page_load + + # Highlight code that triggered database queries in logs. + config.active_record.verbose_query_logs = true + + + # Raises error for missing translations. + # config.action_view.raise_on_missing_translations = true + + # Use an evented file watcher to asynchronously detect changes in source code, + # routes, locales, etc. This feature depends on the listen gem. + # config.file_watcher = ActiveSupport::EventedFileUpdateChecker +end diff --git a/contrib/connected_active_record/sample/config/environments/production.rb b/contrib/connected_active_record/sample/config/environments/production.rb new file mode 100644 index 0000000000..3af1e10966 --- /dev/null +++ b/contrib/connected_active_record/sample/config/environments/production.rb @@ -0,0 +1,92 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.cache_classes = true + + # Eager load code on boot. This eager loads most of Rails and + # your application in memory, allowing both threaded web servers + # and those relying on copy on write to perform better. + # Rake tasks automatically ignore this option for performance. + config.eager_load = true + + # Full error reports are disabled and caching is turned on. + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] + # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). + # config.require_master_key = true + + # Disable serving static files from the `/public` folder by default since + # Apache or NGINX already handles this. + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.action_controller.asset_host = 'http://assets.example.com' + + # Specifies the header that your server uses for sending files. + # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache + # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + # config.force_ssl = true + + # Use the lowest log level to ensure availability of diagnostic information + # when problems arise. + config.log_level = :debug + + # Prepend all log lines with the following tags. + config.log_tags = [ :request_id ] + + # Use a different cache store in production. + # config.cache_store = :mem_cache_store + + # Use a real queuing backend for Active Job (and separate queues per environment). + # config.active_job.queue_adapter = :resque + # config.active_job.queue_name_prefix = "sample_production" + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true + + # Send deprecation notices to registered listeners. + config.active_support.deprecation = :notify + + # Use default logging formatter so that PID and timestamp are not suppressed. + config.log_formatter = ::Logger::Formatter.new + + # Use a different logger for distributed setups. + # require 'syslog/logger' + # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') + + if ENV["RAILS_LOG_TO_STDOUT"].present? + logger = ActiveSupport::Logger.new(STDOUT) + logger.formatter = config.log_formatter + config.logger = ActiveSupport::TaggedLogging.new(logger) + end + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false + + # Inserts middleware to perform automatic connection switching. + # The `database_selector` hash is used to pass options to the DatabaseSelector + # middleware. The `delay` is used to determine how long to wait after a write + # to send a subsequent read to the primary. + # + # The `database_resolver` class is used by the middleware to determine which + # database is appropriate to use based on the time delay. + # + # The `database_resolver_context` class is used by the middleware to set + # timestamps for the last write to the primary. The resolver uses the context + # class timestamps to determine how long to wait before reading from the + # replica. + # + # By default Rails will store a last write timestamp in the session. The + # DatabaseSelector middleware is designed as such you can define your own + # strategy for connection switching and pass that into the middleware through + # these configuration options. + # config.active_record.database_selector = { delay: 2.seconds } + # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver + # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session +end diff --git a/contrib/connected_active_record/sample/config/environments/test.rb b/contrib/connected_active_record/sample/config/environments/test.rb new file mode 100644 index 0000000000..06aef36da8 --- /dev/null +++ b/contrib/connected_active_record/sample/config/environments/test.rb @@ -0,0 +1,38 @@ +# The test environment is used exclusively to run your application's +# test suite. You never need to work with it otherwise. Remember that +# your test database is "scratch space" for the test suite and is wiped +# and recreated between test runs. Don't rely on the data there! + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + config.cache_classes = true + + # Do not eager load code on boot. This avoids loading your whole application + # just for the purpose of running a single test. If you are using a tool that + # preloads Rails for running tests, you may have to set it to true. + config.eager_load = false + + # Configure public file server for tests with Cache-Control for performance. + config.public_file_server.enabled = true + config.public_file_server.headers = { + 'Cache-Control' => "public, max-age=#{1.hour.to_i}" + } + + # Show full error reports and disable caching. + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + config.cache_store = :null_store + + # Raise exceptions instead of rendering exception templates. + config.action_dispatch.show_exceptions = false + + # Disable request forgery protection in test environment. + config.action_controller.allow_forgery_protection = false + + # Print deprecation notices to the stderr. + config.active_support.deprecation = :stderr + + # Raises error for missing translations. + # config.action_view.raise_on_missing_translations = true +end diff --git a/contrib/connected_active_record/sample/config/initializers/application_controller_renderer.rb b/contrib/connected_active_record/sample/config/initializers/application_controller_renderer.rb new file mode 100644 index 0000000000..89d2efab2b --- /dev/null +++ b/contrib/connected_active_record/sample/config/initializers/application_controller_renderer.rb @@ -0,0 +1,8 @@ +# Be sure to restart your server when you modify this file. + +# ActiveSupport::Reloader.to_prepare do +# ApplicationController.renderer.defaults.merge!( +# http_host: 'example.org', +# https: false +# ) +# end diff --git a/contrib/connected_active_record/sample/config/initializers/backtrace_silencers.rb b/contrib/connected_active_record/sample/config/initializers/backtrace_silencers.rb new file mode 100644 index 0000000000..59385cdf37 --- /dev/null +++ b/contrib/connected_active_record/sample/config/initializers/backtrace_silencers.rb @@ -0,0 +1,7 @@ +# Be sure to restart your server when you modify this file. + +# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. +# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } + +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. +# Rails.backtrace_cleaner.remove_silencers! diff --git a/contrib/connected_active_record/sample/config/initializers/content_security_policy.rb b/contrib/connected_active_record/sample/config/initializers/content_security_policy.rb new file mode 100644 index 0000000000..41c43016f1 --- /dev/null +++ b/contrib/connected_active_record/sample/config/initializers/content_security_policy.rb @@ -0,0 +1,28 @@ +# Be sure to restart your server when you modify this file. + +# Define an application-wide content security policy +# For further information see the following documentation +# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy + +# Rails.application.config.content_security_policy do |policy| +# policy.default_src :self, :https +# policy.font_src :self, :https, :data +# policy.img_src :self, :https, :data +# policy.object_src :none +# policy.script_src :self, :https +# policy.style_src :self, :https + +# # Specify URI for violation reports +# # policy.report_uri "/csp-violation-report-endpoint" +# end + +# If you are using UJS then enable automatic nonce generation +# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } + +# Set the nonce only to specific directives +# Rails.application.config.content_security_policy_nonce_directives = %w(script-src) + +# Report CSP violations to a specified URI +# For further information see the following documentation: +# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only +# Rails.application.config.content_security_policy_report_only = true diff --git a/contrib/connected_active_record/sample/config/initializers/cookies_serializer.rb b/contrib/connected_active_record/sample/config/initializers/cookies_serializer.rb new file mode 100644 index 0000000000..5a6a32d371 --- /dev/null +++ b/contrib/connected_active_record/sample/config/initializers/cookies_serializer.rb @@ -0,0 +1,5 @@ +# Be sure to restart your server when you modify this file. + +# Specify a serializer for the signed and encrypted cookie jars. +# Valid options are :json, :marshal, and :hybrid. +Rails.application.config.action_dispatch.cookies_serializer = :json diff --git a/contrib/connected_active_record/sample/config/initializers/filter_parameter_logging.rb b/contrib/connected_active_record/sample/config/initializers/filter_parameter_logging.rb new file mode 100644 index 0000000000..4a994e1e7b --- /dev/null +++ b/contrib/connected_active_record/sample/config/initializers/filter_parameter_logging.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Configure sensitive parameters which will be filtered from the log file. +Rails.application.config.filter_parameters += [:password] diff --git a/contrib/connected_active_record/sample/config/initializers/inflections.rb b/contrib/connected_active_record/sample/config/initializers/inflections.rb new file mode 100644 index 0000000000..ac033bf9dc --- /dev/null +++ b/contrib/connected_active_record/sample/config/initializers/inflections.rb @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format. Inflections +# are locale specific, and you may define rules for as many different +# locales as you wish. All of these examples are active by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.plural /^(ox)$/i, '\1en' +# inflect.singular /^(ox)en/i, '\1' +# inflect.irregular 'person', 'people' +# inflect.uncountable %w( fish sheep ) +# end + +# These inflection rules are supported but not enabled by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.acronym 'RESTful' +# end diff --git a/contrib/connected_active_record/sample/config/initializers/mime_types.rb b/contrib/connected_active_record/sample/config/initializers/mime_types.rb new file mode 100644 index 0000000000..dc1899682b --- /dev/null +++ b/contrib/connected_active_record/sample/config/initializers/mime_types.rb @@ -0,0 +1,4 @@ +# Be sure to restart your server when you modify this file. + +# Add new mime types for use in respond_to blocks: +# Mime::Type.register "text/richtext", :rtf diff --git a/contrib/connected_active_record/sample/config/initializers/rails_event_store.rb b/contrib/connected_active_record/sample/config/initializers/rails_event_store.rb new file mode 100644 index 0000000000..054f64c857 --- /dev/null +++ b/contrib/connected_active_record/sample/config/initializers/rails_event_store.rb @@ -0,0 +1,25 @@ +require 'rails_event_store' +require 'aggregate_root' +require 'arkency/command_bus' + +Rails.configuration.to_prepare do + Rails.configuration.event_store = RailsEventStore::Client.new + Rails.configuration.command_bus = Arkency::CommandBus.new + + AggregateRoot.configure do |config| + config.default_event_store = Rails.configuration.event_store + end + + # Subscribe event handlers below + # Rails.configuration.event_store.tap do |store| + # store.subscribe(InvoiceReadModel.new, to: [InvoicePrinted]) + # store.subscribe(->(event) { SendOrderConfirmation.new.call(event) }, to: [OrderSubmitted]) + # store.subscribe_to_all_events(->(event) { Rails.logger.info(event.type) }) + # end + + # Register command handlers below + # Rails.configuration.command_bus.tap do |bus| + # bus.register(PrintInvoice, Invoicing::OnPrint.new) + # bus.register(SubmitOrder, ->(cmd) { Ordering::OnSubmitOrder.new.call(cmd) }) + # end +end diff --git a/contrib/connected_active_record/sample/config/initializers/wrap_parameters.rb b/contrib/connected_active_record/sample/config/initializers/wrap_parameters.rb new file mode 100644 index 0000000000..bbfc3961bf --- /dev/null +++ b/contrib/connected_active_record/sample/config/initializers/wrap_parameters.rb @@ -0,0 +1,14 @@ +# Be sure to restart your server when you modify this file. + +# This file contains settings for ActionController::ParamsWrapper which +# is enabled by default. + +# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. +ActiveSupport.on_load(:action_controller) do + wrap_parameters format: [:json] +end + +# To enable root element in JSON for ActiveRecord objects. +# ActiveSupport.on_load(:active_record) do +# self.include_root_in_json = true +# end diff --git a/contrib/connected_active_record/sample/config/locales/en.yml b/contrib/connected_active_record/sample/config/locales/en.yml new file mode 100644 index 0000000000..cf9b342d0a --- /dev/null +++ b/contrib/connected_active_record/sample/config/locales/en.yml @@ -0,0 +1,33 @@ +# Files in the config/locales directory are used for internationalization +# and are automatically loaded by Rails. If you want to use locales other +# than English, add the necessary files in this directory. +# +# To use the locales, use `I18n.t`: +# +# I18n.t 'hello' +# +# In views, this is aliased to just `t`: +# +# <%= t('hello') %> +# +# To use a different locale, set it with `I18n.locale`: +# +# I18n.locale = :es +# +# This would use the information in config/locales/es.yml. +# +# The following keys must be escaped otherwise they will not be retrieved by +# the default I18n backend: +# +# true, false, on, off, yes, no +# +# Instead, surround them with single quotes. +# +# en: +# 'true': 'foo' +# +# To learn more, please read the Rails Internationalization guide +# available at https://guides.rubyonrails.org/i18n.html. + +en: + hello: "Hello world" diff --git a/contrib/connected_active_record/sample/config/puma.rb b/contrib/connected_active_record/sample/config/puma.rb new file mode 100644 index 0000000000..5ed4437744 --- /dev/null +++ b/contrib/connected_active_record/sample/config/puma.rb @@ -0,0 +1,38 @@ +# Puma can serve each request in a thread from an internal thread pool. +# The `threads` method setting takes two numbers: a minimum and maximum. +# Any libraries that use thread pools should be configured to match +# the maximum value specified for Puma. Default is set to 5 threads for minimum +# and maximum; this matches the default thread size of Active Record. +# +max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } +min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } +threads min_threads_count, max_threads_count + +# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +# +port ENV.fetch("PORT") { 3000 } + +# Specifies the `environment` that Puma will run in. +# +environment ENV.fetch("RAILS_ENV") { "development" } + +# Specifies the `pidfile` that Puma will use. +pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } + +# Specifies the number of `workers` to boot in clustered mode. +# Workers are forked web server processes. If using threads and workers together +# the concurrency of the application would be max `threads` * `workers`. +# Workers do not work on JRuby or Windows (both of which do not support +# processes). +# +# workers ENV.fetch("WEB_CONCURRENCY") { 2 } + +# Use the `preload_app!` method when specifying a `workers` number. +# This directive tells Puma to first boot the application and load code +# before forking the application. This takes advantage of Copy On Write +# process behavior so workers use less memory. +# +# preload_app! + +# Allow puma to be restarted by `rails restart` command. +plugin :tmp_restart diff --git a/contrib/connected_active_record/sample/config/routes.rb b/contrib/connected_active_record/sample/config/routes.rb new file mode 100644 index 0000000000..96a6bd1396 --- /dev/null +++ b/contrib/connected_active_record/sample/config/routes.rb @@ -0,0 +1,4 @@ +Rails.application.routes.draw do + mount RailsEventStore::Browser => '/res' if Rails.env.development? + # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html +end diff --git a/contrib/connected_active_record/sample/db/migrate/20200604152933_create_event_store_events.rb b/contrib/connected_active_record/sample/db/migrate/20200604152933_create_event_store_events.rb new file mode 100644 index 0000000000..c583aaea5b --- /dev/null +++ b/contrib/connected_active_record/sample/db/migrate/20200604152933_create_event_store_events.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +class CreateEventStoreEvents < ActiveRecord::Migration[4.2] + def change + postgres = ActiveRecord::Base.connection.adapter_name == "PostgreSQL" + sqlite = ActiveRecord::Base.connection.adapter_name == "SQLite" + rails_42 = Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new("5.0.0") + enable_extension "pgcrypto" if postgres + create_table(:event_store_events_in_streams, force: false) do |t| + t.string :stream, null: false + t.integer :position, null: true + if postgres + t.references :event, null: false, type: :uuid + else + t.references :event, null: false, type: :string, limit: 36 + end + t.datetime :created_at, null: false + end + add_index :event_store_events_in_streams, [:stream, :position], unique: true + add_index :event_store_events_in_streams, [:created_at] + add_index :event_store_events_in_streams, [:stream, :event_id], unique: true + + if postgres + create_table(:event_store_events, id: :uuid, default: 'gen_random_uuid()', force: false) do |t| + t.string :event_type, null: false + t.binary :metadata + t.binary :data, null: false + t.datetime :created_at, null: false + end + else + create_table(:event_store_events, id: false, force: false) do |t| + t.string :id, limit: 36, primary_key: true, null: false + t.string :event_type, null: false + t.binary :metadata + t.binary :data, null: false + t.datetime :created_at, null: false + end + if sqlite && rails_42 + add_index :event_store_events, :id, unique: true + end + end + add_index :event_store_events, :created_at + add_index :event_store_events, :event_type + end +end diff --git a/contrib/connected_active_record/sample/db/schema.rb b/contrib/connected_active_record/sample/db/schema.rb new file mode 100644 index 0000000000..01923995c3 --- /dev/null +++ b/contrib/connected_active_record/sample/db/schema.rb @@ -0,0 +1,34 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# This file is the source Rails uses to define your schema when running `rails +# db:schema:load`. When creating a new database, `rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 2020_06_04_152933) do + + create_table "event_store_events", id: :string, limit: 36, force: :cascade do |t| + t.string "event_type", null: false + t.binary "metadata" + t.binary "data", null: false + t.datetime "created_at", null: false + t.index ["created_at"], name: "index_event_store_events_on_created_at" + t.index ["event_type"], name: "index_event_store_events_on_event_type" + end + + create_table "event_store_events_in_streams", force: :cascade do |t| + t.string "stream", null: false + t.integer "position" + t.string "event_id", limit: 36, null: false + t.datetime "created_at", null: false + t.index ["created_at"], name: "index_event_store_events_in_streams_on_created_at" + t.index ["stream", "event_id"], name: "index_event_store_events_in_streams_on_stream_and_event_id", unique: true + t.index ["stream", "position"], name: "index_event_store_events_in_streams_on_stream_and_position", unique: true + end + +end diff --git a/contrib/connected_active_record/sample/db/seeds.rb b/contrib/connected_active_record/sample/db/seeds.rb new file mode 100644 index 0000000000..1beea2accd --- /dev/null +++ b/contrib/connected_active_record/sample/db/seeds.rb @@ -0,0 +1,7 @@ +# This file should contain all the record creation needed to seed the database with its default values. +# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). +# +# Examples: +# +# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) +# Character.create(name: 'Luke', movie: movies.first) diff --git a/contrib/connected_active_record/sample/public/404.html b/contrib/connected_active_record/sample/public/404.html new file mode 100644 index 0000000000..2be3af26fc --- /dev/null +++ b/contrib/connected_active_record/sample/public/404.html @@ -0,0 +1,67 @@ + + + + The page you were looking for doesn't exist (404) + + + + + + +
+
+

The page you were looking for doesn't exist.

+

You may have mistyped the address or the page may have moved.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/contrib/connected_active_record/sample/public/422.html b/contrib/connected_active_record/sample/public/422.html new file mode 100644 index 0000000000..c08eac0d1d --- /dev/null +++ b/contrib/connected_active_record/sample/public/422.html @@ -0,0 +1,67 @@ + + + + The change you wanted was rejected (422) + + + + + + +
+
+

The change you wanted was rejected.

+

Maybe you tried to change something you didn't have access to.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/contrib/connected_active_record/sample/public/500.html b/contrib/connected_active_record/sample/public/500.html new file mode 100644 index 0000000000..78a030af22 --- /dev/null +++ b/contrib/connected_active_record/sample/public/500.html @@ -0,0 +1,66 @@ + + + + We're sorry, but something went wrong (500) + + + + + + +
+
+

We're sorry, but something went wrong.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/contrib/connected_active_record/sample/public/apple-touch-icon-precomposed.png b/contrib/connected_active_record/sample/public/apple-touch-icon-precomposed.png new file mode 100644 index 0000000000..e69de29bb2 diff --git a/contrib/connected_active_record/sample/public/apple-touch-icon.png b/contrib/connected_active_record/sample/public/apple-touch-icon.png new file mode 100644 index 0000000000..e69de29bb2 diff --git a/contrib/connected_active_record/sample/public/favicon.ico b/contrib/connected_active_record/sample/public/favicon.ico new file mode 100644 index 0000000000..e69de29bb2 diff --git a/contrib/connected_active_record/sample/public/robots.txt b/contrib/connected_active_record/sample/public/robots.txt new file mode 100644 index 0000000000..c19f78ab68 --- /dev/null +++ b/contrib/connected_active_record/sample/public/robots.txt @@ -0,0 +1 @@ +# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file From 1e7951d04f13b3f1dc4bb5325df6eb83b83b2e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 4 Jun 2020 15:42:12 +0200 Subject: [PATCH 08/35] Use gems from branch, not released yet --- contrib/connected_active_record/sample/Gemfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/connected_active_record/sample/Gemfile b/contrib/connected_active_record/sample/Gemfile index 9fb20af8a0..be641ea6dd 100644 --- a/contrib/connected_active_record/sample/Gemfile +++ b/contrib/connected_active_record/sample/Gemfile @@ -27,4 +27,5 @@ end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] -gem 'rails_event_store', '~> 1.0.0' \ No newline at end of file +gem 'rails_event_store', path: '../../../rails_event_store' +gem 'connected_active_record', path: '../' From 2753074063f6e83e7180086d296db51526b754d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 4 Jun 2020 15:49:08 +0200 Subject: [PATCH 09/35] Define orders & payments databases & migration folders --- .../sample/config/database.yml | 18 ++++++++ ...0200604152933_create_event_store_events.rb | 45 +++++++++++++++++++ ...0200604152933_create_event_store_events.rb | 45 +++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 contrib/connected_active_record/sample/db/orders/20200604152933_create_event_store_events.rb create mode 100644 contrib/connected_active_record/sample/db/payments/20200604152933_create_event_store_events.rb diff --git a/contrib/connected_active_record/sample/config/database.yml b/contrib/connected_active_record/sample/config/database.yml index 4a8a1b26fe..c05cda35d2 100644 --- a/contrib/connected_active_record/sample/config/database.yml +++ b/contrib/connected_active_record/sample/config/database.yml @@ -12,6 +12,12 @@ default: &default development: <<: *default database: db/development.sqlite3 + orders: + database: db/orders_development.sqlite3 + migrations_paths: db/orders + payments: + database: db/payments_development.sqlite3 + migrations_paths: db/payments # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". @@ -19,7 +25,19 @@ development: test: <<: *default database: db/test.sqlite3 + orders: + database: db/orders_test.sqlite3 + migrations_paths: db/orders + payments: + database: db/payments_test.sqlite3 + migrations_paths: db/payments production: <<: *default database: db/production.sqlite3 + orders: + database: db/orders_production.sqlite3 + migrations_paths: db/orders + payments: + database: db/payments_production.sqlite3 + migrations_paths: db/payments diff --git a/contrib/connected_active_record/sample/db/orders/20200604152933_create_event_store_events.rb b/contrib/connected_active_record/sample/db/orders/20200604152933_create_event_store_events.rb new file mode 100644 index 0000000000..c583aaea5b --- /dev/null +++ b/contrib/connected_active_record/sample/db/orders/20200604152933_create_event_store_events.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +class CreateEventStoreEvents < ActiveRecord::Migration[4.2] + def change + postgres = ActiveRecord::Base.connection.adapter_name == "PostgreSQL" + sqlite = ActiveRecord::Base.connection.adapter_name == "SQLite" + rails_42 = Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new("5.0.0") + enable_extension "pgcrypto" if postgres + create_table(:event_store_events_in_streams, force: false) do |t| + t.string :stream, null: false + t.integer :position, null: true + if postgres + t.references :event, null: false, type: :uuid + else + t.references :event, null: false, type: :string, limit: 36 + end + t.datetime :created_at, null: false + end + add_index :event_store_events_in_streams, [:stream, :position], unique: true + add_index :event_store_events_in_streams, [:created_at] + add_index :event_store_events_in_streams, [:stream, :event_id], unique: true + + if postgres + create_table(:event_store_events, id: :uuid, default: 'gen_random_uuid()', force: false) do |t| + t.string :event_type, null: false + t.binary :metadata + t.binary :data, null: false + t.datetime :created_at, null: false + end + else + create_table(:event_store_events, id: false, force: false) do |t| + t.string :id, limit: 36, primary_key: true, null: false + t.string :event_type, null: false + t.binary :metadata + t.binary :data, null: false + t.datetime :created_at, null: false + end + if sqlite && rails_42 + add_index :event_store_events, :id, unique: true + end + end + add_index :event_store_events, :created_at + add_index :event_store_events, :event_type + end +end diff --git a/contrib/connected_active_record/sample/db/payments/20200604152933_create_event_store_events.rb b/contrib/connected_active_record/sample/db/payments/20200604152933_create_event_store_events.rb new file mode 100644 index 0000000000..c583aaea5b --- /dev/null +++ b/contrib/connected_active_record/sample/db/payments/20200604152933_create_event_store_events.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +class CreateEventStoreEvents < ActiveRecord::Migration[4.2] + def change + postgres = ActiveRecord::Base.connection.adapter_name == "PostgreSQL" + sqlite = ActiveRecord::Base.connection.adapter_name == "SQLite" + rails_42 = Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new("5.0.0") + enable_extension "pgcrypto" if postgres + create_table(:event_store_events_in_streams, force: false) do |t| + t.string :stream, null: false + t.integer :position, null: true + if postgres + t.references :event, null: false, type: :uuid + else + t.references :event, null: false, type: :string, limit: 36 + end + t.datetime :created_at, null: false + end + add_index :event_store_events_in_streams, [:stream, :position], unique: true + add_index :event_store_events_in_streams, [:created_at] + add_index :event_store_events_in_streams, [:stream, :event_id], unique: true + + if postgres + create_table(:event_store_events, id: :uuid, default: 'gen_random_uuid()', force: false) do |t| + t.string :event_type, null: false + t.binary :metadata + t.binary :data, null: false + t.datetime :created_at, null: false + end + else + create_table(:event_store_events, id: false, force: false) do |t| + t.string :id, limit: 36, primary_key: true, null: false + t.string :event_type, null: false + t.binary :metadata + t.binary :data, null: false + t.datetime :created_at, null: false + end + if sqlite && rails_42 + add_index :event_store_events, :id, unique: true + end + end + add_index :event_store_events, :created_at + add_index :event_store_events, :event_type + end +end From 984db18bc683218b13d1783fe315ccce6365b694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 4 Jun 2020 15:53:34 +0200 Subject: [PATCH 10/35] Setup Rails to use RSpec for tests --- contrib/connected_active_record/sample/.rspec | 1 + .../connected_active_record/sample/Gemfile | 2 + .../sample/spec/rails_helper.rb | 64 +++++++++++++ .../sample/spec/spec_helper.rb | 96 +++++++++++++++++++ 4 files changed, 163 insertions(+) create mode 100644 contrib/connected_active_record/sample/.rspec create mode 100644 contrib/connected_active_record/sample/spec/rails_helper.rb create mode 100644 contrib/connected_active_record/sample/spec/spec_helper.rb diff --git a/contrib/connected_active_record/sample/.rspec b/contrib/connected_active_record/sample/.rspec new file mode 100644 index 0000000000..c99d2e7396 --- /dev/null +++ b/contrib/connected_active_record/sample/.rspec @@ -0,0 +1 @@ +--require spec_helper diff --git a/contrib/connected_active_record/sample/Gemfile b/contrib/connected_active_record/sample/Gemfile index be641ea6dd..89c7aa55c2 100644 --- a/contrib/connected_active_record/sample/Gemfile +++ b/contrib/connected_active_record/sample/Gemfile @@ -17,6 +17,8 @@ gem 'jbuilder', '~> 2.7' group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] + gem 'rspec-rails' + gem 'database_cleaner' end group :development do diff --git a/contrib/connected_active_record/sample/spec/rails_helper.rb b/contrib/connected_active_record/sample/spec/rails_helper.rb new file mode 100644 index 0000000000..00345af7c0 --- /dev/null +++ b/contrib/connected_active_record/sample/spec/rails_helper.rb @@ -0,0 +1,64 @@ +# This file is copied to spec/ when you run 'rails generate rspec:install' +require 'spec_helper' +ENV['RAILS_ENV'] ||= 'test' +require File.expand_path('../config/environment', __dir__) +# Prevent database truncation if the environment is production +abort("The Rails environment is running in production mode!") if Rails.env.production? +require 'rspec/rails' +# Add additional requires below this line. Rails is not loaded until this point! + +# Requires supporting ruby files with custom matchers and macros, etc, in +# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are +# run as spec files by default. This means that files in spec/support that end +# in _spec.rb will both be required and run as specs, causing the specs to be +# run twice. It is recommended that you do not name files matching this glob to +# end with _spec.rb. You can configure this pattern with the --pattern +# option on the command line or in ~/.rspec, .rspec or `.rspec-local`. +# +# The following line is provided for convenience purposes. It has the downside +# of increasing the boot-up time by auto-requiring all files in the support +# directory. Alternatively, in the individual `*_spec.rb` files, manually +# require only the support files necessary. +# +# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } + +# Checks for pending migrations and applies them before tests are run. +# If you are not using ActiveRecord, you can remove these lines. +begin + ActiveRecord::Migration.maintain_test_schema! +rescue ActiveRecord::PendingMigrationError => e + puts e.to_s.strip + exit 1 +end +RSpec.configure do |config| + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = "#{::Rails.root}/spec/fixtures" + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = true + + # You can uncomment this line to turn off ActiveRecord support entirely. + # config.use_active_record = false + + # RSpec Rails can automatically mix in different behaviours to your tests + # based on their file location, for example enabling you to call `get` and + # `post` in specs under `spec/controllers`. + # + # You can disable this behaviour by removing the line below, and instead + # explicitly tag your specs with their type, e.g.: + # + # RSpec.describe UsersController, type: :controller do + # # ... + # end + # + # The different available types are documented in the features, such as in + # https://relishapp.com/rspec/rspec-rails/docs + config.infer_spec_type_from_file_location! + + # Filter lines from Rails gems in backtraces. + config.filter_rails_from_backtrace! + # arbitrary gems may also be filtered via: + # config.filter_gems_from_backtrace("gem name") +end diff --git a/contrib/connected_active_record/sample/spec/spec_helper.rb b/contrib/connected_active_record/sample/spec/spec_helper.rb new file mode 100644 index 0000000000..ce33d66df6 --- /dev/null +++ b/contrib/connected_active_record/sample/spec/spec_helper.rb @@ -0,0 +1,96 @@ +# This file was generated by the `rails generate rspec:install` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + config.disable_monkey_patching! + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = "doc" + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end +end From b8392fb5782fc3953c82d86eaac4a040d0736eda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 4 Jun 2020 15:54:30 +0200 Subject: [PATCH 11/35] Generate empty Orders & Payments BCs --- .../sample/orders/spec/orders_spec.rb | 4 ++++ .../sample/orders/spec/spec_helper.rb | 7 +++++++ .../sample/payments/spec/payments_spec.rb | 4 ++++ .../sample/payments/spec/spec_helper.rb | 7 +++++++ contrib/connected_active_record/sample/spec/orders_spec.rb | 6 ++++++ .../connected_active_record/sample/spec/payments_spec.rb | 6 ++++++ 6 files changed, 34 insertions(+) create mode 100644 contrib/connected_active_record/sample/orders/spec/orders_spec.rb create mode 100644 contrib/connected_active_record/sample/orders/spec/spec_helper.rb create mode 100644 contrib/connected_active_record/sample/payments/spec/payments_spec.rb create mode 100644 contrib/connected_active_record/sample/payments/spec/spec_helper.rb create mode 100644 contrib/connected_active_record/sample/spec/orders_spec.rb create mode 100644 contrib/connected_active_record/sample/spec/payments_spec.rb diff --git a/contrib/connected_active_record/sample/orders/spec/orders_spec.rb b/contrib/connected_active_record/sample/orders/spec/orders_spec.rb new file mode 100644 index 0000000000..56ec0ef181 --- /dev/null +++ b/contrib/connected_active_record/sample/orders/spec/orders_spec.rb @@ -0,0 +1,4 @@ +require_relative 'spec_helper' + +RSpec.describe Orders do +end diff --git a/contrib/connected_active_record/sample/orders/spec/spec_helper.rb b/contrib/connected_active_record/sample/orders/spec/spec_helper.rb new file mode 100644 index 0000000000..bb7f0256b5 --- /dev/null +++ b/contrib/connected_active_record/sample/orders/spec/spec_helper.rb @@ -0,0 +1,7 @@ +ENV['RAILS_ENV'] = 'test' + +$LOAD_PATH.push File.expand_path('../../../spec', __FILE__) +require File.expand_path('../../../config/environment', __FILE__) +require File.expand_path('../../../spec/rails_helper', __FILE__) + +require_relative '../lib/orders' diff --git a/contrib/connected_active_record/sample/payments/spec/payments_spec.rb b/contrib/connected_active_record/sample/payments/spec/payments_spec.rb new file mode 100644 index 0000000000..4963dd47bf --- /dev/null +++ b/contrib/connected_active_record/sample/payments/spec/payments_spec.rb @@ -0,0 +1,4 @@ +require_relative 'spec_helper' + +RSpec.describe Payments do +end diff --git a/contrib/connected_active_record/sample/payments/spec/spec_helper.rb b/contrib/connected_active_record/sample/payments/spec/spec_helper.rb new file mode 100644 index 0000000000..b50e3a2b33 --- /dev/null +++ b/contrib/connected_active_record/sample/payments/spec/spec_helper.rb @@ -0,0 +1,7 @@ +ENV['RAILS_ENV'] = 'test' + +$LOAD_PATH.push File.expand_path('../../../spec', __FILE__) +require File.expand_path('../../../config/environment', __FILE__) +require File.expand_path('../../../spec/rails_helper', __FILE__) + +require_relative '../lib/payments' diff --git a/contrib/connected_active_record/sample/spec/orders_spec.rb b/contrib/connected_active_record/sample/spec/orders_spec.rb new file mode 100644 index 0000000000..752c4215fe --- /dev/null +++ b/contrib/connected_active_record/sample/spec/orders_spec.rb @@ -0,0 +1,6 @@ +require 'rails_helper' + +path = Rails.root.join('orders/spec') +Dir.glob("#{path}/**/*_spec.rb") do |file| + require file +end diff --git a/contrib/connected_active_record/sample/spec/payments_spec.rb b/contrib/connected_active_record/sample/spec/payments_spec.rb new file mode 100644 index 0000000000..095a3c45cd --- /dev/null +++ b/contrib/connected_active_record/sample/spec/payments_spec.rb @@ -0,0 +1,6 @@ +require 'rails_helper' + +path = Rails.root.join('payments/spec') +Dir.glob("#{path}/**/*_spec.rb") do |file| + require file +end From 7f78da0e1bc860c8df7971325acf1e856cbcc640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 4 Jun 2020 16:47:34 +0200 Subject: [PATCH 12/35] Add load path for BCs --- contrib/connected_active_record/sample/config/application.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/connected_active_record/sample/config/application.rb b/contrib/connected_active_record/sample/config/application.rb index 9628d5486a..f9568e2f6e 100644 --- a/contrib/connected_active_record/sample/config/application.rb +++ b/contrib/connected_active_record/sample/config/application.rb @@ -28,6 +28,8 @@ class Application < Rails::Application # Application configuration can go into files in config/initializers # -- all .rb files in that directory are automatically loaded after loading # the framework and any gems in your application. + config.paths.add 'orders/lib', eager_load: true + config.paths.add 'payments/lib', eager_load: true # Don't generate system test files. config.generators.system_tests = nil From 3612634a0b47268873b2bbc4c22dba0a9ea80155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 4 Jun 2020 16:48:50 +0200 Subject: [PATCH 13/35] Correct format of database.yml --- .../sample/config/database.yml | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/contrib/connected_active_record/sample/config/database.yml b/contrib/connected_active_record/sample/config/database.yml index c05cda35d2..6d8d08aa4e 100644 --- a/contrib/connected_active_record/sample/config/database.yml +++ b/contrib/connected_active_record/sample/config/database.yml @@ -10,12 +10,15 @@ default: &default timeout: 5000 development: - <<: *default - database: db/development.sqlite3 + primary: + <<: *default + database: db/development.sqlite3 orders: + <<: *default database: db/orders_development.sqlite3 migrations_paths: db/orders payments: + <<: *default database: db/payments_development.sqlite3 migrations_paths: db/payments @@ -23,21 +26,27 @@ development: # re-generated from your development database when you run "rake". # Do not set this db to the same as development or production. test: - <<: *default - database: db/test.sqlite3 + primary: + <<: *default + database: db/test.sqlite3 orders: + <<: *default database: db/orders_test.sqlite3 migrations_paths: db/orders payments: + <<: *default database: db/payments_test.sqlite3 migrations_paths: db/payments production: - <<: *default - database: db/production.sqlite3 + primary: + <<: *default + database: db/production.sqlite3 orders: + <<: *default database: db/orders_production.sqlite3 migrations_paths: db/orders payments: + <<: *default database: db/payments_production.sqlite3 migrations_paths: db/payments From c8da3ebda4de5f74dfa4ef9376b95a02744e2edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 4 Jun 2020 16:50:00 +0200 Subject: [PATCH 14/35] Setup orders & payments databases & define base model classes for BCs --- .../sample/db/orders_schema.rb | 34 +++++++++++++++++++ .../sample/db/payments_schema.rb | 34 +++++++++++++++++++ .../sample/orders/lib/orders.rb | 4 +++ .../orders/lib/orders/application_record.rb | 6 ++++ .../sample/payments/lib/payments.rb | 4 +++ .../lib/payments/application_record.rb | 6 ++++ 6 files changed, 88 insertions(+) create mode 100644 contrib/connected_active_record/sample/db/orders_schema.rb create mode 100644 contrib/connected_active_record/sample/db/payments_schema.rb create mode 100644 contrib/connected_active_record/sample/orders/lib/orders.rb create mode 100644 contrib/connected_active_record/sample/orders/lib/orders/application_record.rb create mode 100644 contrib/connected_active_record/sample/payments/lib/payments.rb create mode 100644 contrib/connected_active_record/sample/payments/lib/payments/application_record.rb diff --git a/contrib/connected_active_record/sample/db/orders_schema.rb b/contrib/connected_active_record/sample/db/orders_schema.rb new file mode 100644 index 0000000000..01923995c3 --- /dev/null +++ b/contrib/connected_active_record/sample/db/orders_schema.rb @@ -0,0 +1,34 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# This file is the source Rails uses to define your schema when running `rails +# db:schema:load`. When creating a new database, `rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 2020_06_04_152933) do + + create_table "event_store_events", id: :string, limit: 36, force: :cascade do |t| + t.string "event_type", null: false + t.binary "metadata" + t.binary "data", null: false + t.datetime "created_at", null: false + t.index ["created_at"], name: "index_event_store_events_on_created_at" + t.index ["event_type"], name: "index_event_store_events_on_event_type" + end + + create_table "event_store_events_in_streams", force: :cascade do |t| + t.string "stream", null: false + t.integer "position" + t.string "event_id", limit: 36, null: false + t.datetime "created_at", null: false + t.index ["created_at"], name: "index_event_store_events_in_streams_on_created_at" + t.index ["stream", "event_id"], name: "index_event_store_events_in_streams_on_stream_and_event_id", unique: true + t.index ["stream", "position"], name: "index_event_store_events_in_streams_on_stream_and_position", unique: true + end + +end diff --git a/contrib/connected_active_record/sample/db/payments_schema.rb b/contrib/connected_active_record/sample/db/payments_schema.rb new file mode 100644 index 0000000000..01923995c3 --- /dev/null +++ b/contrib/connected_active_record/sample/db/payments_schema.rb @@ -0,0 +1,34 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# This file is the source Rails uses to define your schema when running `rails +# db:schema:load`. When creating a new database, `rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 2020_06_04_152933) do + + create_table "event_store_events", id: :string, limit: 36, force: :cascade do |t| + t.string "event_type", null: false + t.binary "metadata" + t.binary "data", null: false + t.datetime "created_at", null: false + t.index ["created_at"], name: "index_event_store_events_on_created_at" + t.index ["event_type"], name: "index_event_store_events_on_event_type" + end + + create_table "event_store_events_in_streams", force: :cascade do |t| + t.string "stream", null: false + t.integer "position" + t.string "event_id", limit: 36, null: false + t.datetime "created_at", null: false + t.index ["created_at"], name: "index_event_store_events_in_streams_on_created_at" + t.index ["stream", "event_id"], name: "index_event_store_events_in_streams_on_stream_and_event_id", unique: true + t.index ["stream", "position"], name: "index_event_store_events_in_streams_on_stream_and_position", unique: true + end + +end diff --git a/contrib/connected_active_record/sample/orders/lib/orders.rb b/contrib/connected_active_record/sample/orders/lib/orders.rb new file mode 100644 index 0000000000..bc15755e36 --- /dev/null +++ b/contrib/connected_active_record/sample/orders/lib/orders.rb @@ -0,0 +1,4 @@ +require_relative 'orders/application_record' + +module Orders +end diff --git a/contrib/connected_active_record/sample/orders/lib/orders/application_record.rb b/contrib/connected_active_record/sample/orders/lib/orders/application_record.rb new file mode 100644 index 0000000000..85e510b19e --- /dev/null +++ b/contrib/connected_active_record/sample/orders/lib/orders/application_record.rb @@ -0,0 +1,6 @@ +module Orders + class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true + connects_to database: { writing: :orders, reading: :orders } + end +end diff --git a/contrib/connected_active_record/sample/payments/lib/payments.rb b/contrib/connected_active_record/sample/payments/lib/payments.rb new file mode 100644 index 0000000000..4763dadaef --- /dev/null +++ b/contrib/connected_active_record/sample/payments/lib/payments.rb @@ -0,0 +1,4 @@ +require_relative 'payments/application_record' + +module Payments +end diff --git a/contrib/connected_active_record/sample/payments/lib/payments/application_record.rb b/contrib/connected_active_record/sample/payments/lib/payments/application_record.rb new file mode 100644 index 0000000000..fa51873d0e --- /dev/null +++ b/contrib/connected_active_record/sample/payments/lib/payments/application_record.rb @@ -0,0 +1,6 @@ +module Payments + class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true + connects_to database: { writing: :payments, reading: :payments } + end +end From b2d87156e91a494aaf473df141989fd4a1cbafce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 4 Jun 2020 18:13:08 +0200 Subject: [PATCH 15/35] We do not need connected_active_record gem, just parametrized RESAR EventRepository is enough --- contrib/connected_active_record/sample/Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/connected_active_record/sample/Gemfile b/contrib/connected_active_record/sample/Gemfile index 89c7aa55c2..fd6887f7f9 100644 --- a/contrib/connected_active_record/sample/Gemfile +++ b/contrib/connected_active_record/sample/Gemfile @@ -29,5 +29,5 @@ end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] +gem 'rails_event_store_active_record', path: '../../../rails_event_store_active_record' gem 'rails_event_store', path: '../../../rails_event_store' -gem 'connected_active_record', path: '../' From 4fc1e212c97dc67f49559aa1e318dfb1354ef0ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 4 Jun 2020 18:14:12 +0200 Subject: [PATCH 16/35] Define setup methods to initialize subscriptions & event handlers for modules --- .../config/initializers/rails_event_store.rb | 3 ++ .../sample/orders/lib/orders.rb | 33 +++++++++++++++++++ .../sample/payments/lib/payments.rb | 33 +++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/contrib/connected_active_record/sample/config/initializers/rails_event_store.rb b/contrib/connected_active_record/sample/config/initializers/rails_event_store.rb index 054f64c857..55337518aa 100644 --- a/contrib/connected_active_record/sample/config/initializers/rails_event_store.rb +++ b/contrib/connected_active_record/sample/config/initializers/rails_event_store.rb @@ -22,4 +22,7 @@ # bus.register(PrintInvoice, Invoicing::OnPrint.new) # bus.register(SubmitOrder, ->(cmd) { Ordering::OnSubmitOrder.new.call(cmd) }) # end + + Orders.setup(Rails.configuration) + Payments.setup(Rails.configuration) end diff --git a/contrib/connected_active_record/sample/orders/lib/orders.rb b/contrib/connected_active_record/sample/orders/lib/orders.rb index bc15755e36..d390ca284a 100644 --- a/contrib/connected_active_record/sample/orders/lib/orders.rb +++ b/contrib/connected_active_record/sample/orders/lib/orders.rb @@ -1,4 +1,37 @@ +# frozen_string_literal: true + require_relative 'orders/application_record' module Orders + def self.setup(config) + @@public_event_store = config.event_store + @@module_event_store = RailsEventStore::Client.new( + repository: RailsEventStoreActiveRecord::EventRepository.new( + Orders::ApplicationRecord), + mapper: RubyEventStore::Mappers::Default.new( + serializer: JSON, events_class_remapping: events_class_remapping) + ) + + # Subscribe public event handlers below + # config.event_store.tap do |store| + # store.subscribe(InvoiceReadModel.new, to: [InvoicePrinted]) + # end + + # Register commands handled by this module below + # config.command_bus.tap do |bus| + # bus.register(SubmitOrder, Orders::OnSubmit.new) + # end + end + + def self.public_event_store + @@public_event_store + end + + def self.event_store + @@module_event_store + end + + def self.events_class_remapping + {} + end end diff --git a/contrib/connected_active_record/sample/payments/lib/payments.rb b/contrib/connected_active_record/sample/payments/lib/payments.rb index 4763dadaef..a471f690bb 100644 --- a/contrib/connected_active_record/sample/payments/lib/payments.rb +++ b/contrib/connected_active_record/sample/payments/lib/payments.rb @@ -1,4 +1,37 @@ +# frozen_string_literal: true + require_relative 'payments/application_record' module Payments + def self.setup(config) + @@public_event_store = config.event_store + @@module_event_store = RailsEventStore::Client.new( + repository: RailsEventStoreActiveRecord::EventRepository.new( + Payments::ApplicationRecord), + mapper: RubyEventStore::Mappers::Default.new( + serializer: JSON, events_class_remapping: events_class_remapping) + ) + + # Subscribe public event handlers below + # config.event_store.tap do |store| + # store.subscribe(StartPaymentProcess.new, to: [OrderSubmitted]) + # end + + # Register commands handled by this module below + # config.command_bus.tap do |bus| + # bus.register(SubmitPayment, Payments::OnSubmit.new) + # end + end + + def self.public_event_store + @@public_event_store + end + + def self.event_store + @@module_event_store + end + + def self.events_class_remapping + {} + end end From da670e31b1f594122db127f34fa4403ad7ab05bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Fri, 5 Jun 2020 10:22:53 +0200 Subject: [PATCH 17/35] Add base event class --- .../connected_active_record/sample/Gemfile | 4 ++ .../sample/lib/event.rb | 55 +++++++++++++++++++ .../sample/orders/lib/orders.rb | 1 + .../sample/payments/lib/payments.rb | 1 + 4 files changed, 61 insertions(+) create mode 100644 contrib/connected_active_record/sample/lib/event.rb diff --git a/contrib/connected_active_record/sample/Gemfile b/contrib/connected_active_record/sample/Gemfile index fd6887f7f9..2e8635668f 100644 --- a/contrib/connected_active_record/sample/Gemfile +++ b/contrib/connected_active_record/sample/Gemfile @@ -29,5 +29,9 @@ end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] +gem 'dry-struct' +gem 'dry-types' + +gem 'ruby_event_store', path: '../../../ruby_event_store' gem 'rails_event_store_active_record', path: '../../../rails_event_store_active_record' gem 'rails_event_store', path: '../../../rails_event_store' diff --git a/contrib/connected_active_record/sample/lib/event.rb b/contrib/connected_active_record/sample/lib/event.rb new file mode 100644 index 0000000000..c4d59f83b0 --- /dev/null +++ b/contrib/connected_active_record/sample/lib/event.rb @@ -0,0 +1,55 @@ +require "ruby_event_store" +require 'time' +require 'json' +require 'dry-struct' +require 'dry-types' +require 'rspec/core' + +module Types + include Dry::Types() + + EventId = Types::Coercible::String.default { SecureRandom.uuid } + Metadata = Types.Constructor(RubyEventStore::Metadata) { |value| RubyEventStore::Metadata.new(value.to_h) }.default { RubyEventStore::Metadata.new } +end + + +class Event < Dry::Struct + transform_keys(&:to_sym) + + attribute :event_id, Types::EventId + attribute :metadata, Types::Metadata + alias :message_id :event_id + + def self.new(data: {}, metadata: {}, **args) + super(args.merge(data).merge(metadata: metadata)) + end + + def self.inherited(klass) + super + klass.define_singleton_method(:event_type) do |value| + klass.define_method(:event_type) do + value + end + end + end + + def timestamp + metadata[:timestamp] && Time.parse(metadata[:timestamp]) + end + + def data + to_h.reject{|k,_| [:event_id, :metadata].include?(k) } + end + + def event_type + self.class.name + end + + def ==(other_event) + other_event.instance_of?(self.class) && + other_event.event_id.eql?(event_id) && + other_event.data.eql?(data) + end + + alias_method :eql?, :== +end diff --git a/contrib/connected_active_record/sample/orders/lib/orders.rb b/contrib/connected_active_record/sample/orders/lib/orders.rb index d390ca284a..a919b55f3d 100644 --- a/contrib/connected_active_record/sample/orders/lib/orders.rb +++ b/contrib/connected_active_record/sample/orders/lib/orders.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require_relative 'orders/application_record' +require_relative '../../lib/event' module Orders def self.setup(config) diff --git a/contrib/connected_active_record/sample/payments/lib/payments.rb b/contrib/connected_active_record/sample/payments/lib/payments.rb index a471f690bb..f34f4dffbd 100644 --- a/contrib/connected_active_record/sample/payments/lib/payments.rb +++ b/contrib/connected_active_record/sample/payments/lib/payments.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require_relative 'payments/application_record' +require_relative '../../lib/event' module Payments def self.setup(config) From d17175340dc08358700086f7aafb0715fea8f7a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Fri, 5 Jun 2020 10:27:42 +0200 Subject: [PATCH 18/35] No new gem, just sample app The connected_active_record gem is not needed. Alctually, I think it is even not the best way to do it. Just RailsEventStoreActiveRecord::EventRepository with given base abstract class is enough. And it is better. The idea is: * each BC module need to implement it's own AR base class, * any models used by this BC should inherit from this base class, * BCs data will be now fully separated from main app data (separate dbs) * the only way to interact between BCs are: * direct API calls (including sending commands to command bus) * publishing and handling domain events using BC's public event store Regarding the event stores: * each BC has it's own event store where BC's private domain events are stored, * this private domain events must not leak outside the BC boundary, * any domain event published by BC to external world is stored in public event store (defined on application level) * publishing to public event store could use some patterns like Fat Events, Outbox. See more https://verraes.net/2019/05/ddd-msg-arch/ --- contrib/connected_active_record/Gemfile | 11 --- .../connected_active_record.gemspec | 28 ------- .../lib/connected_active_record.rb | 5 -- .../lib/connected_active_record/repository.rb | 59 ------------- .../lib/connected_active_record/version.rb | 3 - .../spec/connected_active_record_spec.rb | 82 ------------------- .../spec/spec_helper.rb | 8 -- .../.gitignore | 0 .../.rspec | 0 .../Gemfile | 6 +- .../README.md | 0 .../Rakefile | 0 .../app/assets/config/manifest.js | 0 .../app/assets/stylesheets/application.css | 0 .../app/controllers/application_controller.rb | 0 .../app/helpers/application_helper.rb | 0 .../app/jobs/application_job.rb | 0 .../app/models/application_record.rb | 0 .../app/views/layouts/application.html.erb | 0 .../config.ru | 0 .../config/application.rb | 0 .../config/boot.rb | 0 .../config/credentials.yml.enc | 0 .../config/database.yml | 0 .../config/environment.rb | 0 .../config/environments/development.rb | 0 .../config/environments/production.rb | 0 .../config/environments/test.rb | 0 .../application_controller_renderer.rb | 0 .../initializers/backtrace_silencers.rb | 0 .../initializers/content_security_policy.rb | 0 .../config/initializers/cookies_serializer.rb | 0 .../initializers/filter_parameter_logging.rb | 0 .../config/initializers/inflections.rb | 0 .../config/initializers/mime_types.rb | 0 .../config/initializers/rails_event_store.rb | 0 .../config/initializers/wrap_parameters.rb | 0 .../config/locales/en.yml | 0 .../config/puma.rb | 0 .../config/routes.rb | 0 ...0200604152933_create_event_store_events.rb | 0 ...0200604152933_create_event_store_events.rb | 0 .../db/orders_schema.rb | 0 ...0200604152933_create_event_store_events.rb | 0 .../db/payments_schema.rb | 0 .../db/schema.rb | 0 .../db/seeds.rb | 0 .../lib/event.rb | 0 .../orders/lib/orders.rb | 32 ++++++-- .../orders/lib/orders/application_record.rb | 0 .../orders/spec/orders_spec.rb | 0 .../orders/spec/spec_helper.rb | 0 .../payments/lib/payments.rb | 31 ++++++- .../lib/payments/application_record.rb | 0 .../payments/spec/payments_spec.rb | 0 .../payments/spec/spec_helper.rb | 0 .../public/404.html | 0 .../public/422.html | 0 .../public/500.html | 0 .../public/apple-touch-icon-precomposed.png | 0 .../public/apple-touch-icon.png | 0 .../public/favicon.ico | 0 .../public/robots.txt | 0 .../spec/orders_spec.rb | 0 .../spec/payments_spec.rb | 0 .../spec/rails_helper.rb | 0 .../spec/spec_helper.rb | 0 67 files changed, 55 insertions(+), 210 deletions(-) delete mode 100644 contrib/connected_active_record/Gemfile delete mode 100644 contrib/connected_active_record/connected_active_record.gemspec delete mode 100644 contrib/connected_active_record/lib/connected_active_record.rb delete mode 100644 contrib/connected_active_record/lib/connected_active_record/repository.rb delete mode 100644 contrib/connected_active_record/lib/connected_active_record/version.rb delete mode 100644 contrib/connected_active_record/spec/connected_active_record_spec.rb delete mode 100644 contrib/connected_active_record/spec/spec_helper.rb rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/.gitignore (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/.rspec (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/Gemfile (83%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/README.md (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/Rakefile (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/app/assets/config/manifest.js (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/app/assets/stylesheets/application.css (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/app/controllers/application_controller.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/app/helpers/application_helper.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/app/jobs/application_job.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/app/models/application_record.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/app/views/layouts/application.html.erb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config.ru (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/application.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/boot.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/credentials.yml.enc (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/database.yml (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/environment.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/environments/development.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/environments/production.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/environments/test.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/initializers/application_controller_renderer.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/initializers/backtrace_silencers.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/initializers/content_security_policy.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/initializers/cookies_serializer.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/initializers/filter_parameter_logging.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/initializers/inflections.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/initializers/mime_types.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/initializers/rails_event_store.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/initializers/wrap_parameters.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/locales/en.yml (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/puma.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/config/routes.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/db/migrate/20200604152933_create_event_store_events.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/db/orders/20200604152933_create_event_store_events.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/db/orders_schema.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/db/payments/20200604152933_create_event_store_events.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/db/payments_schema.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/db/schema.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/db/seeds.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/lib/event.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/orders/lib/orders.rb (50%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/orders/lib/orders/application_record.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/orders/spec/orders_spec.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/orders/spec/spec_helper.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/payments/lib/payments.rb (50%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/payments/lib/payments/application_record.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/payments/spec/payments_spec.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/payments/spec/spec_helper.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/public/404.html (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/public/422.html (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/public/500.html (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/public/apple-touch-icon-precomposed.png (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/public/apple-touch-icon.png (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/public/favicon.ico (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/public/robots.txt (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/spec/orders_spec.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/spec/payments_spec.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/spec/rails_helper.rb (100%) rename contrib/{connected_active_record/sample => multiple_databases_repository_sample_app}/spec/spec_helper.rb (100%) diff --git a/contrib/connected_active_record/Gemfile b/contrib/connected_active_record/Gemfile deleted file mode 100644 index e44040ada1..0000000000 --- a/contrib/connected_active_record/Gemfile +++ /dev/null @@ -1,11 +0,0 @@ -source "https://rubygems.org" - -git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } - -# Specify your gem's dependencies in connected_active_record.gemspec -gemspec - -gem 'ruby_event_store', path: '../../ruby_event_store' -gem 'rails_event_store_active_record', path: '../../rails_event_store_active_record' -gem 'childprocess' -gem 'sqlite3', '1.4.2' \ No newline at end of file diff --git a/contrib/connected_active_record/connected_active_record.gemspec b/contrib/connected_active_record/connected_active_record.gemspec deleted file mode 100644 index f842800a19..0000000000 --- a/contrib/connected_active_record/connected_active_record.gemspec +++ /dev/null @@ -1,28 +0,0 @@ -lib = File.expand_path("../lib", __FILE__) -$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require "connected_active_record/version" - -Gem::Specification.new do |spec| - spec.name = "connected_active_record" - spec.version = ConnectedActiveRecord::VERSION - spec.authors = ["Mirosław Pragłowski"] - spec.email = ["m@praglowski.com"] - - spec.summary = %q{EventRepository wrapper to allow easy use of - https://guides.rubyonrails.org/active_record_multiple_databases.html} - - spec.add_dependency "activerecord", ">= 6" - spec.add_dependency "ruby_event_store" - spec.add_dependency "rails_event_store_active_record" - - spec.add_development_dependency "rspec-rails" - - # Specify which files should be added to the gem when it is released. - # The `git ls-files -z` loads the files in the RubyGem that have been added into git. - spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } - end - spec.bindir = "exe" - spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } - spec.require_paths = ["lib"] -end \ No newline at end of file diff --git a/contrib/connected_active_record/lib/connected_active_record.rb b/contrib/connected_active_record/lib/connected_active_record.rb deleted file mode 100644 index c7c7865121..0000000000 --- a/contrib/connected_active_record/lib/connected_active_record.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'connected_active_record/version' -require 'connected_active_record/repository' - -module ConnectedActiveRecord -end \ No newline at end of file diff --git a/contrib/connected_active_record/lib/connected_active_record/repository.rb b/contrib/connected_active_record/lib/connected_active_record/repository.rb deleted file mode 100644 index 4fbe306449..0000000000 --- a/contrib/connected_active_record/lib/connected_active_record/repository.rb +++ /dev/null @@ -1,59 +0,0 @@ -require 'active_record' -require 'rails_event_store_active_record' - -module ConnectedActiveRecord - class Repository - def initialize(writing: nil, reading: nil) - @repository = RailsEventStoreActiveRecord::EventRepository.new( - build_base_klass(writing, reading) - ) - end - - def append_to_stream(events, stream, expected_version) - repository.append_to_stream(events, stream, expected_version) - end - - def link_to_stream(event_ids, stream, expected_version) - repository.link_to_stream(event_ids, stream, expected_version) - end - - def delete_stream(stream) - repository.delete_stream(stream) - end - - def has_event?(event_id) - repository.has_event?(event_id) - end - - def last_stream_event(stream) - repository.last_stream_event(stream) - end - - def read(specification) - repository.read(specification) - end - - def count(specification) - repository.count(specification) - end - - def update_messages(messages) - repository.update_messages(messages) - end - - def streams_of(event_id) - repository.streams_of(event_id) - end - - private - attr_reader :repository - - def build_base_klass(writing, reading) - return ActiveRecord::Base if writing.nil? && reading.nil? - Class.new(ActiveRecord::Base) do - self.abstract_class = true - connects_to database: { writing: writing, reading: reading } - end - end - end -end diff --git a/contrib/connected_active_record/lib/connected_active_record/version.rb b/contrib/connected_active_record/lib/connected_active_record/version.rb deleted file mode 100644 index 9dd1c6bb58..0000000000 --- a/contrib/connected_active_record/lib/connected_active_record/version.rb +++ /dev/null @@ -1,3 +0,0 @@ -module ConnectedActiveRecord - VERSION = "0.0.1" -end \ No newline at end of file diff --git a/contrib/connected_active_record/spec/connected_active_record_spec.rb b/contrib/connected_active_record/spec/connected_active_record_spec.rb deleted file mode 100644 index f87601f23a..0000000000 --- a/contrib/connected_active_record/spec/connected_active_record_spec.rb +++ /dev/null @@ -1,82 +0,0 @@ -require "spec_helper" -require 'ruby_event_store' -require 'ruby_event_store/spec/event_repository_lint' - -module ConnectedActiveRecord - RSpec.describe Repository do - include SchemaHelper - - context 'like a repository' do - around(:each) do |example| - begin - establish_database_connection - load_database_schema - example.run - ensure - drop_database - close_database_connection - end - end - - let(:test_race_conditions_auto) { !ENV['DATABASE_URL'].include?("sqlite") } - let(:test_race_conditions_any) { !ENV['DATABASE_URL'].include?("sqlite") } - let(:test_binary) { true } - let(:test_change) { true } - - it_behaves_like :event_repository, Repository - end - - context 'multiple databases' do - class PrimaryApplicationrecord < ActiveRecord::Base - self.abstract_class = true - establish_connection({ - adapter: "sqlite3", - primary: { - database: "primary.db" - }, - secondary: { - database: "secondary.db" - } - }) - connects_to database: { writing: :primary, reading: :primary } - end - - class SecondaryApplicationrecord < ActiveRecord::Base - self.abstract_class = true - establish_connection({ - adapter: "sqlite3", - primary: { - database: "primary.db" - }, - secondary: { - database: "secondary.db" - } - }) - connects_to database: { writing: :secondary, reading: :secondary } - end - - specify "each repository instance could have it's own database" do - mapper = RubyEventStore::Mappers::NullMapper.new - - primary_repository = Repository.new(PrimaryApplicationRecord) - primary_reader = RubyEventStore::SpecificationReader.new(primary_repository, mapper) - primary_repository.append_to_stream( - [primary_event = RubyEventStore::SRecord.new], - RubyEventStore::Stream.new(RubyEventStore::GLOBAL_STREAM), - RubyEventStore::ExpectedVersion.any - ) - - secondary_repository = Repository.new(SecondaryApplicationRecord) - secondary_reader = RubyEventStore::SpecificationReader.new(secondary_repository, mapper) - secondary_repository.append_to_stream( - [secondary_event = RubyEventStore::SRecord.new], - RubyEventStore::Stream.new(RubyEventStore::GLOBAL_STREAM), - RubyEventStore::ExpectedVersion.any - ) - - read_from_primary = primary_reader.read.to_a - read_from_secondary = secondary_reader.read.to_a - end - end - end -end diff --git a/contrib/connected_active_record/spec/spec_helper.rb b/contrib/connected_active_record/spec/spec_helper.rb deleted file mode 100644 index b3aaa77a33..0000000000 --- a/contrib/connected_active_record/spec/spec_helper.rb +++ /dev/null @@ -1,8 +0,0 @@ -require_relative '../../../support/helpers/rspec_defaults' -require_relative '../../../support/helpers/schema_helper' -require 'connected_active_record' - -$verbose = ENV.has_key?('VERBOSE') ? true : false -ActiveRecord::Schema.verbose = $verbose - -ENV['DATABASE_URL'] ||= 'sqlite3:db.sqlite3' \ No newline at end of file diff --git a/contrib/connected_active_record/sample/.gitignore b/contrib/multiple_databases_repository_sample_app/.gitignore similarity index 100% rename from contrib/connected_active_record/sample/.gitignore rename to contrib/multiple_databases_repository_sample_app/.gitignore diff --git a/contrib/connected_active_record/sample/.rspec b/contrib/multiple_databases_repository_sample_app/.rspec similarity index 100% rename from contrib/connected_active_record/sample/.rspec rename to contrib/multiple_databases_repository_sample_app/.rspec diff --git a/contrib/connected_active_record/sample/Gemfile b/contrib/multiple_databases_repository_sample_app/Gemfile similarity index 83% rename from contrib/connected_active_record/sample/Gemfile rename to contrib/multiple_databases_repository_sample_app/Gemfile index 2e8635668f..594350377e 100644 --- a/contrib/connected_active_record/sample/Gemfile +++ b/contrib/multiple_databases_repository_sample_app/Gemfile @@ -32,6 +32,6 @@ gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem 'dry-struct' gem 'dry-types' -gem 'ruby_event_store', path: '../../../ruby_event_store' -gem 'rails_event_store_active_record', path: '../../../rails_event_store_active_record' -gem 'rails_event_store', path: '../../../rails_event_store' +gem 'ruby_event_store', path: '../../ruby_event_store' +gem 'rails_event_store_active_record', path: '../../rails_event_store_active_record' +gem 'rails_event_store', path: '../../rails_event_store' diff --git a/contrib/connected_active_record/sample/README.md b/contrib/multiple_databases_repository_sample_app/README.md similarity index 100% rename from contrib/connected_active_record/sample/README.md rename to contrib/multiple_databases_repository_sample_app/README.md diff --git a/contrib/connected_active_record/sample/Rakefile b/contrib/multiple_databases_repository_sample_app/Rakefile similarity index 100% rename from contrib/connected_active_record/sample/Rakefile rename to contrib/multiple_databases_repository_sample_app/Rakefile diff --git a/contrib/connected_active_record/sample/app/assets/config/manifest.js b/contrib/multiple_databases_repository_sample_app/app/assets/config/manifest.js similarity index 100% rename from contrib/connected_active_record/sample/app/assets/config/manifest.js rename to contrib/multiple_databases_repository_sample_app/app/assets/config/manifest.js diff --git a/contrib/connected_active_record/sample/app/assets/stylesheets/application.css b/contrib/multiple_databases_repository_sample_app/app/assets/stylesheets/application.css similarity index 100% rename from contrib/connected_active_record/sample/app/assets/stylesheets/application.css rename to contrib/multiple_databases_repository_sample_app/app/assets/stylesheets/application.css diff --git a/contrib/connected_active_record/sample/app/controllers/application_controller.rb b/contrib/multiple_databases_repository_sample_app/app/controllers/application_controller.rb similarity index 100% rename from contrib/connected_active_record/sample/app/controllers/application_controller.rb rename to contrib/multiple_databases_repository_sample_app/app/controllers/application_controller.rb diff --git a/contrib/connected_active_record/sample/app/helpers/application_helper.rb b/contrib/multiple_databases_repository_sample_app/app/helpers/application_helper.rb similarity index 100% rename from contrib/connected_active_record/sample/app/helpers/application_helper.rb rename to contrib/multiple_databases_repository_sample_app/app/helpers/application_helper.rb diff --git a/contrib/connected_active_record/sample/app/jobs/application_job.rb b/contrib/multiple_databases_repository_sample_app/app/jobs/application_job.rb similarity index 100% rename from contrib/connected_active_record/sample/app/jobs/application_job.rb rename to contrib/multiple_databases_repository_sample_app/app/jobs/application_job.rb diff --git a/contrib/connected_active_record/sample/app/models/application_record.rb b/contrib/multiple_databases_repository_sample_app/app/models/application_record.rb similarity index 100% rename from contrib/connected_active_record/sample/app/models/application_record.rb rename to contrib/multiple_databases_repository_sample_app/app/models/application_record.rb diff --git a/contrib/connected_active_record/sample/app/views/layouts/application.html.erb b/contrib/multiple_databases_repository_sample_app/app/views/layouts/application.html.erb similarity index 100% rename from contrib/connected_active_record/sample/app/views/layouts/application.html.erb rename to contrib/multiple_databases_repository_sample_app/app/views/layouts/application.html.erb diff --git a/contrib/connected_active_record/sample/config.ru b/contrib/multiple_databases_repository_sample_app/config.ru similarity index 100% rename from contrib/connected_active_record/sample/config.ru rename to contrib/multiple_databases_repository_sample_app/config.ru diff --git a/contrib/connected_active_record/sample/config/application.rb b/contrib/multiple_databases_repository_sample_app/config/application.rb similarity index 100% rename from contrib/connected_active_record/sample/config/application.rb rename to contrib/multiple_databases_repository_sample_app/config/application.rb diff --git a/contrib/connected_active_record/sample/config/boot.rb b/contrib/multiple_databases_repository_sample_app/config/boot.rb similarity index 100% rename from contrib/connected_active_record/sample/config/boot.rb rename to contrib/multiple_databases_repository_sample_app/config/boot.rb diff --git a/contrib/connected_active_record/sample/config/credentials.yml.enc b/contrib/multiple_databases_repository_sample_app/config/credentials.yml.enc similarity index 100% rename from contrib/connected_active_record/sample/config/credentials.yml.enc rename to contrib/multiple_databases_repository_sample_app/config/credentials.yml.enc diff --git a/contrib/connected_active_record/sample/config/database.yml b/contrib/multiple_databases_repository_sample_app/config/database.yml similarity index 100% rename from contrib/connected_active_record/sample/config/database.yml rename to contrib/multiple_databases_repository_sample_app/config/database.yml diff --git a/contrib/connected_active_record/sample/config/environment.rb b/contrib/multiple_databases_repository_sample_app/config/environment.rb similarity index 100% rename from contrib/connected_active_record/sample/config/environment.rb rename to contrib/multiple_databases_repository_sample_app/config/environment.rb diff --git a/contrib/connected_active_record/sample/config/environments/development.rb b/contrib/multiple_databases_repository_sample_app/config/environments/development.rb similarity index 100% rename from contrib/connected_active_record/sample/config/environments/development.rb rename to contrib/multiple_databases_repository_sample_app/config/environments/development.rb diff --git a/contrib/connected_active_record/sample/config/environments/production.rb b/contrib/multiple_databases_repository_sample_app/config/environments/production.rb similarity index 100% rename from contrib/connected_active_record/sample/config/environments/production.rb rename to contrib/multiple_databases_repository_sample_app/config/environments/production.rb diff --git a/contrib/connected_active_record/sample/config/environments/test.rb b/contrib/multiple_databases_repository_sample_app/config/environments/test.rb similarity index 100% rename from contrib/connected_active_record/sample/config/environments/test.rb rename to contrib/multiple_databases_repository_sample_app/config/environments/test.rb diff --git a/contrib/connected_active_record/sample/config/initializers/application_controller_renderer.rb b/contrib/multiple_databases_repository_sample_app/config/initializers/application_controller_renderer.rb similarity index 100% rename from contrib/connected_active_record/sample/config/initializers/application_controller_renderer.rb rename to contrib/multiple_databases_repository_sample_app/config/initializers/application_controller_renderer.rb diff --git a/contrib/connected_active_record/sample/config/initializers/backtrace_silencers.rb b/contrib/multiple_databases_repository_sample_app/config/initializers/backtrace_silencers.rb similarity index 100% rename from contrib/connected_active_record/sample/config/initializers/backtrace_silencers.rb rename to contrib/multiple_databases_repository_sample_app/config/initializers/backtrace_silencers.rb diff --git a/contrib/connected_active_record/sample/config/initializers/content_security_policy.rb b/contrib/multiple_databases_repository_sample_app/config/initializers/content_security_policy.rb similarity index 100% rename from contrib/connected_active_record/sample/config/initializers/content_security_policy.rb rename to contrib/multiple_databases_repository_sample_app/config/initializers/content_security_policy.rb diff --git a/contrib/connected_active_record/sample/config/initializers/cookies_serializer.rb b/contrib/multiple_databases_repository_sample_app/config/initializers/cookies_serializer.rb similarity index 100% rename from contrib/connected_active_record/sample/config/initializers/cookies_serializer.rb rename to contrib/multiple_databases_repository_sample_app/config/initializers/cookies_serializer.rb diff --git a/contrib/connected_active_record/sample/config/initializers/filter_parameter_logging.rb b/contrib/multiple_databases_repository_sample_app/config/initializers/filter_parameter_logging.rb similarity index 100% rename from contrib/connected_active_record/sample/config/initializers/filter_parameter_logging.rb rename to contrib/multiple_databases_repository_sample_app/config/initializers/filter_parameter_logging.rb diff --git a/contrib/connected_active_record/sample/config/initializers/inflections.rb b/contrib/multiple_databases_repository_sample_app/config/initializers/inflections.rb similarity index 100% rename from contrib/connected_active_record/sample/config/initializers/inflections.rb rename to contrib/multiple_databases_repository_sample_app/config/initializers/inflections.rb diff --git a/contrib/connected_active_record/sample/config/initializers/mime_types.rb b/contrib/multiple_databases_repository_sample_app/config/initializers/mime_types.rb similarity index 100% rename from contrib/connected_active_record/sample/config/initializers/mime_types.rb rename to contrib/multiple_databases_repository_sample_app/config/initializers/mime_types.rb diff --git a/contrib/connected_active_record/sample/config/initializers/rails_event_store.rb b/contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb similarity index 100% rename from contrib/connected_active_record/sample/config/initializers/rails_event_store.rb rename to contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb diff --git a/contrib/connected_active_record/sample/config/initializers/wrap_parameters.rb b/contrib/multiple_databases_repository_sample_app/config/initializers/wrap_parameters.rb similarity index 100% rename from contrib/connected_active_record/sample/config/initializers/wrap_parameters.rb rename to contrib/multiple_databases_repository_sample_app/config/initializers/wrap_parameters.rb diff --git a/contrib/connected_active_record/sample/config/locales/en.yml b/contrib/multiple_databases_repository_sample_app/config/locales/en.yml similarity index 100% rename from contrib/connected_active_record/sample/config/locales/en.yml rename to contrib/multiple_databases_repository_sample_app/config/locales/en.yml diff --git a/contrib/connected_active_record/sample/config/puma.rb b/contrib/multiple_databases_repository_sample_app/config/puma.rb similarity index 100% rename from contrib/connected_active_record/sample/config/puma.rb rename to contrib/multiple_databases_repository_sample_app/config/puma.rb diff --git a/contrib/connected_active_record/sample/config/routes.rb b/contrib/multiple_databases_repository_sample_app/config/routes.rb similarity index 100% rename from contrib/connected_active_record/sample/config/routes.rb rename to contrib/multiple_databases_repository_sample_app/config/routes.rb diff --git a/contrib/connected_active_record/sample/db/migrate/20200604152933_create_event_store_events.rb b/contrib/multiple_databases_repository_sample_app/db/migrate/20200604152933_create_event_store_events.rb similarity index 100% rename from contrib/connected_active_record/sample/db/migrate/20200604152933_create_event_store_events.rb rename to contrib/multiple_databases_repository_sample_app/db/migrate/20200604152933_create_event_store_events.rb diff --git a/contrib/connected_active_record/sample/db/orders/20200604152933_create_event_store_events.rb b/contrib/multiple_databases_repository_sample_app/db/orders/20200604152933_create_event_store_events.rb similarity index 100% rename from contrib/connected_active_record/sample/db/orders/20200604152933_create_event_store_events.rb rename to contrib/multiple_databases_repository_sample_app/db/orders/20200604152933_create_event_store_events.rb diff --git a/contrib/connected_active_record/sample/db/orders_schema.rb b/contrib/multiple_databases_repository_sample_app/db/orders_schema.rb similarity index 100% rename from contrib/connected_active_record/sample/db/orders_schema.rb rename to contrib/multiple_databases_repository_sample_app/db/orders_schema.rb diff --git a/contrib/connected_active_record/sample/db/payments/20200604152933_create_event_store_events.rb b/contrib/multiple_databases_repository_sample_app/db/payments/20200604152933_create_event_store_events.rb similarity index 100% rename from contrib/connected_active_record/sample/db/payments/20200604152933_create_event_store_events.rb rename to contrib/multiple_databases_repository_sample_app/db/payments/20200604152933_create_event_store_events.rb diff --git a/contrib/connected_active_record/sample/db/payments_schema.rb b/contrib/multiple_databases_repository_sample_app/db/payments_schema.rb similarity index 100% rename from contrib/connected_active_record/sample/db/payments_schema.rb rename to contrib/multiple_databases_repository_sample_app/db/payments_schema.rb diff --git a/contrib/connected_active_record/sample/db/schema.rb b/contrib/multiple_databases_repository_sample_app/db/schema.rb similarity index 100% rename from contrib/connected_active_record/sample/db/schema.rb rename to contrib/multiple_databases_repository_sample_app/db/schema.rb diff --git a/contrib/connected_active_record/sample/db/seeds.rb b/contrib/multiple_databases_repository_sample_app/db/seeds.rb similarity index 100% rename from contrib/connected_active_record/sample/db/seeds.rb rename to contrib/multiple_databases_repository_sample_app/db/seeds.rb diff --git a/contrib/connected_active_record/sample/lib/event.rb b/contrib/multiple_databases_repository_sample_app/lib/event.rb similarity index 100% rename from contrib/connected_active_record/sample/lib/event.rb rename to contrib/multiple_databases_repository_sample_app/lib/event.rb diff --git a/contrib/connected_active_record/sample/orders/lib/orders.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb similarity index 50% rename from contrib/connected_active_record/sample/orders/lib/orders.rb rename to contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb index a919b55f3d..7e370aa106 100644 --- a/contrib/connected_active_record/sample/orders/lib/orders.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb @@ -14,14 +14,15 @@ def self.setup(config) ) # Subscribe public event handlers below - # config.event_store.tap do |store| - # store.subscribe(InvoiceReadModel.new, to: [InvoicePrinted]) - # end + config.event_store.tap do |store| +# store.subscribe(MarkAsPaid.new, to: [Paid]) +# store.subscribe(RequestPayment.new, to: [PaymentFailed]) + end # Register commands handled by this module below - # config.command_bus.tap do |bus| - # bus.register(SubmitOrder, Orders::OnSubmit.new) - # end + config.command_bus.tap do |bus| +# bus.register(SubmitOrder, Submit.new) + end end def self.public_event_store @@ -33,6 +34,23 @@ def self.event_store end def self.events_class_remapping - {} + { + 'order-submitted' => Submitted, + } + end + + class Paid < Event + event_type 'payment-completed' + attribute :order_id, Types::Strict::String + end + class PaymentFailed < Event + event_type 'payment-failed' + attribute :order_id, Types::Strict::String + end + class Submitted < Event + event_type 'order-submitted' + attribute :order_id, Types::Strict::String + attribute :customer_id, Types::Strict::String + attribute :total_amount, Types::Strict::Float end end diff --git a/contrib/connected_active_record/sample/orders/lib/orders/application_record.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/application_record.rb similarity index 100% rename from contrib/connected_active_record/sample/orders/lib/orders/application_record.rb rename to contrib/multiple_databases_repository_sample_app/orders/lib/orders/application_record.rb diff --git a/contrib/connected_active_record/sample/orders/spec/orders_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/orders_spec.rb similarity index 100% rename from contrib/connected_active_record/sample/orders/spec/orders_spec.rb rename to contrib/multiple_databases_repository_sample_app/orders/spec/orders_spec.rb diff --git a/contrib/connected_active_record/sample/orders/spec/spec_helper.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb similarity index 100% rename from contrib/connected_active_record/sample/orders/spec/spec_helper.rb rename to contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb diff --git a/contrib/connected_active_record/sample/payments/lib/payments.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb similarity index 50% rename from contrib/connected_active_record/sample/payments/lib/payments.rb rename to contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb index f34f4dffbd..1553f27671 100644 --- a/contrib/connected_active_record/sample/payments/lib/payments.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb @@ -14,9 +14,9 @@ def self.setup(config) ) # Subscribe public event handlers below - # config.event_store.tap do |store| - # store.subscribe(StartPaymentProcess.new, to: [OrderSubmitted]) - # end + config.event_store.tap do |store| +# store.subscribe(Authorize.new, to: [Initiated]) + end # Register commands handled by this module below # config.command_bus.tap do |bus| @@ -33,6 +33,29 @@ def self.event_store end def self.events_class_remapping - {} + { + 'order-submitted' => Initiated, + 'payment-authorized' => Authorized, + 'payment-authorization-failed' => AuthorizationFailed, + } + end + + class Initiated < Event + event_type 'order-submitted' + attribute :customer_id, Types::Strict::String + attribute :order_id, Types::Strict::String + attribute :total_amount, Types::Strict::Float + end + + class Authorized < Event + event_type 'payment-authorized' + attribute :transaction_id, Types::Strict::Integer + attribute :order_id, Types::Strict::String + end + + class AuthorizationFailed < Event + event_type 'payment-authorization-failed' + attribute :transaction_id, Types::Strict::Integer + attribute :order_id, Types::Strict::String end end diff --git a/contrib/connected_active_record/sample/payments/lib/payments/application_record.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/application_record.rb similarity index 100% rename from contrib/connected_active_record/sample/payments/lib/payments/application_record.rb rename to contrib/multiple_databases_repository_sample_app/payments/lib/payments/application_record.rb diff --git a/contrib/connected_active_record/sample/payments/spec/payments_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/payments_spec.rb similarity index 100% rename from contrib/connected_active_record/sample/payments/spec/payments_spec.rb rename to contrib/multiple_databases_repository_sample_app/payments/spec/payments_spec.rb diff --git a/contrib/connected_active_record/sample/payments/spec/spec_helper.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb similarity index 100% rename from contrib/connected_active_record/sample/payments/spec/spec_helper.rb rename to contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb diff --git a/contrib/connected_active_record/sample/public/404.html b/contrib/multiple_databases_repository_sample_app/public/404.html similarity index 100% rename from contrib/connected_active_record/sample/public/404.html rename to contrib/multiple_databases_repository_sample_app/public/404.html diff --git a/contrib/connected_active_record/sample/public/422.html b/contrib/multiple_databases_repository_sample_app/public/422.html similarity index 100% rename from contrib/connected_active_record/sample/public/422.html rename to contrib/multiple_databases_repository_sample_app/public/422.html diff --git a/contrib/connected_active_record/sample/public/500.html b/contrib/multiple_databases_repository_sample_app/public/500.html similarity index 100% rename from contrib/connected_active_record/sample/public/500.html rename to contrib/multiple_databases_repository_sample_app/public/500.html diff --git a/contrib/connected_active_record/sample/public/apple-touch-icon-precomposed.png b/contrib/multiple_databases_repository_sample_app/public/apple-touch-icon-precomposed.png similarity index 100% rename from contrib/connected_active_record/sample/public/apple-touch-icon-precomposed.png rename to contrib/multiple_databases_repository_sample_app/public/apple-touch-icon-precomposed.png diff --git a/contrib/connected_active_record/sample/public/apple-touch-icon.png b/contrib/multiple_databases_repository_sample_app/public/apple-touch-icon.png similarity index 100% rename from contrib/connected_active_record/sample/public/apple-touch-icon.png rename to contrib/multiple_databases_repository_sample_app/public/apple-touch-icon.png diff --git a/contrib/connected_active_record/sample/public/favicon.ico b/contrib/multiple_databases_repository_sample_app/public/favicon.ico similarity index 100% rename from contrib/connected_active_record/sample/public/favicon.ico rename to contrib/multiple_databases_repository_sample_app/public/favicon.ico diff --git a/contrib/connected_active_record/sample/public/robots.txt b/contrib/multiple_databases_repository_sample_app/public/robots.txt similarity index 100% rename from contrib/connected_active_record/sample/public/robots.txt rename to contrib/multiple_databases_repository_sample_app/public/robots.txt diff --git a/contrib/connected_active_record/sample/spec/orders_spec.rb b/contrib/multiple_databases_repository_sample_app/spec/orders_spec.rb similarity index 100% rename from contrib/connected_active_record/sample/spec/orders_spec.rb rename to contrib/multiple_databases_repository_sample_app/spec/orders_spec.rb diff --git a/contrib/connected_active_record/sample/spec/payments_spec.rb b/contrib/multiple_databases_repository_sample_app/spec/payments_spec.rb similarity index 100% rename from contrib/connected_active_record/sample/spec/payments_spec.rb rename to contrib/multiple_databases_repository_sample_app/spec/payments_spec.rb diff --git a/contrib/connected_active_record/sample/spec/rails_helper.rb b/contrib/multiple_databases_repository_sample_app/spec/rails_helper.rb similarity index 100% rename from contrib/connected_active_record/sample/spec/rails_helper.rb rename to contrib/multiple_databases_repository_sample_app/spec/rails_helper.rb diff --git a/contrib/connected_active_record/sample/spec/spec_helper.rb b/contrib/multiple_databases_repository_sample_app/spec/spec_helper.rb similarity index 100% rename from contrib/connected_active_record/sample/spec/spec_helper.rb rename to contrib/multiple_databases_repository_sample_app/spec/spec_helper.rb From 81a7658702f9615ef3ae89c8fb8d3206727d58df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Mon, 8 Jun 2020 19:53:40 +0200 Subject: [PATCH 19/35] Use sample domain logic From https://github.com/RailsEventStore/cqrs-es-sample-with-res with some changes: * RSpec instead of MiniTest * use RES RSpec matchers --- .../Gemfile | 1 + .../config/environments/development.rb | 2 + .../config/environments/production.rb | 2 + .../config/environments/test.rb | 2 + .../config/initializers/rails_event_store.rb | 2 +- .../lib/command.rb | 11 ++ .../lib/command_handler.rb | 21 ++++ .../lib/types.rb | 8 ++ .../orders/lib/orders.rb | 58 ++++++---- .../orders/lib/orders/.keep | 0 .../orders/lib/orders/add_item_to_basket.rb | 6 + .../orders/lib/orders/application_record.rb | 2 + .../lib/orders/fake_number_generator.rb | 7 ++ .../orders/lib/orders/item_added_to_basket.rb | 6 + .../lib/orders/item_removed_from_basket.rb | 6 + .../orders/lib/orders/mark_order_as_paid.rb | 6 + .../orders/lib/orders/number_generator.rb | 7 ++ .../lib/orders/on_add_item_to_basket.rb | 11 ++ .../lib/orders/on_mark_order_as_paid.rb | 11 ++ .../lib/orders/on_remove_item_from_basket.rb | 11 ++ .../lib/orders/on_set_order_as_expired.rb | 11 ++ .../orders/lib/orders/on_submit_order.rb | 24 ++++ .../orders/lib/orders/order.rb | 93 +++++++++++++++ .../orders/lib/orders/order_expired.rb | 5 + .../orders/lib/orders/order_line.rb | 31 +++++ .../orders/lib/orders/order_paid.rb | 6 + .../orders/lib/orders/order_submitted.rb | 7 ++ .../lib/orders/remove_item_from_basket.rb | 6 + .../orders/lib/orders/set_order_as_expired.rb | 5 + .../orders/lib/orders/submit_order.rb | 6 + .../orders/spec/add_item_to_basket_spec.rb | 29 +++++ .../orders/spec/mark_order_as_paid_spec.rb | 46 ++++++++ .../orders/spec/order_spec.rb | 6 + .../orders/spec/orders_spec.rb | 4 - .../spec/remove_item_from_basket_spec.rb | 30 +++++ .../orders/spec/set_order_as_expired_spec.rb | 52 +++++++++ .../orders/spec/spec_helper.rb | 19 ++- .../orders/spec/submit_order_spec.rb | 52 +++++++++ .../payments/lib/payments.rb | 51 +++----- .../payments/lib/payments/.keep | 0 .../lib/payments/authorize_payment.rb | 6 + .../payments/lib/payments/capture_payment.rb | 5 + .../lib/payments/on_authorize_payment.rb | 11 ++ .../lib/payments/on_capture_payment.rb | 11 ++ .../lib/payments/on_release_payment.rb | 11 ++ .../payments/lib/payments/payment.rb | 66 +++++++++++ .../lib/payments/payment_authorized.rb | 6 + .../payments/lib/payments/payment_captured.rb | 6 + .../payments/lib/payments/payment_released.rb | 6 + .../payments/lib/payments/release_payment.rb | 5 + .../spec/on_authorize_payment_spec.rb | 18 +++ .../payments/spec/on_capture_payment_spec.rb | 19 +++ .../payments/spec/on_release_payment_spec.rb | 19 +++ .../payments/spec/payment_spec.rb | 109 ++++++++++++++++++ .../payments/spec/payments_spec.rb | 4 - .../payments/spec/spec_helper.rb | 18 ++- 56 files changed, 910 insertions(+), 73 deletions(-) create mode 100644 contrib/multiple_databases_repository_sample_app/lib/command.rb create mode 100644 contrib/multiple_databases_repository_sample_app/lib/command_handler.rb create mode 100644 contrib/multiple_databases_repository_sample_app/lib/types.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/.keep create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/add_item_to_basket.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/fake_number_generator.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/item_added_to_basket.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/item_removed_from_basket.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/mark_order_as_paid.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/number_generator.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_add_item_to_basket.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_mark_order_as_paid.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_remove_item_from_basket.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_set_order_as_expired.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_submit_order.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/order.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_expired.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_line.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_paid.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_submitted.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/remove_item_from_basket.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/set_order_as_expired.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/submit_order.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/spec/add_item_to_basket_spec.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/spec/mark_order_as_paid_spec.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/spec/order_spec.rb delete mode 100644 contrib/multiple_databases_repository_sample_app/orders/spec/orders_spec.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/spec/remove_item_from_basket_spec.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/spec/set_order_as_expired_spec.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/spec/submit_order_spec.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/lib/payments/.keep create mode 100644 contrib/multiple_databases_repository_sample_app/payments/lib/payments/authorize_payment.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/lib/payments/capture_payment.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_authorize_payment.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_capture_payment.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_release_payment.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_authorized.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_captured.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_released.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/lib/payments/release_payment.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/spec/on_authorize_payment_spec.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/spec/on_capture_payment_spec.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/spec/on_release_payment_spec.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/spec/payment_spec.rb delete mode 100644 contrib/multiple_databases_repository_sample_app/payments/spec/payments_spec.rb diff --git a/contrib/multiple_databases_repository_sample_app/Gemfile b/contrib/multiple_databases_repository_sample_app/Gemfile index 594350377e..ecdd2e1742 100644 --- a/contrib/multiple_databases_repository_sample_app/Gemfile +++ b/contrib/multiple_databases_repository_sample_app/Gemfile @@ -19,6 +19,7 @@ group :development, :test do gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'rspec-rails' gem 'database_cleaner' + gem 'rails_event_store-rspec' end group :development do diff --git a/contrib/multiple_databases_repository_sample_app/config/environments/development.rb b/contrib/multiple_databases_repository_sample_app/config/environments/development.rb index 8ea13e444a..bd72d8c4c1 100644 --- a/contrib/multiple_databases_repository_sample_app/config/environments/development.rb +++ b/contrib/multiple_databases_repository_sample_app/config/environments/development.rb @@ -44,4 +44,6 @@ # Use an evented file watcher to asynchronously detect changes in source code, # routes, locales, etc. This feature depends on the listen gem. # config.file_watcher = ActiveSupport::EventedFileUpdateChecker + + config.number_generator = nil # Orders::NumberGenerator.new end diff --git a/contrib/multiple_databases_repository_sample_app/config/environments/production.rb b/contrib/multiple_databases_repository_sample_app/config/environments/production.rb index 3af1e10966..082c9847fb 100644 --- a/contrib/multiple_databases_repository_sample_app/config/environments/production.rb +++ b/contrib/multiple_databases_repository_sample_app/config/environments/production.rb @@ -89,4 +89,6 @@ # config.active_record.database_selector = { delay: 2.seconds } # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session + + config.number_generator = nil # Orders::NumberGenerator.new end diff --git a/contrib/multiple_databases_repository_sample_app/config/environments/test.rb b/contrib/multiple_databases_repository_sample_app/config/environments/test.rb index 06aef36da8..097317dbe3 100644 --- a/contrib/multiple_databases_repository_sample_app/config/environments/test.rb +++ b/contrib/multiple_databases_repository_sample_app/config/environments/test.rb @@ -35,4 +35,6 @@ # Raises error for missing translations. # config.action_view.raise_on_missing_translations = true + + config.number_generator = nil # Orders::FakeNumberGenerator.new end diff --git a/contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb b/contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb index 55337518aa..9436d5c2f6 100644 --- a/contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb +++ b/contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb @@ -20,7 +20,7 @@ # Register command handlers below # Rails.configuration.command_bus.tap do |bus| # bus.register(PrintInvoice, Invoicing::OnPrint.new) - # bus.register(SubmitOrder, ->(cmd) { Ordering::OnSubmitOrder.new.call(cmd) }) + # bus.register(SubmitOrder, ->(cmd) { Orders::OnSubmitOrder.new.call(cmd) }) # end Orders.setup(Rails.configuration) diff --git a/contrib/multiple_databases_repository_sample_app/lib/command.rb b/contrib/multiple_databases_repository_sample_app/lib/command.rb new file mode 100644 index 0000000000..e8d4cee961 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/lib/command.rb @@ -0,0 +1,11 @@ +require 'dry-struct' + +class Command < Dry::Struct::Value + Invalid = Class.new(StandardError) + + def self.new(*) + super + rescue Dry::Struct::Error => doh + raise Invalid, doh + end +end diff --git a/contrib/multiple_databases_repository_sample_app/lib/command_handler.rb b/contrib/multiple_databases_repository_sample_app/lib/command_handler.rb new file mode 100644 index 0000000000..9132e92f7e --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/lib/command_handler.rb @@ -0,0 +1,21 @@ +module CommandHandler + def initialize(event_store) + @repository = AggregateRoot::InstrumentedRepository.new( + AggregateRoot::Repository.new(event_store), + ActiveSupport::Notifications + ) + end + + def with_aggregate(aggregate, aggregate_id, &block) + stream = stream_name(aggregate.class, aggregate_id) + repository.with_aggregate(aggregate, stream, &block) + end + + private + attr_reader :repository + + def stream_name(aggregate_class, aggregate_id) + "#{aggregate_class.name}$#{aggregate_id}" + end +end + diff --git a/contrib/multiple_databases_repository_sample_app/lib/types.rb b/contrib/multiple_databases_repository_sample_app/lib/types.rb new file mode 100644 index 0000000000..955f08da96 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/lib/types.rb @@ -0,0 +1,8 @@ +require 'dry-types' + +module Types + ID = Types::Strict::Integer + UUID = Types::Strict::String.constrained(format: /\A[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\z/i) + TransactionId = Types::Strict::String.constrained(format: /\A[0-9a-fA-F]{32}\z/i) + OrderNumber = Types::Strict::String.constrained(format: /\A\d{4}\/\d{2}\/\d+\z/i) +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb index 7e370aa106..42e36f688b 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb @@ -2,15 +2,38 @@ require_relative 'orders/application_record' require_relative '../../lib/event' +require_relative '../../lib/types' +require_relative '../../lib/command' +require_relative '../../lib/command_handler' + +require_dependency 'orders/add_item_to_basket' +require_dependency 'orders/fake_number_generator' +require_dependency 'orders/item_added_to_basket' +require_dependency 'orders/item_removed_from_basket' +require_dependency 'orders/number_generator' +require_dependency 'orders/on_add_item_to_basket' +require_dependency 'orders/on_remove_item_from_basket' +require_dependency 'orders/on_set_order_as_expired' +require_dependency 'orders/on_mark_order_as_paid' +require_dependency 'orders/on_submit_order' +require_dependency 'orders/order' +require_dependency 'orders/order_expired' +require_dependency 'orders/order_paid' +require_dependency 'orders/order_line' +require_dependency 'orders/order_submitted' +require_dependency 'orders/remove_item_from_basket' +require_dependency 'orders/set_order_as_expired' +require_dependency 'orders/mark_order_as_paid' +require_dependency 'orders/submit_order' module Orders def self.setup(config) @@public_event_store = config.event_store + @@command_bus = config.command_bus @@module_event_store = RailsEventStore::Client.new( repository: RailsEventStoreActiveRecord::EventRepository.new( Orders::ApplicationRecord), - mapper: RubyEventStore::Mappers::Default.new( - serializer: JSON, events_class_remapping: events_class_remapping) + mapper: RubyEventStore::Mappers::Default.new(serializer: JSON) ) # Subscribe public event handlers below @@ -21,10 +44,18 @@ def self.setup(config) # Register commands handled by this module below config.command_bus.tap do |bus| -# bus.register(SubmitOrder, Submit.new) + bus.register(Orders::SubmitOrder, Orders::OnSubmitOrder.new(event_store, number_generator: config.number_generator)) + bus.register(Orders::SetOrderAsExpired, Orders::OnSetOrderAsExpired.new(event_store)) + bus.register(Orders::MarkOrderAsPaid, Orders::OnMarkOrderAsPaid.new(event_store)) + bus.register(Orders::AddItemToBasket, Orders::OnAddItemToBasket.new(event_store)) + bus.register(Orders::RemoveItemFromBasket, Orders::OnRemoveItemFromBasket.new(event_store)) end end + def self.command_bus + @@command_bus + end + def self.public_event_store @@public_event_store end @@ -32,25 +63,4 @@ def self.public_event_store def self.event_store @@module_event_store end - - def self.events_class_remapping - { - 'order-submitted' => Submitted, - } - end - - class Paid < Event - event_type 'payment-completed' - attribute :order_id, Types::Strict::String - end - class PaymentFailed < Event - event_type 'payment-failed' - attribute :order_id, Types::Strict::String - end - class Submitted < Event - event_type 'order-submitted' - attribute :order_id, Types::Strict::String - attribute :customer_id, Types::Strict::String - attribute :total_amount, Types::Strict::Float - end end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/.keep b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/add_item_to_basket.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/add_item_to_basket.rb new file mode 100644 index 0000000000..f0e4ab35de --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/add_item_to_basket.rb @@ -0,0 +1,6 @@ +module Orders + class AddItemToBasket < Command + attribute :order_id, Types::UUID + attribute :product_id, Types::Coercible::Integer + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/application_record.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/application_record.rb index 85e510b19e..28321d9aa2 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/application_record.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/application_record.rb @@ -1,3 +1,5 @@ +require 'active_record' + module Orders class ApplicationRecord < ActiveRecord::Base self.abstract_class = true diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/fake_number_generator.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/fake_number_generator.rb new file mode 100644 index 0000000000..eb10681ddf --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/fake_number_generator.rb @@ -0,0 +1,7 @@ +module Orders + class FakeNumberGenerator + def call + "2019/01/60" + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/item_added_to_basket.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/item_added_to_basket.rb new file mode 100644 index 0000000000..9eed977c17 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/item_added_to_basket.rb @@ -0,0 +1,6 @@ +module Orders + class ItemAddedToBasket < Event + attribute :order_id, Types::UUID + attribute :product_id, Types::ID + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/item_removed_from_basket.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/item_removed_from_basket.rb new file mode 100644 index 0000000000..5005735db3 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/item_removed_from_basket.rb @@ -0,0 +1,6 @@ +module Orders + class ItemRemovedFromBasket < Event + attribute :order_id, Types::UUID + attribute :product_id, Types::ID + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/mark_order_as_paid.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/mark_order_as_paid.rb new file mode 100644 index 0000000000..166653b215 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/mark_order_as_paid.rb @@ -0,0 +1,6 @@ +module Orders + class MarkOrderAsPaid < Command + attribute :order_id, Types::UUID + attribute :transaction_id, Types::Coercible::String + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/number_generator.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/number_generator.rb new file mode 100644 index 0000000000..ed60d6cf92 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/number_generator.rb @@ -0,0 +1,7 @@ +module Orders + class NumberGenerator + def call + Time.current.strftime("%Y/%m/#{SecureRandom.random_number(100)}") + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_add_item_to_basket.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_add_item_to_basket.rb new file mode 100644 index 0000000000..7f9ae9b052 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_add_item_to_basket.rb @@ -0,0 +1,11 @@ +module Orders + class OnAddItemToBasket + include CommandHandler + + def call(command) + with_aggregate(Order.new(command.order_id), command.order_id) do |order| + order.add_item(command.product_id) + end + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_mark_order_as_paid.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_mark_order_as_paid.rb new file mode 100644 index 0000000000..79bd72b7a8 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_mark_order_as_paid.rb @@ -0,0 +1,11 @@ +module Orders + class OnMarkOrderAsPaid + include CommandHandler + + def call(command) + with_aggregate(Order.new(command.order_id), command.order_id) do |order| + order.mark_as_paid(command.transaction_id) + end + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_remove_item_from_basket.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_remove_item_from_basket.rb new file mode 100644 index 0000000000..b287f29b1d --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_remove_item_from_basket.rb @@ -0,0 +1,11 @@ +module Orders + class OnRemoveItemFromBasket + include CommandHandler + + def call(command) + with_aggregate(Order.new(command.order_id), command.order_id) do |order| + order.remove_item(command.product_id) + end + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_set_order_as_expired.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_set_order_as_expired.rb new file mode 100644 index 0000000000..9e8f29d41d --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_set_order_as_expired.rb @@ -0,0 +1,11 @@ +module Orders + class OnSetOrderAsExpired + include CommandHandler + + def call(command) + with_aggregate(Order.new(command.order_id), command.order_id) do |order| + order.expire + end + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_submit_order.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_submit_order.rb new file mode 100644 index 0000000000..b7281e0dbe --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_submit_order.rb @@ -0,0 +1,24 @@ +module Orders + class OnSubmitOrder + include CommandHandler + + def initialize(event_store, number_generator:) + @repository = AggregateRoot::InstrumentedRepository.new( + AggregateRoot::Repository.new(event_store), + ActiveSupport::Notifications + ) + @number_generator = number_generator + end + + def call(command) + with_aggregate(Order.new(command.order_id), command.order_id) do |order| + order_number = number_generator.call + order.submit(order_number, command.customer_id) + end + end + + private + + attr_accessor :number_generator + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order.rb new file mode 100644 index 0000000000..ca405edf5c --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order.rb @@ -0,0 +1,93 @@ +require 'aggregate_root' + +module Orders + class Order + include AggregateRoot + + AlreadySubmitted = Class.new(StandardError) + AlreadyPaid = Class.new(StandardError) + NotSubmitted = Class.new(StandardError) + OrderHasExpired = Class.new(StandardError) + MissingCustomer = Class.new(StandardError) + + def initialize(id) + @id = id + @state = :draft + @order_lines = [] + end + + def submit(order_number, customer_id) + raise AlreadySubmitted if @state == :submitted + raise OrderHasExpired if @state == :expired + raise MissingCustomer unless customer_id + apply OrderSubmitted.new(data: {order_id: @id, order_number: order_number, customer_id: customer_id}) + end + + def mark_as_paid(transaction_id) + raise OrderHasExpired if @state == :expired + raise NotSubmitted unless @state == :submitted + apply OrderPaid.new(data: {order_id: @id, transaction_id: transaction_id}) + end + + def expire + raise AlreadyPaid if @state == :paid + apply OrderExpired.new(data: {order_id: @id}) + end + + def add_item(product_id) + raise AlreadySubmitted unless @state == :draft + apply ItemAddedToBasket.new(data: {order_id: @id, product_id: product_id}) + end + + def remove_item(product_id) + raise AlreadySubmitted unless @state == :draft + apply ItemRemovedFromBasket.new(data: {order_id: @id, product_id: product_id}) + end + + on OrderSubmitted do |event| + @customer_id = event.data[:customer_id] + @number = event.data[:order_number] + @state = :submitted + end + + on OrderPaid do |event| + @state = :paid + end + + on OrderExpired do |event| + @state = :expired + end + + on ItemAddedToBasket do |event| + product_id = event.data[:product_id] + order_line = find_order_line(product_id) + unless order_line + order_line = create_order_line(product_id) + @order_lines << order_line + end + order_line.increase_quantity + end + + on ItemRemovedFromBasket do |event| + product_id = event.data[:product_id] + order_line = find_order_line(product_id) + return unless order_line + order_line.decrease_quantity + remove_order_line(order_line) if order_line.empty? + end + + private + + def find_order_line(product_id) + @order_lines.select { |line| line.product_id == product_id }.first + end + + def create_order_line(product_id) + OrderLine.new(product_id) + end + + def remove_order_line(order_line) + @order_lines.delete(order_line) + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_expired.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_expired.rb new file mode 100644 index 0000000000..c9dbaa210a --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_expired.rb @@ -0,0 +1,5 @@ +module Orders + class OrderExpired < Event + attribute :order_id, Types::UUID + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_line.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_line.rb new file mode 100644 index 0000000000..b565444568 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_line.rb @@ -0,0 +1,31 @@ +module Orders + class OrderLine + include Comparable + attr_reader :product_id + + def initialize(product_id) + @product_id = product_id + @quantity = 0 + end + + def increase_quantity + @quantity += 1 + end + + def decrease_quantity + @quantity -= 1 + end + + def empty? + @quantity == 0 + end + + def <=>(other) + self.product_id <=> other.product_id + end + + private + + attr_accessor :quantity + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_paid.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_paid.rb new file mode 100644 index 0000000000..9d27a719ca --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_paid.rb @@ -0,0 +1,6 @@ +module Orders + class OrderPaid < Event + attribute :order_id, Types::UUID + attribute :transaction_id, Types::TransactionId + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_submitted.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_submitted.rb new file mode 100644 index 0000000000..41f90a40bb --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_submitted.rb @@ -0,0 +1,7 @@ +module Orders + class OrderSubmitted < Event + attribute :order_id, Types::UUID + attribute :order_number, Types::OrderNumber + attribute :customer_id, Types::ID + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/remove_item_from_basket.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/remove_item_from_basket.rb new file mode 100644 index 0000000000..2c8b065bb1 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/remove_item_from_basket.rb @@ -0,0 +1,6 @@ +module Orders + class RemoveItemFromBasket < Command + attribute :order_id, Types::UUID + attribute :product_id, Types::Coercible::Integer + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/set_order_as_expired.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/set_order_as_expired.rb new file mode 100644 index 0000000000..8cec655bc0 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/set_order_as_expired.rb @@ -0,0 +1,5 @@ +module Orders + class SetOrderAsExpired < Command + attribute :order_id, Types::UUID + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/submit_order.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/submit_order.rb new file mode 100644 index 0000000000..bcaae42fea --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/submit_order.rb @@ -0,0 +1,6 @@ +module Orders + class SubmitOrder < Command + attribute :order_id, Types::UUID + attribute :customer_id, Types::Coercible::Integer + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/add_item_to_basket_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/add_item_to_basket_spec.rb new file mode 100644 index 0000000000..5da88dcb7a --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/add_item_to_basket_spec.rb @@ -0,0 +1,29 @@ +require_relative 'spec_helper' + +module Orders + RSpec.describe AddItemToBasket do + let(:aggregate_id) { SecureRandom.uuid } + let(:stream) { "Orders::Order$#{aggregate_id}" } + let(:customer_id) { 997 } + let(:product_id) { 123 } + let(:order_number) { "2019/01/60" } + + it 'item is added to draft order' do + act(AddItemToBasket.new(order_id: aggregate_id, product_id: product_id)) + expect(Orders.event_store).to have_published( + an_event(ItemAddedToBasket) + .with_data(order_id: aggregate_id, product_id: product_id) + ) + end + + it 'no add allowed to submitted order' do + arrange(stream, [ + ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), + OrderSubmitted.new(data: {order_id: aggregate_id, order_number: order_number, customer_id: customer_id})]) + + expect do + act(AddItemToBasket.new(order_id: aggregate_id, product_id: product_id)) + end.to raise_error(Order::AlreadySubmitted) + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/mark_order_as_paid_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/mark_order_as_paid_spec.rb new file mode 100644 index 0000000000..258158e70e --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/mark_order_as_paid_spec.rb @@ -0,0 +1,46 @@ +require_relative 'spec_helper' + +module Orders + RSpec.describe MarkOrderAsPaid do + let(:aggregate_id) { SecureRandom.uuid } + let(:stream) { "Orders::Order$#{aggregate_id}" } + let(:customer_id) { 997 } + let(:product_id) { 123 } + let(:order_number) { "2019/01/60" } + + it 'draft order could not be marked as paid' do + arrange(stream, [ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id})]) + + expect do + act(MarkOrderAsPaid.new(order_id: aggregate_id, transaction_id: SecureRandom.hex(16))) + end.to raise_error(Order::NotSubmitted) + end + + it 'submitted order will be marked as paid' do + arrange(stream, [ + ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), + OrderSubmitted.new(data: {order_id: aggregate_id, order_number: '2018/12/1', customer_id: customer_id}), + ]) + + transaction_id = SecureRandom.hex(16) + act(MarkOrderAsPaid.new(order_id: aggregate_id, transaction_id: transaction_id)) + + expect(Orders.event_store).to have_published( + an_event(OrderPaid) + .with_data(order_id: aggregate_id, transaction_id: transaction_id) + ) + end + + it 'expired order cannot be marked as paid' do + arrange(stream, [ + ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), + OrderSubmitted.new(data: {order_id: aggregate_id, order_number: '2018/12/1', customer_id: customer_id}), + OrderExpired.new(data: {order_id: aggregate_id}), + ]) + + expect do + act(MarkOrderAsPaid.new(order_id: aggregate_id, transaction_id: SecureRandom.hex(16))) + end.to raise_error(Order::OrderHasExpired) + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/order_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/order_spec.rb new file mode 100644 index 0000000000..cdbec04180 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/order_spec.rb @@ -0,0 +1,6 @@ +require_relative 'spec_helper' + +module Orders + RSpec.describe Order do + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/orders_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/orders_spec.rb deleted file mode 100644 index 56ec0ef181..0000000000 --- a/contrib/multiple_databases_repository_sample_app/orders/spec/orders_spec.rb +++ /dev/null @@ -1,4 +0,0 @@ -require_relative 'spec_helper' - -RSpec.describe Orders do -end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/remove_item_from_basket_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/remove_item_from_basket_spec.rb new file mode 100644 index 0000000000..b62c256116 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/remove_item_from_basket_spec.rb @@ -0,0 +1,30 @@ +require_relative 'spec_helper' + +module Orders + RSpec.describe RemoveItemFromBasket do + let(:aggregate_id) { SecureRandom.uuid } + let(:stream) { "Orders::Order$#{aggregate_id}" } + let(:customer_id) { 997 } + let(:product_id) { 123 } + let(:order_number) { "2019/01/60" } + + it 'item is removed from draft order' do + arrange(stream, [ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id})]) + act(RemoveItemFromBasket.new(order_id: aggregate_id, product_id: product_id)) + expect(Orders.event_store).to have_published( + an_event(ItemRemovedFromBasket) + .with_data(order_id: aggregate_id, product_id: product_id) + ) + end + + it 'no remove allowed to created order' do + arrange(stream, [ + ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), + OrderSubmitted.new(data: {order_id: aggregate_id, order_number: order_number, customer_id: customer_id})]) + + expect do + act(RemoveItemFromBasket.new(order_id: aggregate_id, product_id: product_id)) + end.to raise_error(Order::AlreadySubmitted) + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/set_order_as_expired_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/set_order_as_expired_spec.rb new file mode 100644 index 0000000000..9ea01cf08a --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/set_order_as_expired_spec.rb @@ -0,0 +1,52 @@ +require_relative 'spec_helper' + +module Orders + RSpec.describe SetOrderAsExpired do + let(:aggregate_id) { SecureRandom.uuid } + let(:stream) { "Orders::Order$#{aggregate_id}" } + let(:customer_id) { 997 } + let(:product_id) { 123 } + let(:order_number) { "2019/01/60" } + + it 'draft order will expire' do + arrange(stream, [ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id})]) + + act(SetOrderAsExpired.new(order_id: aggregate_id)) + + expect(Orders.event_store).to have_published( + an_event(OrderExpired) + .with_data( + order_id: aggregate_id, + ) + ) + end + + it 'submitted order will expire' do + arrange(stream, [ + ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), + OrderSubmitted.new(data: {order_id: aggregate_id, order_number: '2018/12/1', customer_id: customer_id}), + ]) + + act(SetOrderAsExpired.new(order_id: aggregate_id)) + + expect(Orders.event_store).to have_published( + an_event(OrderExpired) + .with_data( + order_id: aggregate_id, + ) + ) + end + + it 'paid order cannot expire' do + arrange(stream, [ + ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), + OrderSubmitted.new(data: {order_id: aggregate_id, order_number: '2018/12/1', customer_id: customer_id}), + OrderPaid.new(data: {order_id: aggregate_id, transaction_id: SecureRandom.hex(16)}), + ]) + + expect do + act(SetOrderAsExpired.new(order_id: aggregate_id)) + end.to raise_error(Order::AlreadyPaid) + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb index bb7f0256b5..0d9d1cdbe0 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb @@ -1,7 +1,18 @@ ENV['RAILS_ENV'] = 'test' -$LOAD_PATH.push File.expand_path('../../../spec', __FILE__) -require File.expand_path('../../../config/environment', __FILE__) -require File.expand_path('../../../spec/rails_helper', __FILE__) - require_relative '../lib/orders' + +def arrange(stream, events, event_store: Orders.event_store) + event_store.append(events, stream_name: stream) +end + +def act(command, bus: Orders.command_bus) + bus.call(command) +end + +Configuration = Struct.new(:event_store, :command_bus, :number_generator) +Orders.setup(Configuration.new( + RailsEventStore::Client.new, + Arkency::CommandBus.new, + Orders::FakeNumberGenerator.new, +)) diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/submit_order_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/submit_order_spec.rb new file mode 100644 index 0000000000..48f2954a8a --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/submit_order_spec.rb @@ -0,0 +1,52 @@ +require_relative 'spec_helper' + +module Orders + RSpec.describe SubmitOrder do + let(:aggregate_id) { SecureRandom.uuid } + let(:stream) { "Orders::Order$#{aggregate_id}" } + let(:customer_id) { 997 } + let(:product_id) { 123 } + let(:order_number) { "2019/01/60" } + + it 'order is submitted' do + arrange(stream, [ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id})]) + act(SubmitOrder.new(order_id: aggregate_id, customer_id: customer_id)) + + expect(Orders.event_store).to have_published( + an_event(OrderSubmitted) + .with_data( + order_id: aggregate_id, + order_number: order_number, + customer_id: customer_id + ) + ) + end + + it 'could not create order where customer is not given' do + expect do + act(SubmitOrder.new(order_id: aggregate_id, customer_id: nil)) + end.to raise_error(Command::Invalid) + end + + it 'already created order could not be created again' do + another_customer_id = 998 + arrange(stream, [ + ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), + OrderSubmitted.new(data: {order_id: aggregate_id, order_number: order_number, customer_id: customer_id})]) + + expect do + act(SubmitOrder.new(order_id: aggregate_id, customer_id: another_customer_id)) + end.to raise_error(Order::AlreadySubmitted) + end + + it 'expired order could not be created' do + arrange(stream, [ + ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), + OrderExpired.new(data: {order_id: aggregate_id})]) + + expect do + act(SubmitOrder.new(order_id: aggregate_id, customer_id: customer_id)) + end.to raise_error(Order::OrderHasExpired) + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb index 1553f27671..47cbe0eec9 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb @@ -2,15 +2,23 @@ require_relative 'payments/application_record' require_relative '../../lib/event' +require_relative '../../lib/types' +require_relative '../../lib/command' +require_relative '../../lib/command_handler' + +require_dependency 'payments/payment_authorized' +require_dependency 'payments/payment_captured' +require_dependency 'payments/payment_released' +require_dependency 'payments/payment' module Payments def self.setup(config) @@public_event_store = config.event_store + @@command_bus = config.command_bus @@module_event_store = RailsEventStore::Client.new( repository: RailsEventStoreActiveRecord::EventRepository.new( Payments::ApplicationRecord), - mapper: RubyEventStore::Mappers::Default.new( - serializer: JSON, events_class_remapping: events_class_remapping) + mapper: RubyEventStore::Mappers::Default.new(serializer: JSON) ) # Subscribe public event handlers below @@ -19,9 +27,15 @@ def self.setup(config) end # Register commands handled by this module below - # config.command_bus.tap do |bus| - # bus.register(SubmitPayment, Payments::OnSubmit.new) - # end + config.command_bus.tap do |bus| + bus.register(Payments::AuthorizePayment, Payments::OnAuthorizePayment.new(event_store)) + bus.register(Payments::CapturePayment, Payments::OnCapturePayment.new(event_store)) + bus.register(Payments::ReleasePayment, Payments::OnReleasePayment.new(event_store)) + end + end + + def self.command_bus + @@command_bus end def self.public_event_store @@ -31,31 +45,4 @@ def self.public_event_store def self.event_store @@module_event_store end - - def self.events_class_remapping - { - 'order-submitted' => Initiated, - 'payment-authorized' => Authorized, - 'payment-authorization-failed' => AuthorizationFailed, - } - end - - class Initiated < Event - event_type 'order-submitted' - attribute :customer_id, Types::Strict::String - attribute :order_id, Types::Strict::String - attribute :total_amount, Types::Strict::Float - end - - class Authorized < Event - event_type 'payment-authorized' - attribute :transaction_id, Types::Strict::Integer - attribute :order_id, Types::Strict::String - end - - class AuthorizationFailed < Event - event_type 'payment-authorization-failed' - attribute :transaction_id, Types::Strict::Integer - attribute :order_id, Types::Strict::String - end end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/.keep b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/authorize_payment.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/authorize_payment.rb new file mode 100644 index 0000000000..ea172a7570 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/authorize_payment.rb @@ -0,0 +1,6 @@ +module Payments + class AuthorizePayment < Command + attribute :transaction_id, Types::Coercible::String + attribute :order_id, Types::UUID + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/capture_payment.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/capture_payment.rb new file mode 100644 index 0000000000..e210af707c --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/capture_payment.rb @@ -0,0 +1,5 @@ +module Payments + class CapturePayment < Command + attribute :transaction_id, Types::Coercible::String + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_authorize_payment.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_authorize_payment.rb new file mode 100644 index 0000000000..1835ba963a --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_authorize_payment.rb @@ -0,0 +1,11 @@ +module Payments + class OnAuthorizePayment + include CommandHandler + + def call(command) + with_aggregate(Payment.new, command.transaction_id) do |payment| + payment.authorize(command.transaction_id, command.order_id) + end + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_capture_payment.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_capture_payment.rb new file mode 100644 index 0000000000..fd7b5f3307 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_capture_payment.rb @@ -0,0 +1,11 @@ +module Payments + class OnCapturePayment + include CommandHandler + + def call(command) + with_aggregate(Payment.new, command.transaction_id) do |payment| + payment.capture + end + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_release_payment.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_release_payment.rb new file mode 100644 index 0000000000..022acbfc7c --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_release_payment.rb @@ -0,0 +1,11 @@ +module Payments + class OnReleasePayment + include CommandHandler + + def call(command) + with_aggregate(Payment.new, command.transaction_id) do |payment| + payment.release + end + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment.rb new file mode 100644 index 0000000000..83517fb1f6 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment.rb @@ -0,0 +1,66 @@ +module Payments + class Payment + include AggregateRoot + + AlreadyAuthorized = Class.new(StandardError) + NotAuthorized = Class.new(StandardError) + AlreadyCaptured = Class.new(StandardError) + AlreadyReleased = Class.new(StandardError) + + def authorize(transaction_id, order_id) + raise AlreadyAuthorized if authorized? + apply(PaymentAuthorized.new(data: { + transaction_id: transaction_id, + order_id: order_id + })) + end + + def capture + raise AlreadyCaptured if captured? + raise NotAuthorized unless authorized? + apply(PaymentCaptured.new(data: { + transaction_id: @transaction_id, + order_id: @order_id + })) + end + + def release + raise AlreadyReleased if released? + raise AlreadyCaptured if captured? + raise NotAuthorized unless authorized? + apply(PaymentReleased.new(data: { + transaction_id: @transaction_id, + order_id: @order_id + })) + end + + private + + on PaymentAuthorized do |event| + @state = :authorized + @transaction_id = event.data.fetch(:transaction_id) + @order_id = event.data.fetch(:order_id) + end + + on PaymentCaptured do |event| + @state = :captured + end + + on PaymentReleased do |event| + @state = :released + end + + def authorized? + @state == :authorized + end + + def captured? + @state == :captured + end + + def released? + @state == :released + end + end +end + diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_authorized.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_authorized.rb new file mode 100644 index 0000000000..3dbbcf3eea --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_authorized.rb @@ -0,0 +1,6 @@ +module Payments + class PaymentAuthorized < Event + attribute :order_id, Types::UUID + attribute :transaction_id, Types::TransactionId + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_captured.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_captured.rb new file mode 100644 index 0000000000..07e1cf45c1 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_captured.rb @@ -0,0 +1,6 @@ +module Payments + class PaymentCaptured < Event + attribute :order_id, Types::UUID + attribute :transaction_id, Types::TransactionId + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_released.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_released.rb new file mode 100644 index 0000000000..01d8e13805 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_released.rb @@ -0,0 +1,6 @@ +module Payments + class PaymentReleased < Event + attribute :order_id, Types::UUID + attribute :transaction_id, Types::TransactionId + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/release_payment.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/release_payment.rb new file mode 100644 index 0000000000..6e7ab47ed6 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/release_payment.rb @@ -0,0 +1,5 @@ +module Payments + class ReleasePayment < Command + attribute :transaction_id, Types::Coercible::String + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/on_authorize_payment_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/on_authorize_payment_spec.rb new file mode 100644 index 0000000000..b54e1052dc --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/on_authorize_payment_spec.rb @@ -0,0 +1,18 @@ +require_relative 'spec_helper' + +module Payments + RSpec.describe OnAuthorizePayment do + it 'authorize payment' do + transaction_id = SecureRandom.hex(16) + order_id = SecureRandom.uuid + + act(AuthorizePayment.new(transaction_id: transaction_id, order_id: order_id)) + + expect(Payments.event_store).to have_published( + an_event(PaymentAuthorized) + .with_data(transaction_id: transaction_id, order_id: order_id) + ) + end + end +end + diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/on_capture_payment_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/on_capture_payment_spec.rb new file mode 100644 index 0000000000..3976ba4148 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/on_capture_payment_spec.rb @@ -0,0 +1,19 @@ +require_relative 'spec_helper' + +module Payments + RSpec.describe OnCapturePayment do + it 'capture payment' do + transaction_id = SecureRandom.hex(16) + order_id = SecureRandom.uuid + stream = "Payments::Payment$#{transaction_id}" + + arrange(stream, [PaymentAuthorized.new(data: {transaction_id: transaction_id, order_id: order_id})]) + act(CapturePayment.new(transaction_id: transaction_id, order_id: order_id)) + + expect(Payments.event_store).to have_published( + an_event(PaymentCaptured) + .with_data(transaction_id: transaction_id, order_id: order_id) + ) + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/on_release_payment_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/on_release_payment_spec.rb new file mode 100644 index 0000000000..8911370d28 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/on_release_payment_spec.rb @@ -0,0 +1,19 @@ +require_relative 'spec_helper' + +module Payments + RSpec.describe OnReleasePayment do + it 'capture payment' do + transaction_id = SecureRandom.hex(16) + order_id = SecureRandom.uuid + stream = "Payments::Payment$#{transaction_id}" + + arrange(stream, [PaymentAuthorized.new(data: {transaction_id: transaction_id, order_id: order_id})]) + act(ReleasePayment.new(transaction_id: transaction_id, order_id: order_id)) + + expect(Payments.event_store).to have_published( + an_event(PaymentReleased) + .with_data(transaction_id: transaction_id, order_id: order_id) + ) + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/payment_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/payment_spec.rb new file mode 100644 index 0000000000..e65d0755c2 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/payment_spec.rb @@ -0,0 +1,109 @@ +require_relative 'spec_helper' + +module Payments + RSpec.describe Payment do + it 'authorize' do + payment = Payment.new + expect do + payment.authorize(transaction_id, order_id) + end.to apply( + an_event(PaymentAuthorized) + .with_data(transaction_id: transaction_id, order_id: order_id) + ).in(payment) + end + + it 'should not allow for double authorization' do + expect do + authorized_payment.authorize(transaction_id, order_id) + end.to raise_error(Payment::AlreadyAuthorized) + end + + it 'should capture authorized payment' do + payment = authorized_payment + + expect do + payment.capture + end.to apply( + an_event(PaymentCaptured) + .with_data(transaction_id: transaction_id, order_id: order_id) + ).in(payment) + end + + it 'must not capture not authorized payment' do + expect do + Payment.new.capture + end.to raise_error(Payment::NotAuthorized) + end + + it 'should not allow for double capture' do + expect do + captured_payment.capture + end.to raise_error(Payment::AlreadyCaptured) + end + + it 'authorization could be released' do + payment = authorized_payment + + expect do + payment.release + end.to apply( + an_event(PaymentReleased) + .with_data(transaction_id: transaction_id, order_id: order_id) + ).in(payment) + end + + it 'must not release not captured payment' do + expect do + captured_payment.release + end.to raise_error(Payment::AlreadyCaptured) + end + + it 'must not release not authorized payment' do + expect do + Payment.new.release + end.to raise_error(Payment::NotAuthorized) + end + + it 'should not allow for double release' do + expect do + released_payment.release + end.to raise_error(Payment::AlreadyReleased) + end + + let(:transaction_id) { SecureRandom.hex(16) } + let(:order_id) { SecureRandom.uuid } + + def authorized_payment + Payment.new.tap do |payment| + payment.apply( + PaymentAuthorized.new(data: { + transaction_id: transaction_id, + order_id: order_id, + }) + ) + end + end + + def captured_payment + authorized_payment.tap do |payment| + payment.apply( + PaymentCaptured.new(data: { + transaction_id: transaction_id, + order_id: order_id, + }) + ) + end + end + + def released_payment + captured_payment.tap do |payment| + payment.apply( + PaymentReleased.new(data: { + transaction_id: transaction_id, + order_id: order_id, + }) + ) + end + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/payments_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/payments_spec.rb deleted file mode 100644 index 4963dd47bf..0000000000 --- a/contrib/multiple_databases_repository_sample_app/payments/spec/payments_spec.rb +++ /dev/null @@ -1,4 +0,0 @@ -require_relative 'spec_helper' - -RSpec.describe Payments do -end diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb index b50e3a2b33..c108ee5888 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb @@ -1,7 +1,17 @@ ENV['RAILS_ENV'] = 'test' -$LOAD_PATH.push File.expand_path('../../../spec', __FILE__) -require File.expand_path('../../../config/environment', __FILE__) -require File.expand_path('../../../spec/rails_helper', __FILE__) - require_relative '../lib/payments' + +def arrange(stream, events, event_store: Payments.event_store) + event_store.append(events, stream_name: stream) +end + +def act(command, bus: Orders.command_bus) + bus.call(command) +end + +Configuration = Struct.new(:event_store, :command_bus) +Payments.setup(Configuration.new( + RailsEventStore::Client.new, + Arkency::CommandBus.new, +)) From 6d5046ba5bfd8e8253e1ec0a99520bac3507ec62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Mon, 8 Jun 2020 20:32:55 +0200 Subject: [PATCH 20/35] Allow to define separate event class remapping for each BC and APP --- .../config/initializers/rails_event_store.rb | 12 +++++++++++- .../orders/lib/orders.rb | 16 +++++++++++++--- .../orders/spec/spec_helper.rb | 4 ++-- .../payments/lib/payments.rb | 16 +++++++++++++--- .../payments/spec/spec_helper.rb | 4 ++-- 5 files changed, 41 insertions(+), 11 deletions(-) diff --git a/contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb b/contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb index 9436d5c2f6..fdd7fc84bc 100644 --- a/contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb +++ b/contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb @@ -3,7 +3,17 @@ require 'arkency/command_bus' Rails.configuration.to_prepare do - Rails.configuration.event_store = RailsEventStore::Client.new + events_class_remapping = {} + + Rails.configuration.event_repository = + RailsEventStoreActiveRecord::EventRepository.new + Rails.configuration.event_store = RailsEventStore::Client.new( + repository: Rails.configuration.event_repository, + mapper: RubyEventStore::Mappers::Default.new( + serializer: JSON, + events_class_remapping: events_class_remapping + ) + ) Rails.configuration.command_bus = Arkency::CommandBus.new AggregateRoot.configure do |config| diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb index 42e36f688b..780c4a90a6 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb @@ -28,8 +28,14 @@ module Orders def self.setup(config) - @@public_event_store = config.event_store @@command_bus = config.command_bus + @@public_event_store = RailsEventStore::Client.new( + repository: config.event_repository, + mapper: RubyEventStore::Mappers::Default.new( + serializer: JSON, + events_class_remapping: events_class_remapping + ) + ) @@module_event_store = RailsEventStore::Client.new( repository: RailsEventStoreActiveRecord::EventRepository.new( Orders::ApplicationRecord), @@ -37,13 +43,13 @@ def self.setup(config) ) # Subscribe public event handlers below - config.event_store.tap do |store| + public_event_store.tap do |store| # store.subscribe(MarkAsPaid.new, to: [Paid]) # store.subscribe(RequestPayment.new, to: [PaymentFailed]) end # Register commands handled by this module below - config.command_bus.tap do |bus| + command_bus.tap do |bus| bus.register(Orders::SubmitOrder, Orders::OnSubmitOrder.new(event_store, number_generator: config.number_generator)) bus.register(Orders::SetOrderAsExpired, Orders::OnSetOrderAsExpired.new(event_store)) bus.register(Orders::MarkOrderAsPaid, Orders::OnMarkOrderAsPaid.new(event_store)) @@ -52,6 +58,10 @@ def self.setup(config) end end + def self.events_class_remapping + {} + end + def self.command_bus @@command_bus end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb index 0d9d1cdbe0..66f8fd59de 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb @@ -10,9 +10,9 @@ def act(command, bus: Orders.command_bus) bus.call(command) end -Configuration = Struct.new(:event_store, :command_bus, :number_generator) +Configuration = Struct.new(:event_repository, :command_bus, :number_generator) Orders.setup(Configuration.new( - RailsEventStore::Client.new, + RubyEventStore::InMemoryRepository.new, Arkency::CommandBus.new, Orders::FakeNumberGenerator.new, )) diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb index 47cbe0eec9..1fa328598f 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb @@ -13,8 +13,14 @@ module Payments def self.setup(config) - @@public_event_store = config.event_store @@command_bus = config.command_bus + @@public_event_store = RailsEventStore::Client.new( + repository: config.event_repository, + mapper: RubyEventStore::Mappers::Default.new( + serializer: JSON, + events_class_remapping: events_class_remapping + ) + ) @@module_event_store = RailsEventStore::Client.new( repository: RailsEventStoreActiveRecord::EventRepository.new( Payments::ApplicationRecord), @@ -22,18 +28,22 @@ def self.setup(config) ) # Subscribe public event handlers below - config.event_store.tap do |store| + public_event_store.tap do |store| # store.subscribe(Authorize.new, to: [Initiated]) end # Register commands handled by this module below - config.command_bus.tap do |bus| + command_bus.tap do |bus| bus.register(Payments::AuthorizePayment, Payments::OnAuthorizePayment.new(event_store)) bus.register(Payments::CapturePayment, Payments::OnCapturePayment.new(event_store)) bus.register(Payments::ReleasePayment, Payments::OnReleasePayment.new(event_store)) end end + def self.events_class_remapping + {} + end + def self.command_bus @@command_bus end diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb index c108ee5888..67da628d57 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb @@ -10,8 +10,8 @@ def act(command, bus: Orders.command_bus) bus.call(command) end -Configuration = Struct.new(:event_store, :command_bus) +Configuration = Struct.new(:event_repository, :command_bus) Payments.setup(Configuration.new( - RailsEventStore::Client.new, + RubyEventStore::InMemoryRepository.new, Arkency::CommandBus.new, )) From 3910e4c84bcba6ef20dc259dccb22ecbc5b86526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Wed, 17 Jun 2020 11:39:46 +0200 Subject: [PATCH 21/35] Do not override definition of spec helpers methods in BCs --- .../orders/lib/orders.rb | 4 +++ .../orders/spec/add_item_to_basket_spec.rb | 6 ++--- .../orders/spec/mark_order_as_paid_spec.rb | 12 ++++----- .../spec/remove_item_from_basket_spec.rb | 8 +++--- .../orders/spec/set_order_as_expired_spec.rb | 12 ++++----- .../orders/spec/spec_helper.rb | 26 +++++++++++-------- .../orders/spec/submit_order_spec.rb | 14 +++++----- .../payments/lib/payments.rb | 4 +++ .../spec/on_authorize_payment_spec.rb | 2 +- .../payments/spec/on_capture_payment_spec.rb | 4 +-- .../payments/spec/on_release_payment_spec.rb | 4 +-- .../payments/spec/spec_helper.rb | 24 ++++++++++------- 12 files changed, 68 insertions(+), 52 deletions(-) diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb index 780c4a90a6..89fd872e2f 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb @@ -73,4 +73,8 @@ def self.public_event_store def self.event_store @@module_event_store end + + def self.setup? + command_bus && event_store && public_event_store + end end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/add_item_to_basket_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/add_item_to_basket_spec.rb index 5da88dcb7a..9fffd34763 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/spec/add_item_to_basket_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/add_item_to_basket_spec.rb @@ -9,7 +9,7 @@ module Orders let(:order_number) { "2019/01/60" } it 'item is added to draft order' do - act(AddItemToBasket.new(order_id: aggregate_id, product_id: product_id)) + Orders.act(AddItemToBasket.new(order_id: aggregate_id, product_id: product_id)) expect(Orders.event_store).to have_published( an_event(ItemAddedToBasket) .with_data(order_id: aggregate_id, product_id: product_id) @@ -17,12 +17,12 @@ module Orders end it 'no add allowed to submitted order' do - arrange(stream, [ + Orders.arrange(stream, [ ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), OrderSubmitted.new(data: {order_id: aggregate_id, order_number: order_number, customer_id: customer_id})]) expect do - act(AddItemToBasket.new(order_id: aggregate_id, product_id: product_id)) + Orders.act(AddItemToBasket.new(order_id: aggregate_id, product_id: product_id)) end.to raise_error(Order::AlreadySubmitted) end end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/mark_order_as_paid_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/mark_order_as_paid_spec.rb index 258158e70e..83611b20d3 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/spec/mark_order_as_paid_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/mark_order_as_paid_spec.rb @@ -9,21 +9,21 @@ module Orders let(:order_number) { "2019/01/60" } it 'draft order could not be marked as paid' do - arrange(stream, [ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id})]) + Orders.arrange(stream, [ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id})]) expect do - act(MarkOrderAsPaid.new(order_id: aggregate_id, transaction_id: SecureRandom.hex(16))) + Orders.act(MarkOrderAsPaid.new(order_id: aggregate_id, transaction_id: SecureRandom.hex(16))) end.to raise_error(Order::NotSubmitted) end it 'submitted order will be marked as paid' do - arrange(stream, [ + Orders.arrange(stream, [ ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), OrderSubmitted.new(data: {order_id: aggregate_id, order_number: '2018/12/1', customer_id: customer_id}), ]) transaction_id = SecureRandom.hex(16) - act(MarkOrderAsPaid.new(order_id: aggregate_id, transaction_id: transaction_id)) + Orders.act(MarkOrderAsPaid.new(order_id: aggregate_id, transaction_id: transaction_id)) expect(Orders.event_store).to have_published( an_event(OrderPaid) @@ -32,14 +32,14 @@ module Orders end it 'expired order cannot be marked as paid' do - arrange(stream, [ + Orders.arrange(stream, [ ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), OrderSubmitted.new(data: {order_id: aggregate_id, order_number: '2018/12/1', customer_id: customer_id}), OrderExpired.new(data: {order_id: aggregate_id}), ]) expect do - act(MarkOrderAsPaid.new(order_id: aggregate_id, transaction_id: SecureRandom.hex(16))) + Orders.act(MarkOrderAsPaid.new(order_id: aggregate_id, transaction_id: SecureRandom.hex(16))) end.to raise_error(Order::OrderHasExpired) end end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/remove_item_from_basket_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/remove_item_from_basket_spec.rb index b62c256116..6b8e20b3cc 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/spec/remove_item_from_basket_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/remove_item_from_basket_spec.rb @@ -9,8 +9,8 @@ module Orders let(:order_number) { "2019/01/60" } it 'item is removed from draft order' do - arrange(stream, [ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id})]) - act(RemoveItemFromBasket.new(order_id: aggregate_id, product_id: product_id)) + Orders.arrange(stream, [ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id})]) + Orders.act(RemoveItemFromBasket.new(order_id: aggregate_id, product_id: product_id)) expect(Orders.event_store).to have_published( an_event(ItemRemovedFromBasket) .with_data(order_id: aggregate_id, product_id: product_id) @@ -18,12 +18,12 @@ module Orders end it 'no remove allowed to created order' do - arrange(stream, [ + Orders.arrange(stream, [ ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), OrderSubmitted.new(data: {order_id: aggregate_id, order_number: order_number, customer_id: customer_id})]) expect do - act(RemoveItemFromBasket.new(order_id: aggregate_id, product_id: product_id)) + Orders.act(RemoveItemFromBasket.new(order_id: aggregate_id, product_id: product_id)) end.to raise_error(Order::AlreadySubmitted) end end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/set_order_as_expired_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/set_order_as_expired_spec.rb index 9ea01cf08a..e11a766ff2 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/spec/set_order_as_expired_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/set_order_as_expired_spec.rb @@ -9,9 +9,9 @@ module Orders let(:order_number) { "2019/01/60" } it 'draft order will expire' do - arrange(stream, [ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id})]) + Orders.arrange(stream, [ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id})]) - act(SetOrderAsExpired.new(order_id: aggregate_id)) + Orders.act(SetOrderAsExpired.new(order_id: aggregate_id)) expect(Orders.event_store).to have_published( an_event(OrderExpired) @@ -22,12 +22,12 @@ module Orders end it 'submitted order will expire' do - arrange(stream, [ + Orders.arrange(stream, [ ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), OrderSubmitted.new(data: {order_id: aggregate_id, order_number: '2018/12/1', customer_id: customer_id}), ]) - act(SetOrderAsExpired.new(order_id: aggregate_id)) + Orders.act(SetOrderAsExpired.new(order_id: aggregate_id)) expect(Orders.event_store).to have_published( an_event(OrderExpired) @@ -38,14 +38,14 @@ module Orders end it 'paid order cannot expire' do - arrange(stream, [ + Orders.arrange(stream, [ ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), OrderSubmitted.new(data: {order_id: aggregate_id, order_number: '2018/12/1', customer_id: customer_id}), OrderPaid.new(data: {order_id: aggregate_id, transaction_id: SecureRandom.hex(16)}), ]) expect do - act(SetOrderAsExpired.new(order_id: aggregate_id)) + Orders.act(SetOrderAsExpired.new(order_id: aggregate_id)) end.to raise_error(Order::AlreadyPaid) end end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb index 66f8fd59de..52f55c62d3 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb @@ -2,17 +2,21 @@ require_relative '../lib/orders' -def arrange(stream, events, event_store: Orders.event_store) - event_store.append(events, stream_name: stream) -end +module Orders + def self.arrange(stream, events, event_store: Orders.event_store) + event_store.append(events, stream_name: stream) + end -def act(command, bus: Orders.command_bus) - bus.call(command) + def self.act(command, bus: Orders.command_bus) + bus.call(command) + end end -Configuration = Struct.new(:event_repository, :command_bus, :number_generator) -Orders.setup(Configuration.new( - RubyEventStore::InMemoryRepository.new, - Arkency::CommandBus.new, - Orders::FakeNumberGenerator.new, -)) +unless Orders.setup? + Configuration = Struct.new(:event_repository, :command_bus, :number_generator) + Orders.setup(Configuration.new( + RubyEventStore::InMemoryRepository.new, + Arkency::CommandBus.new, + Orders::FakeNumberGenerator.new, + )) +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/submit_order_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/submit_order_spec.rb index 48f2954a8a..a8aa2d71d3 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/spec/submit_order_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/submit_order_spec.rb @@ -9,8 +9,8 @@ module Orders let(:order_number) { "2019/01/60" } it 'order is submitted' do - arrange(stream, [ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id})]) - act(SubmitOrder.new(order_id: aggregate_id, customer_id: customer_id)) + Orders.arrange(stream, [ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id})]) + Orders.act(SubmitOrder.new(order_id: aggregate_id, customer_id: customer_id)) expect(Orders.event_store).to have_published( an_event(OrderSubmitted) @@ -24,28 +24,28 @@ module Orders it 'could not create order where customer is not given' do expect do - act(SubmitOrder.new(order_id: aggregate_id, customer_id: nil)) + Orders.act(SubmitOrder.new(order_id: aggregate_id, customer_id: nil)) end.to raise_error(Command::Invalid) end it 'already created order could not be created again' do another_customer_id = 998 - arrange(stream, [ + Orders.arrange(stream, [ ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), OrderSubmitted.new(data: {order_id: aggregate_id, order_number: order_number, customer_id: customer_id})]) expect do - act(SubmitOrder.new(order_id: aggregate_id, customer_id: another_customer_id)) + Orders.act(SubmitOrder.new(order_id: aggregate_id, customer_id: another_customer_id)) end.to raise_error(Order::AlreadySubmitted) end it 'expired order could not be created' do - arrange(stream, [ + Orders.arrange(stream, [ ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), OrderExpired.new(data: {order_id: aggregate_id})]) expect do - act(SubmitOrder.new(order_id: aggregate_id, customer_id: customer_id)) + Orders.act(SubmitOrder.new(order_id: aggregate_id, customer_id: customer_id)) end.to raise_error(Order::OrderHasExpired) end end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb index 1fa328598f..20882bd559 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb @@ -55,4 +55,8 @@ def self.public_event_store def self.event_store @@module_event_store end + + def self.setup? + command_bus && event_store && public_event_store + end end diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/on_authorize_payment_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/on_authorize_payment_spec.rb index b54e1052dc..eb16e79bb1 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/spec/on_authorize_payment_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/on_authorize_payment_spec.rb @@ -6,7 +6,7 @@ module Payments transaction_id = SecureRandom.hex(16) order_id = SecureRandom.uuid - act(AuthorizePayment.new(transaction_id: transaction_id, order_id: order_id)) + Payments.act(AuthorizePayment.new(transaction_id: transaction_id, order_id: order_id)) expect(Payments.event_store).to have_published( an_event(PaymentAuthorized) diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/on_capture_payment_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/on_capture_payment_spec.rb index 3976ba4148..121a41cb47 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/spec/on_capture_payment_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/on_capture_payment_spec.rb @@ -7,8 +7,8 @@ module Payments order_id = SecureRandom.uuid stream = "Payments::Payment$#{transaction_id}" - arrange(stream, [PaymentAuthorized.new(data: {transaction_id: transaction_id, order_id: order_id})]) - act(CapturePayment.new(transaction_id: transaction_id, order_id: order_id)) + Payments.arrange(stream, [PaymentAuthorized.new(data: {transaction_id: transaction_id, order_id: order_id})]) + Payments.act(CapturePayment.new(transaction_id: transaction_id, order_id: order_id)) expect(Payments.event_store).to have_published( an_event(PaymentCaptured) diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/on_release_payment_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/on_release_payment_spec.rb index 8911370d28..98a0df16ef 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/spec/on_release_payment_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/on_release_payment_spec.rb @@ -7,8 +7,8 @@ module Payments order_id = SecureRandom.uuid stream = "Payments::Payment$#{transaction_id}" - arrange(stream, [PaymentAuthorized.new(data: {transaction_id: transaction_id, order_id: order_id})]) - act(ReleasePayment.new(transaction_id: transaction_id, order_id: order_id)) + Payments.arrange(stream, [PaymentAuthorized.new(data: {transaction_id: transaction_id, order_id: order_id})]) + Payments.act(ReleasePayment.new(transaction_id: transaction_id, order_id: order_id)) expect(Payments.event_store).to have_published( an_event(PaymentReleased) diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb index 67da628d57..7b9de1b545 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb @@ -2,16 +2,20 @@ require_relative '../lib/payments' -def arrange(stream, events, event_store: Payments.event_store) - event_store.append(events, stream_name: stream) -end +module Payments + def self.arrange(stream, events, event_store: Payments.event_store) + event_store.append(events, stream_name: stream) + end -def act(command, bus: Orders.command_bus) - bus.call(command) + def self.act(command, bus: Payments.command_bus) + bus.call(command) + end end -Configuration = Struct.new(:event_repository, :command_bus) -Payments.setup(Configuration.new( - RubyEventStore::InMemoryRepository.new, - Arkency::CommandBus.new, -)) +unless Payments.setup? + Configuration = Struct.new(:event_repository, :command_bus) + Payments.setup(Configuration.new( + RubyEventStore::InMemoryRepository.new, + Arkency::CommandBus.new, + )) +end From f7c3c4a3d216b9615e464ababc3c6d53254e6917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Wed, 17 Jun 2020 13:19:51 +0200 Subject: [PATCH 22/35] Factory instead of instance Because of Rails initialization process. No database configurations ready when we need to initialize new number generator instance. This way (by using factory) we delay it's initialization to the moment when it is really needed. And at this time database is ready waiting for connections :) --- .../config/environments/development.rb | 2 +- .../config/environments/production.rb | 2 +- .../config/environments/test.rb | 2 +- .../orders/lib/orders.rb | 2 +- .../orders/lib/orders/on_submit_order.rb | 4 ++-- .../orders/spec/spec_helper.rb | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/contrib/multiple_databases_repository_sample_app/config/environments/development.rb b/contrib/multiple_databases_repository_sample_app/config/environments/development.rb index bd72d8c4c1..7eafa4acaf 100644 --- a/contrib/multiple_databases_repository_sample_app/config/environments/development.rb +++ b/contrib/multiple_databases_repository_sample_app/config/environments/development.rb @@ -45,5 +45,5 @@ # routes, locales, etc. This feature depends on the listen gem. # config.file_watcher = ActiveSupport::EventedFileUpdateChecker - config.number_generator = nil # Orders::NumberGenerator.new + config.number_generator_factory = ->{ Orders::NumberGenerator.new } end diff --git a/contrib/multiple_databases_repository_sample_app/config/environments/production.rb b/contrib/multiple_databases_repository_sample_app/config/environments/production.rb index 082c9847fb..adfbaa8a5a 100644 --- a/contrib/multiple_databases_repository_sample_app/config/environments/production.rb +++ b/contrib/multiple_databases_repository_sample_app/config/environments/production.rb @@ -90,5 +90,5 @@ # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session - config.number_generator = nil # Orders::NumberGenerator.new + config.number_generator_factory = -> { Orders::NumberGenerator.new } end diff --git a/contrib/multiple_databases_repository_sample_app/config/environments/test.rb b/contrib/multiple_databases_repository_sample_app/config/environments/test.rb index 097317dbe3..1308f3401a 100644 --- a/contrib/multiple_databases_repository_sample_app/config/environments/test.rb +++ b/contrib/multiple_databases_repository_sample_app/config/environments/test.rb @@ -36,5 +36,5 @@ # Raises error for missing translations. # config.action_view.raise_on_missing_translations = true - config.number_generator = nil # Orders::FakeNumberGenerator.new + config.number_generator_factory = ->{ Orders::FakeNumberGenerator.new } end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb index 89fd872e2f..f4bb10d002 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb @@ -50,7 +50,7 @@ def self.setup(config) # Register commands handled by this module below command_bus.tap do |bus| - bus.register(Orders::SubmitOrder, Orders::OnSubmitOrder.new(event_store, number_generator: config.number_generator)) + bus.register(Orders::SubmitOrder, Orders::OnSubmitOrder.new(event_store, number_generator_factory: config.number_generator_factory)) bus.register(Orders::SetOrderAsExpired, Orders::OnSetOrderAsExpired.new(event_store)) bus.register(Orders::MarkOrderAsPaid, Orders::OnMarkOrderAsPaid.new(event_store)) bus.register(Orders::AddItemToBasket, Orders::OnAddItemToBasket.new(event_store)) diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_submit_order.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_submit_order.rb index b7281e0dbe..a5a6ce549e 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_submit_order.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_submit_order.rb @@ -2,12 +2,12 @@ module Orders class OnSubmitOrder include CommandHandler - def initialize(event_store, number_generator:) + def initialize(event_store, number_generator_factory:) @repository = AggregateRoot::InstrumentedRepository.new( AggregateRoot::Repository.new(event_store), ActiveSupport::Notifications ) - @number_generator = number_generator + @number_generator = number_generator_factory.call end def call(command) diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb index 52f55c62d3..6f6b503cfc 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/spec_helper.rb @@ -13,10 +13,10 @@ def self.act(command, bus: Orders.command_bus) end unless Orders.setup? - Configuration = Struct.new(:event_repository, :command_bus, :number_generator) + Configuration = Struct.new(:event_repository, :command_bus, :number_generator_factory) Orders.setup(Configuration.new( RubyEventStore::InMemoryRepository.new, Arkency::CommandBus.new, - Orders::FakeNumberGenerator.new, + ->{ Orders::FakeNumberGenerator.new }, )) end From 10de969a02092388c0ce03bbf3474159cfbab17f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 18 Jun 2020 18:39:11 +0200 Subject: [PATCH 23/35] Redesigned sample app process, added event storming board to readme --- .../README.md | 25 ++------ .../orders/lib/orders.rb | 45 +++++++++----- .../lib/orders/on_mark_order_as_paid.rb | 11 ---- .../lib/orders/on_set_order_as_expired.rb | 11 ---- .../orders/lib/orders/on_submit_order.rb | 7 ++- .../orders/lib/orders/order.rb | 32 +++------- .../orders/lib/orders/order_expired.rb | 5 -- .../orders/lib/orders/order_paid.rb | 6 -- .../orders/lib/orders/order_submitted.rb | 6 +- .../lib/orders/prepare_order_process.rb | 62 +++++++++++++++++++ .../orders/lib/orders/set_order_as_expired.rb | 5 -- .../orders/lib/orders/submit_order.rb | 2 + .../orders/spec/add_item_to_basket_spec.rb | 11 +++- .../orders/spec/mark_order_as_paid_spec.rb | 46 -------------- .../spec/remove_item_from_basket_spec.rb | 11 +++- .../orders/spec/set_order_as_expired_spec.rb | 52 ---------------- .../orders/spec/submit_order_spec.rb | 46 +++++++++----- .../payments/lib/payments.rb | 30 ++++++++- .../lib/payments/complete_payment.rb} | 6 +- .../payments/lib/payments/initiate_payment.rb | 14 +++++ .../lib/payments/on_complete_payment.rb | 14 +++++ .../payments/lib/payments/payment_expired.rb | 5 ++ .../payments/lib/payments/payment_process.rb | 61 ++++++++++++++++++ 23 files changed, 291 insertions(+), 222 deletions(-) delete mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_mark_order_as_paid.rb delete mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_set_order_as_expired.rb delete mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_expired.rb delete mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_paid.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/prepare_order_process.rb delete mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/set_order_as_expired.rb delete mode 100644 contrib/multiple_databases_repository_sample_app/orders/spec/mark_order_as_paid_spec.rb delete mode 100644 contrib/multiple_databases_repository_sample_app/orders/spec/set_order_as_expired_spec.rb rename contrib/multiple_databases_repository_sample_app/{orders/lib/orders/mark_order_as_paid.rb => payments/lib/payments/complete_payment.rb} (67%) create mode 100644 contrib/multiple_databases_repository_sample_app/payments/lib/payments/initiate_payment.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_complete_payment.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_expired.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_process.rb diff --git a/contrib/multiple_databases_repository_sample_app/README.md b/contrib/multiple_databases_repository_sample_app/README.md index 7db80e4ca1..b33bb459e2 100644 --- a/contrib/multiple_databases_repository_sample_app/README.md +++ b/contrib/multiple_databases_repository_sample_app/README.md @@ -1,24 +1,7 @@ -# README +# Domain description -This README would normally document whatever steps are necessary to get the -application up and running. +TDB -Things you may want to cover: +# Event storming -* Ruby version - -* System dependencies - -* Configuration - -* Database creation - -* Database initialization - -* How to run the test suite - -* Services (job queues, cache servers, search engines, etc.) - -* Deployment instructions - -* ... + diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb index f4bb10d002..9fcb6389d2 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb @@ -7,24 +7,21 @@ require_relative '../../lib/command_handler' require_dependency 'orders/add_item_to_basket' -require_dependency 'orders/fake_number_generator' +require_dependency 'orders/remove_item_from_basket' +require_dependency 'orders/submit_order' + require_dependency 'orders/item_added_to_basket' require_dependency 'orders/item_removed_from_basket' -require_dependency 'orders/number_generator' +require_dependency 'orders/order_submitted' + require_dependency 'orders/on_add_item_to_basket' require_dependency 'orders/on_remove_item_from_basket' -require_dependency 'orders/on_set_order_as_expired' -require_dependency 'orders/on_mark_order_as_paid' require_dependency 'orders/on_submit_order' + require_dependency 'orders/order' -require_dependency 'orders/order_expired' -require_dependency 'orders/order_paid' require_dependency 'orders/order_line' -require_dependency 'orders/order_submitted' -require_dependency 'orders/remove_item_from_basket' -require_dependency 'orders/set_order_as_expired' -require_dependency 'orders/mark_order_as_paid' -require_dependency 'orders/submit_order' +require_dependency 'orders/number_generator' +require_dependency 'orders/fake_number_generator' module Orders def self.setup(config) @@ -44,22 +41,29 @@ def self.setup(config) # Subscribe public event handlers below public_event_store.tap do |store| -# store.subscribe(MarkAsPaid.new, to: [Paid]) -# store.subscribe(RequestPayment.new, to: [PaymentFailed]) + end + + # Subscribe private event handlers below + event_store.tap do |store| + store.subscribe(PrepareOrderProcess.new(store, command_bus), to: [ + ItemAddedToBasket, + ItemRemovedFromBasket, + OrderSubmitted, + ]) end # Register commands handled by this module below command_bus.tap do |bus| bus.register(Orders::SubmitOrder, Orders::OnSubmitOrder.new(event_store, number_generator_factory: config.number_generator_factory)) - bus.register(Orders::SetOrderAsExpired, Orders::OnSetOrderAsExpired.new(event_store)) - bus.register(Orders::MarkOrderAsPaid, Orders::OnMarkOrderAsPaid.new(event_store)) bus.register(Orders::AddItemToBasket, Orders::OnAddItemToBasket.new(event_store)) bus.register(Orders::RemoveItemFromBasket, Orders::OnRemoveItemFromBasket.new(event_store)) end end def self.events_class_remapping - {} + { + 'new-order' => OrderPlaced, + } end def self.command_bus @@ -77,4 +81,13 @@ def self.event_store def self.setup? command_bus && event_store && public_event_store end + + class OrderPlaced < Event + event_type 'new-order' + attribute :order_id, Types::UUID + attribute :customer_id, Types::ID + attribute :delivery_address_id, Types::ID + attribute :payment_method_id, Types::ID + attribute :amount, Types::Decimal + end end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_mark_order_as_paid.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_mark_order_as_paid.rb deleted file mode 100644 index 79bd72b7a8..0000000000 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_mark_order_as_paid.rb +++ /dev/null @@ -1,11 +0,0 @@ -module Orders - class OnMarkOrderAsPaid - include CommandHandler - - def call(command) - with_aggregate(Order.new(command.order_id), command.order_id) do |order| - order.mark_as_paid(command.transaction_id) - end - end - end -end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_set_order_as_expired.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_set_order_as_expired.rb deleted file mode 100644 index 9e8f29d41d..0000000000 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_set_order_as_expired.rb +++ /dev/null @@ -1,11 +0,0 @@ -module Orders - class OnSetOrderAsExpired - include CommandHandler - - def call(command) - with_aggregate(Order.new(command.order_id), command.order_id) do |order| - order.expire - end - end - end -end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_submit_order.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_submit_order.rb index a5a6ce549e..1b872aa019 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_submit_order.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/on_submit_order.rb @@ -13,7 +13,12 @@ def initialize(event_store, number_generator_factory:) def call(command) with_aggregate(Order.new(command.order_id), command.order_id) do |order| order_number = number_generator.call - order.submit(order_number, command.customer_id) + order.submit( + order_number, + command.customer_id, + command.delivery_address_id, + command.payment_method_id + ) end end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order.rb index ca405edf5c..7389784ba3 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order.rb @@ -5,9 +5,7 @@ class Order include AggregateRoot AlreadySubmitted = Class.new(StandardError) - AlreadyPaid = Class.new(StandardError) NotSubmitted = Class.new(StandardError) - OrderHasExpired = Class.new(StandardError) MissingCustomer = Class.new(StandardError) def initialize(id) @@ -16,22 +14,16 @@ def initialize(id) @order_lines = [] end - def submit(order_number, customer_id) + def submit(order_number, customer_id, delivery_address_id, payment_method_id) raise AlreadySubmitted if @state == :submitted - raise OrderHasExpired if @state == :expired raise MissingCustomer unless customer_id - apply OrderSubmitted.new(data: {order_id: @id, order_number: order_number, customer_id: customer_id}) - end - - def mark_as_paid(transaction_id) - raise OrderHasExpired if @state == :expired - raise NotSubmitted unless @state == :submitted - apply OrderPaid.new(data: {order_id: @id, transaction_id: transaction_id}) - end - - def expire - raise AlreadyPaid if @state == :paid - apply OrderExpired.new(data: {order_id: @id}) + apply OrderSubmitted.new(data: { + order_id: @id, + order_number: order_number, + customer_id: customer_id, + delivery_address_id: delivery_address_id, + payment_method_id: payment_method_id, + }) end def add_item(product_id) @@ -50,14 +42,6 @@ def remove_item(product_id) @state = :submitted end - on OrderPaid do |event| - @state = :paid - end - - on OrderExpired do |event| - @state = :expired - end - on ItemAddedToBasket do |event| product_id = event.data[:product_id] order_line = find_order_line(product_id) diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_expired.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_expired.rb deleted file mode 100644 index c9dbaa210a..0000000000 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_expired.rb +++ /dev/null @@ -1,5 +0,0 @@ -module Orders - class OrderExpired < Event - attribute :order_id, Types::UUID - end -end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_paid.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_paid.rb deleted file mode 100644 index 9d27a719ca..0000000000 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_paid.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Orders - class OrderPaid < Event - attribute :order_id, Types::UUID - attribute :transaction_id, Types::TransactionId - end -end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_submitted.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_submitted.rb index 41f90a40bb..4d29900b80 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_submitted.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/order_submitted.rb @@ -1,7 +1,9 @@ module Orders class OrderSubmitted < Event - attribute :order_id, Types::UUID + attribute :order_id, Types::UUID attribute :order_number, Types::OrderNumber - attribute :customer_id, Types::ID + attribute :customer_id, Types::ID + attribute :delivery_address_id, Types::ID + attribute :payment_method_id, Types::ID end end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/prepare_order_process.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/prepare_order_process.rb new file mode 100644 index 0000000000..20fdb56829 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/prepare_order_process.rb @@ -0,0 +1,62 @@ +module Orders + class PrepareOrderProcess + def initialize(event_store, bus) + @event_store = event_store + @bus = bus + end + + def call(event) + with_linked(event) do |state| + bus.call(state.command) if state.done? + end + end + + private + attr_reader :event_store, :bus + + class State + def initialize(events) + @data = { amount: 0.to_d } + events.each{|e| apply(e)} + end + + def done? + data[:amount] > 0 && data[:customer_id] + end + + def command + Orders::PlaceOrder.new(**data) + end + + private + attr_reader :data + + def apply(event) + case event + when Orders::ItemAddedToBasket + data[:amount] += 10.to_d + when Orders::ItemRemovedFromBasket + data[:amount] -= 10.to_d + when Orders::OrderSubmitted + data.merge(event.data.slice( + :customer_id, + :delivery_address_id, + :payment_method_id + )) + else + raise ArgumentError.new("Not suported domain event") + end + end + end + + def with_linked(event) + stream = "PreparationProcess$#{event.order_id}" + event_store.link( + event.event_id, + stream_name: stream + ) + yield State.new(event_store.read.stream(stream)) + rescue RubyEventStore::EventDuplicatedInStream + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/set_order_as_expired.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/set_order_as_expired.rb deleted file mode 100644 index 8cec655bc0..0000000000 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/set_order_as_expired.rb +++ /dev/null @@ -1,5 +0,0 @@ -module Orders - class SetOrderAsExpired < Command - attribute :order_id, Types::UUID - end -end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/submit_order.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/submit_order.rb index bcaae42fea..62ea8645a1 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/submit_order.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/submit_order.rb @@ -2,5 +2,7 @@ module Orders class SubmitOrder < Command attribute :order_id, Types::UUID attribute :customer_id, Types::Coercible::Integer + attribute :delivery_address_id, Types::Coercible::Integer + attribute :payment_method_id, Types::Coercible::Integer end end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/add_item_to_basket_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/add_item_to_basket_spec.rb index 9fffd34763..f1486db5ea 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/spec/add_item_to_basket_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/add_item_to_basket_spec.rb @@ -5,6 +5,8 @@ module Orders let(:aggregate_id) { SecureRandom.uuid } let(:stream) { "Orders::Order$#{aggregate_id}" } let(:customer_id) { 997 } + let(:address_id) { 998 } + let(:payment_method_id) { 999 } let(:product_id) { 123 } let(:order_number) { "2019/01/60" } @@ -19,7 +21,14 @@ module Orders it 'no add allowed to submitted order' do Orders.arrange(stream, [ ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), - OrderSubmitted.new(data: {order_id: aggregate_id, order_number: order_number, customer_id: customer_id})]) + OrderSubmitted.new(data: { + order_id: aggregate_id, + order_number: order_number, + customer_id: customer_id, + delivery_address_id: address_id, + payment_method_id: payment_method_id, + }) + ]) expect do Orders.act(AddItemToBasket.new(order_id: aggregate_id, product_id: product_id)) diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/mark_order_as_paid_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/mark_order_as_paid_spec.rb deleted file mode 100644 index 83611b20d3..0000000000 --- a/contrib/multiple_databases_repository_sample_app/orders/spec/mark_order_as_paid_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -require_relative 'spec_helper' - -module Orders - RSpec.describe MarkOrderAsPaid do - let(:aggregate_id) { SecureRandom.uuid } - let(:stream) { "Orders::Order$#{aggregate_id}" } - let(:customer_id) { 997 } - let(:product_id) { 123 } - let(:order_number) { "2019/01/60" } - - it 'draft order could not be marked as paid' do - Orders.arrange(stream, [ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id})]) - - expect do - Orders.act(MarkOrderAsPaid.new(order_id: aggregate_id, transaction_id: SecureRandom.hex(16))) - end.to raise_error(Order::NotSubmitted) - end - - it 'submitted order will be marked as paid' do - Orders.arrange(stream, [ - ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), - OrderSubmitted.new(data: {order_id: aggregate_id, order_number: '2018/12/1', customer_id: customer_id}), - ]) - - transaction_id = SecureRandom.hex(16) - Orders.act(MarkOrderAsPaid.new(order_id: aggregate_id, transaction_id: transaction_id)) - - expect(Orders.event_store).to have_published( - an_event(OrderPaid) - .with_data(order_id: aggregate_id, transaction_id: transaction_id) - ) - end - - it 'expired order cannot be marked as paid' do - Orders.arrange(stream, [ - ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), - OrderSubmitted.new(data: {order_id: aggregate_id, order_number: '2018/12/1', customer_id: customer_id}), - OrderExpired.new(data: {order_id: aggregate_id}), - ]) - - expect do - Orders.act(MarkOrderAsPaid.new(order_id: aggregate_id, transaction_id: SecureRandom.hex(16))) - end.to raise_error(Order::OrderHasExpired) - end - end -end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/remove_item_from_basket_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/remove_item_from_basket_spec.rb index 6b8e20b3cc..d634f20dc1 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/spec/remove_item_from_basket_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/remove_item_from_basket_spec.rb @@ -5,6 +5,8 @@ module Orders let(:aggregate_id) { SecureRandom.uuid } let(:stream) { "Orders::Order$#{aggregate_id}" } let(:customer_id) { 997 } + let(:address_id) { 998 } + let(:payment_method_id) { 999 } let(:product_id) { 123 } let(:order_number) { "2019/01/60" } @@ -20,7 +22,14 @@ module Orders it 'no remove allowed to created order' do Orders.arrange(stream, [ ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), - OrderSubmitted.new(data: {order_id: aggregate_id, order_number: order_number, customer_id: customer_id})]) + OrderSubmitted.new(data: { + order_id: aggregate_id, + order_number: order_number, + customer_id: customer_id, + delivery_address_id: address_id, + payment_method_id: payment_method_id, + }) + ]) expect do Orders.act(RemoveItemFromBasket.new(order_id: aggregate_id, product_id: product_id)) diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/set_order_as_expired_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/set_order_as_expired_spec.rb deleted file mode 100644 index e11a766ff2..0000000000 --- a/contrib/multiple_databases_repository_sample_app/orders/spec/set_order_as_expired_spec.rb +++ /dev/null @@ -1,52 +0,0 @@ -require_relative 'spec_helper' - -module Orders - RSpec.describe SetOrderAsExpired do - let(:aggregate_id) { SecureRandom.uuid } - let(:stream) { "Orders::Order$#{aggregate_id}" } - let(:customer_id) { 997 } - let(:product_id) { 123 } - let(:order_number) { "2019/01/60" } - - it 'draft order will expire' do - Orders.arrange(stream, [ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id})]) - - Orders.act(SetOrderAsExpired.new(order_id: aggregate_id)) - - expect(Orders.event_store).to have_published( - an_event(OrderExpired) - .with_data( - order_id: aggregate_id, - ) - ) - end - - it 'submitted order will expire' do - Orders.arrange(stream, [ - ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), - OrderSubmitted.new(data: {order_id: aggregate_id, order_number: '2018/12/1', customer_id: customer_id}), - ]) - - Orders.act(SetOrderAsExpired.new(order_id: aggregate_id)) - - expect(Orders.event_store).to have_published( - an_event(OrderExpired) - .with_data( - order_id: aggregate_id, - ) - ) - end - - it 'paid order cannot expire' do - Orders.arrange(stream, [ - ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), - OrderSubmitted.new(data: {order_id: aggregate_id, order_number: '2018/12/1', customer_id: customer_id}), - OrderPaid.new(data: {order_id: aggregate_id, transaction_id: SecureRandom.hex(16)}), - ]) - - expect do - Orders.act(SetOrderAsExpired.new(order_id: aggregate_id)) - end.to raise_error(Order::AlreadyPaid) - end - end -end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/submit_order_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/submit_order_spec.rb index a8aa2d71d3..9c95b66086 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/spec/submit_order_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/submit_order_spec.rb @@ -5,26 +5,40 @@ module Orders let(:aggregate_id) { SecureRandom.uuid } let(:stream) { "Orders::Order$#{aggregate_id}" } let(:customer_id) { 997 } + let(:address_id) { 998 } + let(:payment_method_id) { 999 } let(:product_id) { 123 } let(:order_number) { "2019/01/60" } it 'order is submitted' do Orders.arrange(stream, [ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id})]) - Orders.act(SubmitOrder.new(order_id: aggregate_id, customer_id: customer_id)) + Orders.act(SubmitOrder.new( + order_id: aggregate_id, + customer_id: customer_id, + delivery_address_id: address_id, + payment_method_id: payment_method_id, + )) expect(Orders.event_store).to have_published( an_event(OrderSubmitted) .with_data( order_id: aggregate_id, order_number: order_number, - customer_id: customer_id + customer_id: customer_id, + delivery_address_id: address_id, + payment_method_id: payment_method_id, ) ) end it 'could not create order where customer is not given' do expect do - Orders.act(SubmitOrder.new(order_id: aggregate_id, customer_id: nil)) + Orders.act(SubmitOrder.new( + order_id: aggregate_id, + customer_id: nil, + delivery_addredd_id: nil, + payment_method_id: nil + )) end.to raise_error(Command::Invalid) end @@ -32,21 +46,23 @@ module Orders another_customer_id = 998 Orders.arrange(stream, [ ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), - OrderSubmitted.new(data: {order_id: aggregate_id, order_number: order_number, customer_id: customer_id})]) + OrderSubmitted.new(data: { + order_id: aggregate_id, + order_number: order_number, + customer_id: customer_id, + delivery_address_id: address_id, + payment_method_id: payment_method_id, + }) + ]) expect do - Orders.act(SubmitOrder.new(order_id: aggregate_id, customer_id: another_customer_id)) + Orders.act(SubmitOrder.new( + order_id: aggregate_id, + customer_id: another_customer_id, + delivery_address_id: address_id, + payment_method_id: payment_method_id, + )) end.to raise_error(Order::AlreadySubmitted) end - - it 'expired order could not be created' do - Orders.arrange(stream, [ - ItemAddedToBasket.new(data: {order_id: aggregate_id, product_id: product_id}), - OrderExpired.new(data: {order_id: aggregate_id})]) - - expect do - Orders.act(SubmitOrder.new(order_id: aggregate_id, customer_id: customer_id)) - end.to raise_error(Order::OrderHasExpired) - end end end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb index 20882bd559..85045e89fd 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb @@ -9,6 +9,7 @@ require_dependency 'payments/payment_authorized' require_dependency 'payments/payment_captured' require_dependency 'payments/payment_released' +require_dependency 'payments/payment_expired' require_dependency 'payments/payment' module Payments @@ -29,7 +30,16 @@ def self.setup(config) # Subscribe public event handlers below public_event_store.tap do |store| -# store.subscribe(Authorize.new, to: [Initiated]) + store.subscribe(InitiatePayment.new(command_bus), to: [PaymentInitiated]) + end + + # Subscribe private event handlers below + event_store.tap do |store| + store.subscribe(PaymentProcess.new(store, command_bus), to: [ + PaymentAuthorized, + PaymentCaptured, + PaymentExpired, + ]) end # Register commands handled by this module below @@ -37,11 +47,15 @@ def self.setup(config) bus.register(Payments::AuthorizePayment, Payments::OnAuthorizePayment.new(event_store)) bus.register(Payments::CapturePayment, Payments::OnCapturePayment.new(event_store)) bus.register(Payments::ReleasePayment, Payments::OnReleasePayment.new(event_store)) + bus.register(Payments::CompletePayment, Payments::OnCompletePayment.new(public_event_store)) end end def self.events_class_remapping - {} + { + 'new-order' => PaymentInitiated, + 'payment-completed' => PaymentCompleted, + } end def self.command_bus @@ -59,4 +73,16 @@ def self.event_store def self.setup? command_bus && event_store && public_event_store end + + class PaymentInitiated < Event + event_type 'new-order' + attribute :order_id, Types::UUID + attribute :amount, Types::Decimal + end + + class PaymentCompleted < Event + event_type 'payment-completed' + attribute :transaction_id, Types::Coercible::String + attribute :order_id, Types::UUID + end end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/mark_order_as_paid.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/complete_payment.rb similarity index 67% rename from contrib/multiple_databases_repository_sample_app/orders/lib/orders/mark_order_as_paid.rb rename to contrib/multiple_databases_repository_sample_app/payments/lib/payments/complete_payment.rb index 166653b215..eba2e0f109 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/mark_order_as_paid.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/complete_payment.rb @@ -1,6 +1,6 @@ -module Orders - class MarkOrderAsPaid < Command - attribute :order_id, Types::UUID +module Payments + class CompletePayment < Command attribute :transaction_id, Types::Coercible::String + attribute :order_id, Types::UUID end end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/initiate_payment.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/initiate_payment.rb new file mode 100644 index 0000000000..129a1d881d --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/initiate_payment.rb @@ -0,0 +1,14 @@ +module Payments + class InitiatePayment + def initialize(bus) + @bus = bus + end + + def call(event) + bus.call(AuthorizePayment.new( + transaction_id: SecureRandom.hex(16), + order_id: event.data.order_id + )) + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_complete_payment.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_complete_payment.rb new file mode 100644 index 0000000000..dd4df82ee8 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_complete_payment.rb @@ -0,0 +1,14 @@ +module Payments + class OnCompletePayment + def initialize(event_store) + @event_store = event_store + end + + def call(command) + event_store.publish(Payments::PaymentCompleted.new(**command)) + end + + private + attr_reader :event_store + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_expired.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_expired.rb new file mode 100644 index 0000000000..8b96e1d4c7 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_expired.rb @@ -0,0 +1,5 @@ +module Payments + class PaymentExpired < Event + attribute :transaction_id, Types::TransactionId + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_process.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_process.rb new file mode 100644 index 0000000000..5c2972752f --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_process.rb @@ -0,0 +1,61 @@ +module Payments + class PaymentProcess + def initialize(event_store, bus) + @event_store = event_store + @bus = bus + end + + def call(event) + with_linked(event) do |state| + bus.call(state.command) if state.done? + end + end + + private + attr_reader :event_store, :bus + + class State + def initialize(events) + @data = {} + @state = nil + events.each{|e| apply(e)} + end + + def done? + state == :captured + end + + def command + Payments::CompletePayment.new(**data) + end + + private + attr_reader :data, :state + + def apply(event) + case event + when Payments::PaymentAuthorized + data[:transaction_id] = event.transaction_id + data[:order_id] = event.order_id + state = :authorized + when Payments::PaymentCaptured + state = :captured + when Payments::PaymentExpired + state = :expired + else + raise ArgumentError.new("Not suported domain event") + end + end + end + + def with_linked(event) + stream = "PaymentProcess$#{event.transaction_id}" + event_store.link( + event.event_id, + stream_name: stream + ) + yield State.new(event_store.read.stream(stream)) + rescue RubyEventStore::EventDuplicatedInStream + end + end +end From d6b31dd316ae3b9335609a90575cc871e678ebb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 18 Jun 2020 18:42:00 +0200 Subject: [PATCH 24/35] No iframe in readme --- contrib/multiple_databases_repository_sample_app/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contrib/multiple_databases_repository_sample_app/README.md b/contrib/multiple_databases_repository_sample_app/README.md index b33bb459e2..b600c3f026 100644 --- a/contrib/multiple_databases_repository_sample_app/README.md +++ b/contrib/multiple_databases_repository_sample_app/README.md @@ -2,6 +2,4 @@ TDB -# Event storming - - +[Event storming board](https://miro.com/app/embed/o9J_krLAGIQ=/?) From e87cb49a318a70028ec8743bb64ce20c17032796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Fri, 19 Jun 2020 12:06:09 +0200 Subject: [PATCH 25/35] Prepare order process tests & some fixes --- .../orders/lib/orders.rb | 5 +++-- .../orders/lib/orders/prepare_order_process.rb | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb index 9fcb6389d2..5ce1167839 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb @@ -57,12 +57,13 @@ def self.setup(config) bus.register(Orders::SubmitOrder, Orders::OnSubmitOrder.new(event_store, number_generator_factory: config.number_generator_factory)) bus.register(Orders::AddItemToBasket, Orders::OnAddItemToBasket.new(event_store)) bus.register(Orders::RemoveItemFromBasket, Orders::OnRemoveItemFromBasket.new(event_store)) + bus.register(Orders::PlaceOrder, ->(cmd) { public_event_store.publish(OrderPlaced.new(**cmd)) }) end end def self.events_class_remapping { - 'new-order' => OrderPlaced, + 'new-order' => 'Orders::OrderPlaced', } end @@ -88,6 +89,6 @@ class OrderPlaced < Event attribute :customer_id, Types::ID attribute :delivery_address_id, Types::ID attribute :payment_method_id, Types::ID - attribute :amount, Types::Decimal + attribute :amount, Types::Coercible::Decimal end end diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/prepare_order_process.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/prepare_order_process.rb index 20fdb56829..7137f27286 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/prepare_order_process.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/prepare_order_process.rb @@ -21,7 +21,7 @@ def initialize(events) end def done? - data[:amount] > 0 && data[:customer_id] + data[:amount] > 0 && data[:order_id] end def command @@ -38,7 +38,9 @@ def apply(event) when Orders::ItemRemovedFromBasket data[:amount] -= 10.to_d when Orders::OrderSubmitted - data.merge(event.data.slice( + data.merge!(event.data.slice( + :order_id, + :order_number, :customer_id, :delivery_address_id, :payment_method_id From 1742b09622aca41e6092b83dc596e3f4bcbea2f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Fri, 19 Jun 2020 13:20:01 +0200 Subject: [PATCH 26/35] Test & imporove payment initialization --- .../orders/lib/orders/place_order.rb | 9 +++++ .../orders/spec/prepare_order_process_spec.rb | 34 +++++++++++++++++++ .../payments/lib/payments.rb | 8 ++--- .../lib/payments/authorize_payment.rb | 1 + .../payments/lib/payments/initiate_payment.rb | 5 ++- .../lib/payments/on_authorize_payment.rb | 2 +- .../payments/lib/payments/payment.rb | 10 +++--- .../lib/payments/payment_authorized.rb | 1 + .../payments/spec/initiate_payment_spec.rb | 23 +++++++++++++ .../spec/on_authorize_payment_spec.rb | 8 +++-- .../payments/spec/on_capture_payment_spec.rb | 2 +- .../payments/spec/on_release_payment_spec.rb | 2 +- .../payments/spec/payment_spec.rb | 11 ++++-- 13 files changed, 97 insertions(+), 19 deletions(-) create mode 100644 contrib/multiple_databases_repository_sample_app/orders/lib/orders/place_order.rb create mode 100644 contrib/multiple_databases_repository_sample_app/orders/spec/prepare_order_process_spec.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/spec/initiate_payment_spec.rb diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders/place_order.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/place_order.rb new file mode 100644 index 0000000000..45f92d0571 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders/place_order.rb @@ -0,0 +1,9 @@ +module Orders + class PlaceOrder < Command + attribute :order_id, Types::UUID + attribute :customer_id, Types::Coercible::Integer + attribute :delivery_address_id, Types::Coercible::Integer + attribute :payment_method_id, Types::Coercible::Integer + attribute :amount, Types::Decimal + end +end diff --git a/contrib/multiple_databases_repository_sample_app/orders/spec/prepare_order_process_spec.rb b/contrib/multiple_databases_repository_sample_app/orders/spec/prepare_order_process_spec.rb new file mode 100644 index 0000000000..2e6f04ded4 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/orders/spec/prepare_order_process_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +module Orders + RSpec.describe PrepareOrderProcess do + let(:order_id) { SecureRandom.uuid } + let(:stream) { "Orders::Order$#{order_id}" } + let(:customer_id) { 997 } + let(:address_id) { 998 } + let(:payment_method_id) { 999 } + let(:product_id) { 123 } + let(:order_number) { "2019/01/60" } + + it 'works' do + Orders.act(AddItemToBasket.new(order_id: order_id, product_id: product_id)) + Orders.act(AddItemToBasket.new(order_id: order_id, product_id: product_id)) + Orders.act(SubmitOrder.new( + order_id: order_id, + customer_id: customer_id, + delivery_address_id: address_id, + payment_method_id: payment_method_id, + )) + + expect(Orders.public_event_store).to have_published( + an_event(Orders::OrderPlaced).with_data( + order_id: order_id, + customer_id: customer_id, + delivery_address_id: address_id, + payment_method_id: payment_method_id, + amount: 20.to_d, + ) + ) + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb index 85045e89fd..d758cd6f9f 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb @@ -30,7 +30,7 @@ def self.setup(config) # Subscribe public event handlers below public_event_store.tap do |store| - store.subscribe(InitiatePayment.new(command_bus), to: [PaymentInitiated]) + store.subscribe(InitiatePayment.new(command_bus), to: ['new-order']) end # Subscribe private event handlers below @@ -53,8 +53,8 @@ def self.setup(config) def self.events_class_remapping { - 'new-order' => PaymentInitiated, - 'payment-completed' => PaymentCompleted, + 'new-order' => 'Payments::PaymentInitiated', + 'payment-completed' => 'Payments::PaymentCompleted', } end @@ -77,7 +77,7 @@ def self.setup? class PaymentInitiated < Event event_type 'new-order' attribute :order_id, Types::UUID - attribute :amount, Types::Decimal + attribute :amount, Types::Coercible::Decimal end class PaymentCompleted < Event diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/authorize_payment.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/authorize_payment.rb index ea172a7570..c3a8293113 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/authorize_payment.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/authorize_payment.rb @@ -2,5 +2,6 @@ module Payments class AuthorizePayment < Command attribute :transaction_id, Types::Coercible::String attribute :order_id, Types::UUID + attribute :amount, Types::Decimal end end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/initiate_payment.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/initiate_payment.rb index 129a1d881d..8a94af7f66 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/initiate_payment.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/initiate_payment.rb @@ -5,10 +5,9 @@ def initialize(bus) end def call(event) - bus.call(AuthorizePayment.new( + @bus.call(AuthorizePayment.new(**event.data.merge( transaction_id: SecureRandom.hex(16), - order_id: event.data.order_id - )) + ))) end end end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_authorize_payment.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_authorize_payment.rb index 1835ba963a..d609c0a26f 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_authorize_payment.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_authorize_payment.rb @@ -4,7 +4,7 @@ class OnAuthorizePayment def call(command) with_aggregate(Payment.new, command.transaction_id) do |payment| - payment.authorize(command.transaction_id, command.order_id) + payment.authorize(command.transaction_id, command.order_id, command.amount) end end end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment.rb index 83517fb1f6..1486bc252a 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment.rb @@ -7,11 +7,12 @@ class Payment AlreadyCaptured = Class.new(StandardError) AlreadyReleased = Class.new(StandardError) - def authorize(transaction_id, order_id) + def authorize(transaction_id, order_id, amount) raise AlreadyAuthorized if authorized? apply(PaymentAuthorized.new(data: { transaction_id: transaction_id, - order_id: order_id + order_id: order_id, + amount: amount })) end @@ -38,8 +39,9 @@ def release on PaymentAuthorized do |event| @state = :authorized - @transaction_id = event.data.fetch(:transaction_id) - @order_id = event.data.fetch(:order_id) + @transaction_id = event.transaction_id + @order_id = event.order_id + @amount = event.amount end on PaymentCaptured do |event| diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_authorized.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_authorized.rb index 3dbbcf3eea..663740160b 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_authorized.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_authorized.rb @@ -2,5 +2,6 @@ module Payments class PaymentAuthorized < Event attribute :order_id, Types::UUID attribute :transaction_id, Types::TransactionId + attribute :amount, Types::Coercible::Decimal end end diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/initiate_payment_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/initiate_payment_spec.rb new file mode 100644 index 0000000000..920c6aee4b --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/initiate_payment_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +module Payments + RSpec.describe InitiatePayment do + let(:order_id) { SecureRandom.uuid } + + it 'works' do + Payments.public_event_store.publish( + PaymentInitiated.new( + order_id: order_id, + amount: 20.to_d, + ) + ) + + expect(Payments.event_store).to have_published( + an_event(PaymentAuthorized).with_data( + transaction_id: kind_of(String), + order_id: order_id, + ) + ) + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/on_authorize_payment_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/on_authorize_payment_spec.rb index eb16e79bb1..e95775c24c 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/spec/on_authorize_payment_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/on_authorize_payment_spec.rb @@ -6,11 +6,15 @@ module Payments transaction_id = SecureRandom.hex(16) order_id = SecureRandom.uuid - Payments.act(AuthorizePayment.new(transaction_id: transaction_id, order_id: order_id)) + Payments.act(AuthorizePayment.new(transaction_id: transaction_id, order_id: order_id, amount: 20.to_d)) expect(Payments.event_store).to have_published( an_event(PaymentAuthorized) - .with_data(transaction_id: transaction_id, order_id: order_id) + .with_data( + transaction_id: transaction_id, + order_id: order_id, + amount: 20.to_d + ) ) end end diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/on_capture_payment_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/on_capture_payment_spec.rb index 121a41cb47..c7e6248e93 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/spec/on_capture_payment_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/on_capture_payment_spec.rb @@ -7,7 +7,7 @@ module Payments order_id = SecureRandom.uuid stream = "Payments::Payment$#{transaction_id}" - Payments.arrange(stream, [PaymentAuthorized.new(data: {transaction_id: transaction_id, order_id: order_id})]) + Payments.arrange(stream, [PaymentAuthorized.new(data: {transaction_id: transaction_id, order_id: order_id, amount: 20.to_d})]) Payments.act(CapturePayment.new(transaction_id: transaction_id, order_id: order_id)) expect(Payments.event_store).to have_published( diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/on_release_payment_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/on_release_payment_spec.rb index 98a0df16ef..a87b5cf6fd 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/spec/on_release_payment_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/on_release_payment_spec.rb @@ -7,7 +7,7 @@ module Payments order_id = SecureRandom.uuid stream = "Payments::Payment$#{transaction_id}" - Payments.arrange(stream, [PaymentAuthorized.new(data: {transaction_id: transaction_id, order_id: order_id})]) + Payments.arrange(stream, [PaymentAuthorized.new(data: {transaction_id: transaction_id, order_id: order_id, amount: 20.to_d})]) Payments.act(ReleasePayment.new(transaction_id: transaction_id, order_id: order_id)) expect(Payments.event_store).to have_published( diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/payment_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/payment_spec.rb index e65d0755c2..5b74e8d934 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/spec/payment_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/payment_spec.rb @@ -5,16 +5,20 @@ module Payments it 'authorize' do payment = Payment.new expect do - payment.authorize(transaction_id, order_id) + payment.authorize(transaction_id, order_id, 20.to_d) end.to apply( an_event(PaymentAuthorized) - .with_data(transaction_id: transaction_id, order_id: order_id) + .with_data( + transaction_id: transaction_id, + order_id: order_id, + amount: 20.to_d, + ) ).in(payment) end it 'should not allow for double authorization' do expect do - authorized_payment.authorize(transaction_id, order_id) + authorized_payment.authorize(transaction_id, order_id, 20.to_d) end.to raise_error(Payment::AlreadyAuthorized) end @@ -79,6 +83,7 @@ def authorized_payment PaymentAuthorized.new(data: { transaction_id: transaction_id, order_id: order_id, + amount: 20.to_d, }) ) end From 674e290c9c366c651284b3f38cf3839d08badfc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Fri, 19 Jun 2020 13:52:08 +0200 Subject: [PATCH 27/35] Add payment expiration --- .../payments/lib/payments.rb | 15 ++++++++ .../lib/payments/on_expire_payment.rb | 11 ++++++ .../payments/lib/payments/payment.rb | 35 ++++++++++++++++--- .../lib/payments/set_payment_as_expired.rb | 5 +++ 4 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_expire_payment.rb create mode 100644 contrib/multiple_databases_repository_sample_app/payments/lib/payments/set_payment_as_expired.rb diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb index d758cd6f9f..23b082c911 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb @@ -10,7 +10,21 @@ require_dependency 'payments/payment_captured' require_dependency 'payments/payment_released' require_dependency 'payments/payment_expired' + +require_dependency 'payments/authorize_payment' +require_dependency 'payments/capture_payment' +require_dependency 'payments/release_payment' +require_dependency 'payments/set_payment_as_expired' +require_dependency 'payments/complete_payment' + +require_dependency 'payments/on_authorize_payment' +require_dependency 'payments/on_capture_payment' +require_dependency 'payments/on_release_payment' +require_dependency 'payments/on_expire_payment' +require_dependency 'payments/on_complete_payment' + require_dependency 'payments/payment' +require_dependency 'payments/payment_process' module Payments def self.setup(config) @@ -45,6 +59,7 @@ def self.setup(config) # Register commands handled by this module below command_bus.tap do |bus| bus.register(Payments::AuthorizePayment, Payments::OnAuthorizePayment.new(event_store)) + bus.register(Payments::SetPaymentAsExpired, Payments::OnExpirePayment.new(event_store)) bus.register(Payments::CapturePayment, Payments::OnCapturePayment.new(event_store)) bus.register(Payments::ReleasePayment, Payments::OnReleasePayment.new(event_store)) bus.register(Payments::CompletePayment, Payments::OnCompletePayment.new(public_event_store)) diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_expire_payment.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_expire_payment.rb new file mode 100644 index 0000000000..2a6ac21f1c --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/on_expire_payment.rb @@ -0,0 +1,11 @@ +module Payments + class OnExpirePayment + include CommandHandler + + def call(command) + with_aggregate(Payment.new, command.transaction_id) do |payment| + payment.expire + end + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment.rb index 1486bc252a..a16af10e1c 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment.rb @@ -7,8 +7,12 @@ class Payment AlreadyCaptured = Class.new(StandardError) AlreadyReleased = Class.new(StandardError) + def initialize + @state = nil + end + def authorize(transaction_id, order_id, amount) - raise AlreadyAuthorized if authorized? + raise AlreadyAuthorized unless initiated? apply(PaymentAuthorized.new(data: { transaction_id: transaction_id, order_id: order_id, @@ -16,9 +20,18 @@ def authorize(transaction_id, order_id, amount) })) end - def capture + def expire + raise NotAuthorized unless authorized? raise AlreadyCaptured if captured? + raise AlreadyReleased if released? + apply(PaymentExpired.new(data: { + transaction_id: @transaction_id, + })) + end + + def capture raise NotAuthorized unless authorized? + raise AlreadyCaptured if captured? apply(PaymentCaptured.new(data: { transaction_id: @transaction_id, order_id: @order_id @@ -26,9 +39,9 @@ def capture end def release - raise AlreadyReleased if released? - raise AlreadyCaptured if captured? raise NotAuthorized unless authorized? + raise AlreadyCaptured if captured? + raise AlreadyReleased if released? apply(PaymentReleased.new(data: { transaction_id: @transaction_id, order_id: @order_id @@ -44,6 +57,10 @@ def release @amount = event.amount end + on PaymentExpired do |event| + @state = :expired + end + on PaymentCaptured do |event| @state = :captured end @@ -52,8 +69,16 @@ def release @state = :released end + def initiated? + @state == nil + end + def authorized? - @state == :authorized + @state != nil + end + + def expired? + @state == :expired end def captured? diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/set_payment_as_expired.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/set_payment_as_expired.rb new file mode 100644 index 0000000000..9fa24c75a4 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/set_payment_as_expired.rb @@ -0,0 +1,5 @@ +module Payments + class SetPaymentAsExpired < Command + attribute :transaction_id, Types::Coercible::String + end +end From 783078ec13182502dd8bb20a37580af016c07a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Fri, 19 Jun 2020 15:50:14 +0200 Subject: [PATCH 28/35] Specs & improvements in payments BC --- .../payments/lib/payments/payment.rb | 2 -- .../payments/lib/payments/payment_captured.rb | 1 - .../payments/lib/payments/payment_process.rb | 26 ++++++++++---- .../payments/lib/payments/payment_released.rb | 1 - .../payments/spec/initiate_payment_spec.rb | 1 + .../payments/spec/on_capture_payment_spec.rb | 4 +-- .../payments/spec/on_release_payment_spec.rb | 6 ++-- .../payments/spec/payment_process_spec.rb | 35 +++++++++++++++++++ .../payments/spec/payment_spec.rb | 6 ++-- 9 files changed, 63 insertions(+), 19 deletions(-) create mode 100644 contrib/multiple_databases_repository_sample_app/payments/spec/payment_process_spec.rb diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment.rb index a16af10e1c..403d9ff0e2 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment.rb @@ -34,7 +34,6 @@ def capture raise AlreadyCaptured if captured? apply(PaymentCaptured.new(data: { transaction_id: @transaction_id, - order_id: @order_id })) end @@ -44,7 +43,6 @@ def release raise AlreadyReleased if released? apply(PaymentReleased.new(data: { transaction_id: @transaction_id, - order_id: @order_id })) end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_captured.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_captured.rb index 07e1cf45c1..5834f3d901 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_captured.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_captured.rb @@ -1,6 +1,5 @@ module Payments class PaymentCaptured < Event - attribute :order_id, Types::UUID attribute :transaction_id, Types::TransactionId end end diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_process.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_process.rb index 5c2972752f..cc0b66a22b 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_process.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_process.rb @@ -18,34 +18,48 @@ class State def initialize(events) @data = {} @state = nil + @action = nil events.each{|e| apply(e)} end def done? - state == :captured + !!action end def command - Payments::CompletePayment.new(**data) + case action + when :complete + Payments::CompletePayment.new(**data) + when :release + Payments::ReleasePayment.new(**data.slice(:transaction_id)) + else + raise ArgumentError.new("Unknown action") + end end private - attr_reader :data, :state + attr_reader :data, :state, :action def apply(event) case event when Payments::PaymentAuthorized data[:transaction_id] = event.transaction_id data[:order_id] = event.order_id - state = :authorized + @state = :authorized when Payments::PaymentCaptured - state = :captured + @action = :complete if authorized? + @state = :captured when Payments::PaymentExpired - state = :expired + @action = :release if authorized? + @state = :expired else raise ArgumentError.new("Not suported domain event") end end + + def authorized? + state == :authorized + end end def with_linked(event) diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_released.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_released.rb index 01d8e13805..0f85f9b77e 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_released.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/payment_released.rb @@ -1,6 +1,5 @@ module Payments class PaymentReleased < Event - attribute :order_id, Types::UUID attribute :transaction_id, Types::TransactionId end end diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/initiate_payment_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/initiate_payment_spec.rb index 920c6aee4b..08e532997d 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/spec/initiate_payment_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/initiate_payment_spec.rb @@ -16,6 +16,7 @@ module Payments an_event(PaymentAuthorized).with_data( transaction_id: kind_of(String), order_id: order_id, + amount: 20.to_d, ) ) end diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/on_capture_payment_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/on_capture_payment_spec.rb index c7e6248e93..455ec0fd50 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/spec/on_capture_payment_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/on_capture_payment_spec.rb @@ -8,11 +8,11 @@ module Payments stream = "Payments::Payment$#{transaction_id}" Payments.arrange(stream, [PaymentAuthorized.new(data: {transaction_id: transaction_id, order_id: order_id, amount: 20.to_d})]) - Payments.act(CapturePayment.new(transaction_id: transaction_id, order_id: order_id)) + Payments.act(CapturePayment.new(transaction_id: transaction_id)) expect(Payments.event_store).to have_published( an_event(PaymentCaptured) - .with_data(transaction_id: transaction_id, order_id: order_id) + .with_data(transaction_id: transaction_id) ) end end diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/on_release_payment_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/on_release_payment_spec.rb index a87b5cf6fd..0584d79939 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/spec/on_release_payment_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/on_release_payment_spec.rb @@ -2,17 +2,17 @@ module Payments RSpec.describe OnReleasePayment do - it 'capture payment' do + it 'release payment' do transaction_id = SecureRandom.hex(16) order_id = SecureRandom.uuid stream = "Payments::Payment$#{transaction_id}" Payments.arrange(stream, [PaymentAuthorized.new(data: {transaction_id: transaction_id, order_id: order_id, amount: 20.to_d})]) - Payments.act(ReleasePayment.new(transaction_id: transaction_id, order_id: order_id)) + Payments.act(ReleasePayment.new(transaction_id: transaction_id)) expect(Payments.event_store).to have_published( an_event(PaymentReleased) - .with_data(transaction_id: transaction_id, order_id: order_id) + .with_data(transaction_id: transaction_id) ) end end diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/payment_process_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/payment_process_spec.rb new file mode 100644 index 0000000000..2c90a69e5d --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/payment_process_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +module Payments + RSpec.describe PaymentProcess do + let(:transaction_id) { SecureRandom.hex(16) } + let(:order_id) { SecureRandom.uuid } + + it 'complete captured payment' do + Payments.act(AuthorizePayment.new(transaction_id: transaction_id, order_id: order_id, amount: 20.to_d)) + Payments.act(CapturePayment.new(transaction_id: transaction_id, order_id: order_id)) + + expect(Payments.public_event_store).to have_published( + an_event(Payments::PaymentCompleted).with_data( + transaction_id: transaction_id, + order_id: order_id, + ) + ) + end + + it 'release expired payment' do + Payments.act(AuthorizePayment.new(transaction_id: transaction_id, order_id: order_id, amount: 20.to_d)) + Payments.act(SetPaymentAsExpired.new(transaction_id: transaction_id)) + + expect(Payments.public_event_store).not_to have_published( + an_event(Payments::PaymentCompleted) + ) + + expect(Payments.event_store).to have_published( + an_event(Payments::PaymentReleased).with_data( + transaction_id: transaction_id, + ) + ) + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/payment_spec.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/payment_spec.rb index 5b74e8d934..f0126f3f4d 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/spec/payment_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/payment_spec.rb @@ -29,7 +29,7 @@ module Payments payment.capture end.to apply( an_event(PaymentCaptured) - .with_data(transaction_id: transaction_id, order_id: order_id) + .with_data(transaction_id: transaction_id) ).in(payment) end @@ -52,7 +52,7 @@ module Payments payment.release end.to apply( an_event(PaymentReleased) - .with_data(transaction_id: transaction_id, order_id: order_id) + .with_data(transaction_id: transaction_id) ).in(payment) end @@ -94,7 +94,6 @@ def captured_payment payment.apply( PaymentCaptured.new(data: { transaction_id: transaction_id, - order_id: order_id, }) ) end @@ -105,7 +104,6 @@ def released_payment payment.apply( PaymentReleased.new(data: { transaction_id: transaction_id, - order_id: order_id, }) ) end From f0cd54baf7125b8d679638757c1fc4acedbb41d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Fri, 19 Jun 2020 18:41:19 +0200 Subject: [PATCH 29/35] Shipping bounded context --- .../config/application.rb | 1 + .../config/database.yml | 12 +++ ...0200604152933_create_event_store_events.rb | 45 ++++++++++ .../db/shipping_schema.rb | 34 ++++++++ .../shipping/lib/shipping.rb | 82 +++++++++++++++++++ .../shipping/lib/shipping/.keep | 0 .../lib/shipping/application_record.rb | 8 ++ .../shipping/lib/shipping/on_ship_package.rb | 14 ++++ .../shipping/lib/shipping/package.rb | 22 +++++ .../shipping/lib/shipping/package_shipped.rb | 9 ++ .../shipping/lib/shipping/ship_package.rb | 7 ++ .../shipping/lib/shipping/shipping_process.rb | 60 ++++++++++++++ .../shipping/spec/shipping_spec.rb | 4 + .../shipping/spec/spec_helper.rb | 7 ++ .../spec/shipping_spec.rb | 6 ++ 15 files changed, 311 insertions(+) create mode 100644 contrib/multiple_databases_repository_sample_app/db/shipping/20200604152933_create_event_store_events.rb create mode 100644 contrib/multiple_databases_repository_sample_app/db/shipping_schema.rb create mode 100644 contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb create mode 100644 contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/.keep create mode 100644 contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/application_record.rb create mode 100644 contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/on_ship_package.rb create mode 100644 contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/package.rb create mode 100644 contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/package_shipped.rb create mode 100644 contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/ship_package.rb create mode 100644 contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/shipping_process.rb create mode 100644 contrib/multiple_databases_repository_sample_app/shipping/spec/shipping_spec.rb create mode 100644 contrib/multiple_databases_repository_sample_app/shipping/spec/spec_helper.rb create mode 100644 contrib/multiple_databases_repository_sample_app/spec/shipping_spec.rb diff --git a/contrib/multiple_databases_repository_sample_app/config/application.rb b/contrib/multiple_databases_repository_sample_app/config/application.rb index f9568e2f6e..c2fe0ab597 100644 --- a/contrib/multiple_databases_repository_sample_app/config/application.rb +++ b/contrib/multiple_databases_repository_sample_app/config/application.rb @@ -30,6 +30,7 @@ class Application < Rails::Application # the framework and any gems in your application. config.paths.add 'orders/lib', eager_load: true config.paths.add 'payments/lib', eager_load: true + config.paths.add 'shipping/lib', eager_load: true # Don't generate system test files. config.generators.system_tests = nil diff --git a/contrib/multiple_databases_repository_sample_app/config/database.yml b/contrib/multiple_databases_repository_sample_app/config/database.yml index 6d8d08aa4e..38f4810f4d 100644 --- a/contrib/multiple_databases_repository_sample_app/config/database.yml +++ b/contrib/multiple_databases_repository_sample_app/config/database.yml @@ -21,6 +21,10 @@ development: <<: *default database: db/payments_development.sqlite3 migrations_paths: db/payments + shipping: + <<: *default + database: db/shipping_development.sqlite3 + migrations_paths: db/shipping # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". @@ -37,6 +41,10 @@ test: <<: *default database: db/payments_test.sqlite3 migrations_paths: db/payments + shipping: + <<: *default + database: db/shipping_test.sqlite3 + migrations_paths: db/shipping production: primary: @@ -50,3 +58,7 @@ production: <<: *default database: db/payments_production.sqlite3 migrations_paths: db/payments + shipping: + <<: *default + database: db/shipping_production.sqlite3 + migrations_paths: db/shipping diff --git a/contrib/multiple_databases_repository_sample_app/db/shipping/20200604152933_create_event_store_events.rb b/contrib/multiple_databases_repository_sample_app/db/shipping/20200604152933_create_event_store_events.rb new file mode 100644 index 0000000000..c583aaea5b --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/db/shipping/20200604152933_create_event_store_events.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +class CreateEventStoreEvents < ActiveRecord::Migration[4.2] + def change + postgres = ActiveRecord::Base.connection.adapter_name == "PostgreSQL" + sqlite = ActiveRecord::Base.connection.adapter_name == "SQLite" + rails_42 = Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new("5.0.0") + enable_extension "pgcrypto" if postgres + create_table(:event_store_events_in_streams, force: false) do |t| + t.string :stream, null: false + t.integer :position, null: true + if postgres + t.references :event, null: false, type: :uuid + else + t.references :event, null: false, type: :string, limit: 36 + end + t.datetime :created_at, null: false + end + add_index :event_store_events_in_streams, [:stream, :position], unique: true + add_index :event_store_events_in_streams, [:created_at] + add_index :event_store_events_in_streams, [:stream, :event_id], unique: true + + if postgres + create_table(:event_store_events, id: :uuid, default: 'gen_random_uuid()', force: false) do |t| + t.string :event_type, null: false + t.binary :metadata + t.binary :data, null: false + t.datetime :created_at, null: false + end + else + create_table(:event_store_events, id: false, force: false) do |t| + t.string :id, limit: 36, primary_key: true, null: false + t.string :event_type, null: false + t.binary :metadata + t.binary :data, null: false + t.datetime :created_at, null: false + end + if sqlite && rails_42 + add_index :event_store_events, :id, unique: true + end + end + add_index :event_store_events, :created_at + add_index :event_store_events, :event_type + end +end diff --git a/contrib/multiple_databases_repository_sample_app/db/shipping_schema.rb b/contrib/multiple_databases_repository_sample_app/db/shipping_schema.rb new file mode 100644 index 0000000000..01923995c3 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/db/shipping_schema.rb @@ -0,0 +1,34 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# This file is the source Rails uses to define your schema when running `rails +# db:schema:load`. When creating a new database, `rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 2020_06_04_152933) do + + create_table "event_store_events", id: :string, limit: 36, force: :cascade do |t| + t.string "event_type", null: false + t.binary "metadata" + t.binary "data", null: false + t.datetime "created_at", null: false + t.index ["created_at"], name: "index_event_store_events_on_created_at" + t.index ["event_type"], name: "index_event_store_events_on_event_type" + end + + create_table "event_store_events_in_streams", force: :cascade do |t| + t.string "stream", null: false + t.integer "position" + t.string "event_id", limit: 36, null: false + t.datetime "created_at", null: false + t.index ["created_at"], name: "index_event_store_events_in_streams_on_created_at" + t.index ["stream", "event_id"], name: "index_event_store_events_in_streams_on_stream_and_event_id", unique: true + t.index ["stream", "position"], name: "index_event_store_events_in_streams_on_stream_and_position", unique: true + end + +end diff --git a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb new file mode 100644 index 0000000000..c47efb796d --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +require_relative 'shipping/application_record' +require_relative '../../lib/event' +require_relative '../../lib/types' +require_relative '../../lib/command' +require_relative '../../lib/command_handler' + +require_dependency 'shipping/package_shipped' + +require_dependency 'shipping/ship_package' + +require_dependency 'shipping/on_ship_package' + +require_dependency 'shipping/package' +require_dependency 'shipping/shipping_process' + +module Shipping + def self.setup(config) + @@command_bus = config.command_bus + @@public_event_store = RailsEventStore::Client.new( + repository: config.event_repository, + mapper: RubyEventStore::Mappers::Default.new( + serializer: JSON, + events_class_remapping: events_class_remapping + ) + ) + @@module_event_store = RailsEventStore::Client.new( + repository: RailsEventStoreActiveRecord::EventRepository.new( + Shipping::ApplicationRecord), + mapper: RubyEventStore::Mappers::Default.new(serializer: JSON) + ) + + # Subscribe public event handlers below + public_event_store.tap do |store| + store.subscribe(ShippingProcess.new(command_bus), to: ['new-order']) + store.subscribe(ShippingProcess.new(command_bus), to: ['payment-completed']) + end + + # Subscribe private event handlers below + event_store.tap do |store| + end + + # Register commands handled by this module below + command_bus.tap do |bus| + bus.register(Shipping::ShipPackage, Shipping::OnShipPackage.new(event_store)) + end + end + + def self.events_class_remapping + { + 'new-order' => 'Shipping::OrderPlaced', + 'payment-completed' => 'Shipping::OrderPaid', + } + end + + def self.command_bus + @@command_bus + end + + def self.public_event_store + @@public_event_store + end + + def self.event_store + @@module_event_store + end + + def self.setup? + command_bus && event_store && public_event_store + end + + class OrderPlaced < Event + event_type 'new-order' + attribute :order_id, Types::UUID + end + + class OrderPaid < Event + event_type 'payment-completed' + attribute :order_id, Types::UUID + end +end diff --git a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/.keep b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/application_record.rb b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/application_record.rb new file mode 100644 index 0000000000..2e348c816b --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/application_record.rb @@ -0,0 +1,8 @@ +require 'active_record' + +module Shipping + class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true + connects_to database: { writing: :shipping, reading: :shipping } + end +end diff --git a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/on_ship_package.rb b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/on_ship_package.rb new file mode 100644 index 0000000000..41e14dc8b1 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/on_ship_package.rb @@ -0,0 +1,14 @@ +module Shipping + class OnShipPackage + include CommandHandler + + def call(command) + with_aggregate(Package.new, command.order_id) do |package| + package.ship_to( + command.customer_id, + command.delivery_address_id, + ) + end + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/package.rb b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/package.rb new file mode 100644 index 0000000000..dc114e58e7 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/package.rb @@ -0,0 +1,22 @@ +module Shipping + class Package + include AggregateRoot + + def ship_to(customer_id, address_id) + apply(PackageShipped.new(data: { + customer_id: customer_id, + delivery_address_id: address_id, + tracking_number: SecureRandom.hex(16), + estimated_delivery_date: 2.days.from_now.to_date, + })) + end + + private + + on PackageShipped do |event| + @state = :ready + @tracking_number = event.tracking_number + end + end +end + diff --git a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/package_shipped.rb b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/package_shipped.rb new file mode 100644 index 0000000000..56f06fb320 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/package_shipped.rb @@ -0,0 +1,9 @@ +module Shipping + class PackageShipped < Event + attribute :order_id, Types::UUID + attribute :customer_id, Types::ID + attribute :delivery_address_id, Types::ID + attribute :tracking_number, Types::String + attribute :estimated_delivery_date, Types::Date + end +end diff --git a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/ship_package.rb b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/ship_package.rb new file mode 100644 index 0000000000..792c884cfc --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/ship_package.rb @@ -0,0 +1,7 @@ +module Shipping + class ShipPackage < Command + attribute :order_id, Types::UUID + attribute :customer_id, Types::ID + attribute :delivery_address_id, Types::ID + end +end diff --git a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/shipping_process.rb b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/shipping_process.rb new file mode 100644 index 0000000000..b8c64176a6 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/shipping_process.rb @@ -0,0 +1,60 @@ +module Shipping + class ShippingProcess + def initialize(event_store, bus) + @event_store = event_store + @bus = bus + end + + def call(event) + with_linked(event) do |state| + bus.call(state.command) if state.done? + end + end + + private + attr_reader :event_store, :bus + + class State + def initialize(events) + @data = {} + @state = [] + events.each{|e| apply(e)} + end + + def done? + state.include?(:placed) && state.include?(:paid) + end + + def command + Shipping::CompleteShipping.new(**data) + end + + private + attr_reader :data, :state + + def apply(event) + case event + when Shipping::OrderPlaced + data[:order_id] = event.order_id + data[:customer_id] = event.customer_id + data[:delivery_address_id] = event.delivery_address_id + @state << :placed + when Shipping::OrderPaid + @state << :paid + else + raise ArgumentError.new("Not suported domain event") + end + end + end + + def with_linked(event) + stream = "ShippingProcess$#{event.order_id}" + event_store.link( + event.event_id, + stream_name: stream + ) + yield State.new(event_store.read.stream(stream)) + rescue RubyEventStore::EventDuplicatedInStream + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/shipping/spec/shipping_spec.rb b/contrib/multiple_databases_repository_sample_app/shipping/spec/shipping_spec.rb new file mode 100644 index 0000000000..ed529f09ac --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/shipping/spec/shipping_spec.rb @@ -0,0 +1,4 @@ +require_relative 'spec_helper' + +RSpec.describe Shipping do +end diff --git a/contrib/multiple_databases_repository_sample_app/shipping/spec/spec_helper.rb b/contrib/multiple_databases_repository_sample_app/shipping/spec/spec_helper.rb new file mode 100644 index 0000000000..f4b88b9489 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/shipping/spec/spec_helper.rb @@ -0,0 +1,7 @@ +ENV['RAILS_ENV'] = 'test' + +$LOAD_PATH.push File.expand_path('../../../spec', __FILE__) +require File.expand_path('../../../config/environment', __FILE__) +require File.expand_path('../../../spec/rails_helper', __FILE__) + +require_relative '../lib/shipping' diff --git a/contrib/multiple_databases_repository_sample_app/spec/shipping_spec.rb b/contrib/multiple_databases_repository_sample_app/spec/shipping_spec.rb new file mode 100644 index 0000000000..35528697e3 --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/spec/shipping_spec.rb @@ -0,0 +1,6 @@ +require 'rails_helper' + +path = Rails.root.join('shipping/spec') +Dir.glob("#{path}/**/*_spec.rb") do |file| + require file +end From f792c6d769d260f615d4092449eed1f4a7125aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Fri, 19 Jun 2020 19:36:41 +0200 Subject: [PATCH 30/35] Shipping process tests --- .../config/initializers/rails_event_store.rb | 1 + .../shipping/lib/shipping.rb | 6 ++-- .../shipping/spec/shipping_process_spec.rb | 30 +++++++++++++++++++ .../shipping/spec/spec_helper.rb | 24 +++++++++++---- 4 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 contrib/multiple_databases_repository_sample_app/shipping/spec/shipping_process_spec.rb diff --git a/contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb b/contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb index fdd7fc84bc..c80daa1329 100644 --- a/contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb +++ b/contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb @@ -35,4 +35,5 @@ Orders.setup(Rails.configuration) Payments.setup(Rails.configuration) + Shipping.setup(Rails.configuration) end diff --git a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb index c47efb796d..80c68da48b 100644 --- a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb +++ b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb @@ -33,8 +33,8 @@ def self.setup(config) # Subscribe public event handlers below public_event_store.tap do |store| - store.subscribe(ShippingProcess.new(command_bus), to: ['new-order']) - store.subscribe(ShippingProcess.new(command_bus), to: ['payment-completed']) + store.subscribe(ShippingProcess.new(event_store, command_bus), to: ['new-order']) + store.subscribe(ShippingProcess.new(event_store, command_bus), to: ['payment-completed']) end # Subscribe private event handlers below @@ -73,6 +73,8 @@ def self.setup? class OrderPlaced < Event event_type 'new-order' attribute :order_id, Types::UUID + attribute :customer_id, Types::ID + attribute :delivery_address_id, Types::ID end class OrderPaid < Event diff --git a/contrib/multiple_databases_repository_sample_app/shipping/spec/shipping_process_spec.rb b/contrib/multiple_databases_repository_sample_app/shipping/spec/shipping_process_spec.rb new file mode 100644 index 0000000000..a4d7be6daf --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/shipping/spec/shipping_process_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +module Shipping + RSpec.describe ShippingProcess do + let(:order_id) { SecureRandom.uuid } + let(:customer_id) { 123 } + let(:address_id) { 999 } + + it 'works' do + Shipping.public_event_store.publish(OrderPlaced.new( + order_id: order_id, + customer_id: customer_id, + delivery_address_id: address_id, + )) + Shipping.public_event_store.publish(OrderPaid.new( + order_id: order_id, + )) + + expect(Shipping.event_store).to have_published( + an_event(Shipping::PackageShipped).with_data( + order_id: order_id, + customer_id: customer_id, + delivery_address_id: address_id, + tracking_number: kind_of(String), + estimated_delivery_date: 2.days.from_now.to_date, + ) + ) + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/shipping/spec/spec_helper.rb b/contrib/multiple_databases_repository_sample_app/shipping/spec/spec_helper.rb index f4b88b9489..aaa2235dc0 100644 --- a/contrib/multiple_databases_repository_sample_app/shipping/spec/spec_helper.rb +++ b/contrib/multiple_databases_repository_sample_app/shipping/spec/spec_helper.rb @@ -1,7 +1,21 @@ -ENV['RAILS_ENV'] = 'test' - -$LOAD_PATH.push File.expand_path('../../../spec', __FILE__) -require File.expand_path('../../../config/environment', __FILE__) -require File.expand_path('../../../spec/rails_helper', __FILE__) +ENV['RAILS_ENV'] ||= 'test' require_relative '../lib/shipping' + +module Shipping + def self.arrange(stream, events, event_store: Shipping.event_store) + event_store.append(events, stream_name: stream) + end + + def self.act(command, bus: Shipping.command_bus) + bus.call(command) + end +end + +unless Shipping.setup? + Configuration = Struct.new(:event_repository, :command_bus) + Shipping.setup(Configuration.new( + RubyEventStore::InMemoryRepository.new, + Arkency::CommandBus.new, + )) +end From 487244fac7ceaa40b81057647700886841fdc4e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Mon, 22 Jun 2020 18:52:21 +0200 Subject: [PATCH 31/35] Shipping process cound not use linked events Because one could not link to events from different event store. A separate model for process state is needed here. This could be implemented as a snapshoot event and reading only last event from process stream but I've gone with AR based process state model to show that typical AR models could be also used in multiple databases model. --- .../20200622154807_shipping_process_state.rb | 9 ++++ .../db/shipping_schema.rb | 8 +++- .../shipping/lib/shipping.rb | 14 ++++++ .../shipping/lib/shipping/on_ship_package.rb | 2 +- .../shipping/lib/shipping/package.rb | 6 +++ .../shipping/lib/shipping/package_shipped.rb | 2 +- .../shipping/lib/shipping/shipping_process.rb | 44 +++++++++---------- .../shipping/spec/shipping_process_spec.rb | 8 ++++ 8 files changed, 68 insertions(+), 25 deletions(-) create mode 100644 contrib/multiple_databases_repository_sample_app/db/shipping/20200622154807_shipping_process_state.rb diff --git a/contrib/multiple_databases_repository_sample_app/db/shipping/20200622154807_shipping_process_state.rb b/contrib/multiple_databases_repository_sample_app/db/shipping/20200622154807_shipping_process_state.rb new file mode 100644 index 0000000000..06c5dd105e --- /dev/null +++ b/contrib/multiple_databases_repository_sample_app/db/shipping/20200622154807_shipping_process_state.rb @@ -0,0 +1,9 @@ +class ShippingProcessState < ActiveRecord::Migration[6.0] + def change + create_table :shipping_process_state, id: :string, primary_key: :order_id do |t| + t.integer :customer_id + t.integer :delivery_address_id + t.binary :states + end + end +end diff --git a/contrib/multiple_databases_repository_sample_app/db/shipping_schema.rb b/contrib/multiple_databases_repository_sample_app/db/shipping_schema.rb index 01923995c3..8345c08e49 100644 --- a/contrib/multiple_databases_repository_sample_app/db/shipping_schema.rb +++ b/contrib/multiple_databases_repository_sample_app/db/shipping_schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_06_04_152933) do +ActiveRecord::Schema.define(version: 2020_06_22_154807) do create_table "event_store_events", id: :string, limit: 36, force: :cascade do |t| t.string "event_type", null: false @@ -31,4 +31,10 @@ t.index ["stream", "position"], name: "index_event_store_events_in_streams_on_stream_and_position", unique: true end + create_table "shipping_process_state", primary_key: "order_id", id: :string, force: :cascade do |t| + t.integer "customer_id" + t.integer "delivery_address_id" + t.binary "states" + end + end diff --git a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb index 80c68da48b..8ba8effe07 100644 --- a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb +++ b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb @@ -39,6 +39,12 @@ def self.setup(config) # Subscribe private event handlers below event_store.tap do |store| + store.subscribe(->(ev) { + public_event_store.publish(OrderSent.new(**ev.data.slice( + :order_id, :tracking_number, :estimated_delivery_date)) + ) + }, to: [Shipping::PackageShipped] + ) end # Register commands handled by this module below @@ -51,6 +57,7 @@ def self.events_class_remapping { 'new-order' => 'Shipping::OrderPlaced', 'payment-completed' => 'Shipping::OrderPaid', + 'order-sent' => 'Shipping::OrderSent', } end @@ -81,4 +88,11 @@ class OrderPaid < Event event_type 'payment-completed' attribute :order_id, Types::UUID end + + class OrderSent < Event + event_type 'order-sent' + attribute :order_id, Types::UUID + attribute :tracking_number, Types::String + attribute :estimated_delivery_date, Types::JSON::Date + end end diff --git a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/on_ship_package.rb b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/on_ship_package.rb index 41e14dc8b1..100d855062 100644 --- a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/on_ship_package.rb +++ b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/on_ship_package.rb @@ -3,7 +3,7 @@ class OnShipPackage include CommandHandler def call(command) - with_aggregate(Package.new, command.order_id) do |package| + with_aggregate(Package.new(command.order_id), command.order_id) do |package| package.ship_to( command.customer_id, command.delivery_address_id, diff --git a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/package.rb b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/package.rb index dc114e58e7..1e180c0e9d 100644 --- a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/package.rb +++ b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/package.rb @@ -2,8 +2,13 @@ module Shipping class Package include AggregateRoot + def initialize(order_id) + @order_id = order_id + end + def ship_to(customer_id, address_id) apply(PackageShipped.new(data: { + order_id: order_id, customer_id: customer_id, delivery_address_id: address_id, tracking_number: SecureRandom.hex(16), @@ -12,6 +17,7 @@ def ship_to(customer_id, address_id) end private + attr_reader :order_id on PackageShipped do |event| @state = :ready diff --git a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/package_shipped.rb b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/package_shipped.rb index 56f06fb320..d453e7b4df 100644 --- a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/package_shipped.rb +++ b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/package_shipped.rb @@ -4,6 +4,6 @@ class PackageShipped < Event attribute :customer_id, Types::ID attribute :delivery_address_id, Types::ID attribute :tracking_number, Types::String - attribute :estimated_delivery_date, Types::Date + attribute :estimated_delivery_date, Types::JSON::Date end end diff --git a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/shipping_process.rb b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/shipping_process.rb index b8c64176a6..0ca3391889 100644 --- a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/shipping_process.rb +++ b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping/shipping_process.rb @@ -14,33 +14,34 @@ def call(event) private attr_reader :event_store, :bus - class State - def initialize(events) - @data = {} - @state = [] - events.each{|e| apply(e)} + class State < ApplicationRecord + self.table_name = :shipping_process_state + serialize :states, Array + + def intialize + self.states = [] end def done? - state.include?(:placed) && state.include?(:paid) + states.include?(:placed) && states.include?(:paid) end def command - Shipping::CompleteShipping.new(**data) + Shipping::ShipPackage.new( + order_id: order_id, + customer_id: customer_id, + delivery_address_id: delivery_address_id, + ) end - private - attr_reader :data, :state - def apply(event) case event when Shipping::OrderPlaced - data[:order_id] = event.order_id - data[:customer_id] = event.customer_id - data[:delivery_address_id] = event.delivery_address_id - @state << :placed + self.customer_id = event.customer_id + self.delivery_address_id = event.delivery_address_id + states << :placed when Shipping::OrderPaid - @state << :paid + states << :paid else raise ArgumentError.new("Not suported domain event") end @@ -48,13 +49,12 @@ def apply(event) end def with_linked(event) - stream = "ShippingProcess$#{event.order_id}" - event_store.link( - event.event_id, - stream_name: stream - ) - yield State.new(event_store.read.stream(stream)) - rescue RubyEventStore::EventDuplicatedInStream + State.transaction do + state = State.lock.find_or_create_by!(order_id: event.order_id) + state.apply(event) + state.save! + yield state + end end end end diff --git a/contrib/multiple_databases_repository_sample_app/shipping/spec/shipping_process_spec.rb b/contrib/multiple_databases_repository_sample_app/shipping/spec/shipping_process_spec.rb index a4d7be6daf..a4ba2b7016 100644 --- a/contrib/multiple_databases_repository_sample_app/shipping/spec/shipping_process_spec.rb +++ b/contrib/multiple_databases_repository_sample_app/shipping/spec/shipping_process_spec.rb @@ -25,6 +25,14 @@ module Shipping estimated_delivery_date: 2.days.from_now.to_date, ) ) + + expect(Shipping.public_event_store).to have_published( + an_event(Shipping::OrderSent).with_data( + order_id: order_id, + tracking_number: kind_of(String), + estimated_delivery_date: 2.days.from_now.to_date, + ) + ) end end end From d234b8ddba41a17928520f5a70a017bc1d7d8372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 30 Jul 2020 12:36:23 +0200 Subject: [PATCH 32/35] Missing require Needed to run test in scope of the single BC --- .../payments/lib/payments/application_record.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/application_record.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/application_record.rb index fa51873d0e..30bc27595c 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/application_record.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/application_record.rb @@ -1,3 +1,5 @@ +require 'active_record' + module Payments class ApplicationRecord < ActiveRecord::Base self.abstract_class = true From b081de2e84e460a56ada249212fbac76bbebdfbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Thu, 30 Jul 2020 14:17:13 +0200 Subject: [PATCH 33/35] Try to enable running isolated BC tests only (payments) --- .../payments/lib/payments.rb | 6 +++--- .../lib/payments/application_record.rb | 2 -- .../payments/spec/spec_helper.rb | 18 +++++++++++++++++- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb index 23b082c911..97223ef6db 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb @@ -74,15 +74,15 @@ def self.events_class_remapping end def self.command_bus - @@command_bus + @@command_bus rescue nil end def self.public_event_store - @@public_event_store + @@public_event_store rescue nil end def self.event_store - @@module_event_store + @@module_event_store rescue nil end def self.setup? diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/application_record.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/application_record.rb index 30bc27595c..fa51873d0e 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments/application_record.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments/application_record.rb @@ -1,5 +1,3 @@ -require 'active_record' - module Payments class ApplicationRecord < ActiveRecord::Base self.abstract_class = true diff --git a/contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb b/contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb index 7b9de1b545..5fb442c0ff 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/spec/spec_helper.rb @@ -1,4 +1,20 @@ -ENV['RAILS_ENV'] = 'test' +require 'active_record' + +ENV['RAILS_ENV'] ||= 'test' +if ActiveRecord::Base.configurations.empty? + configuration = YAML::load(File.open('../config/database.yml')) + ActiveRecord::Base.configurations = configuration + ActiveRecord::Base.establish_connection(configuration[ENV['RAILS_ENV']]["payments"]) + begin + ActiveRecord::Migration.maintain_test_schema! + rescue ActiveRecord::PendingMigrationError => e + puts e.to_s.strip + exit 1 + end + require 'arkency/command_bus' + require 'rails_event_store' + require 'aggregate_root' +end require_relative '../lib/payments' From 59a241a367d0db352b2ff59c4bb9df7f438e6c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Fri, 11 Sep 2020 19:39:03 +0200 Subject: [PATCH 34/35] Align with changes in event repository & dispatcher/scheduler API --- .../config/initializers/rails_event_store.rb | 8 +++++--- .../orders/lib/orders.rb | 12 ++++++++---- .../payments/lib/payments.rb | 12 ++++++++---- .../shipping/lib/shipping.rb | 12 ++++++++---- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb b/contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb index c80daa1329..f76b95061d 100644 --- a/contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb +++ b/contrib/multiple_databases_repository_sample_app/config/initializers/rails_event_store.rb @@ -6,13 +6,15 @@ events_class_remapping = {} Rails.configuration.event_repository = - RailsEventStoreActiveRecord::EventRepository.new + RailsEventStoreActiveRecord::EventRepository.new(serializer: JSON) Rails.configuration.event_store = RailsEventStore::Client.new( repository: Rails.configuration.event_repository, mapper: RubyEventStore::Mappers::Default.new( - serializer: JSON, events_class_remapping: events_class_remapping - ) + ), + dispatcher: RubyEventStore::ComposedDispatcher.new( + RubyEventStore::ImmediateAsyncDispatcher.new(scheduler: RailsEventStore::ActiveJobScheduler.new(serializer: JSON)), + RubyEventStore::Dispatcher.new), ) Rails.configuration.command_bus = Arkency::CommandBus.new diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb index 5ce1167839..3c3ccfd494 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb @@ -29,14 +29,18 @@ def self.setup(config) @@public_event_store = RailsEventStore::Client.new( repository: config.event_repository, mapper: RubyEventStore::Mappers::Default.new( - serializer: JSON, events_class_remapping: events_class_remapping - ) + ), + dispatcher: RubyEventStore::ComposedDispatcher.new( + RubyEventStore::ImmediateAsyncDispatcher.new(scheduler: RailsEventStore::ActiveJobScheduler.new(serializer: JSON)), + RubyEventStore::Dispatcher.new), ) @@module_event_store = RailsEventStore::Client.new( repository: RailsEventStoreActiveRecord::EventRepository.new( - Orders::ApplicationRecord), - mapper: RubyEventStore::Mappers::Default.new(serializer: JSON) + Orders::ApplicationRecord, serializer: JSON), + dispatcher: RubyEventStore::ComposedDispatcher.new( + RubyEventStore::ImmediateAsyncDispatcher.new(scheduler: RailsEventStore::ActiveJobScheduler.new(serializer: JSON)), + RubyEventStore::Dispatcher.new), ) # Subscribe public event handlers below diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb index 97223ef6db..fe8372d2d5 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb @@ -32,14 +32,18 @@ def self.setup(config) @@public_event_store = RailsEventStore::Client.new( repository: config.event_repository, mapper: RubyEventStore::Mappers::Default.new( - serializer: JSON, events_class_remapping: events_class_remapping - ) + ), + dispatcher: RubyEventStore::ComposedDispatcher.new( + RubyEventStore::ImmediateAsyncDispatcher.new(scheduler: RailsEventStore::ActiveJobScheduler.new(serializer: JSON)), + RubyEventStore::Dispatcher.new), ) @@module_event_store = RailsEventStore::Client.new( repository: RailsEventStoreActiveRecord::EventRepository.new( - Payments::ApplicationRecord), - mapper: RubyEventStore::Mappers::Default.new(serializer: JSON) + Payments::ApplicationRecord, serializer: JSON), + dispatcher: RubyEventStore::ComposedDispatcher.new( + RubyEventStore::ImmediateAsyncDispatcher.new(scheduler: RailsEventStore::ActiveJobScheduler.new(serializer: JSON)), + RubyEventStore::Dispatcher.new), ) # Subscribe public event handlers below diff --git a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb index 8ba8effe07..3b57e04c7b 100644 --- a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb +++ b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb @@ -21,14 +21,18 @@ def self.setup(config) @@public_event_store = RailsEventStore::Client.new( repository: config.event_repository, mapper: RubyEventStore::Mappers::Default.new( - serializer: JSON, events_class_remapping: events_class_remapping - ) + ), + dispatcher: RubyEventStore::ComposedDispatcher.new( + RubyEventStore::ImmediateAsyncDispatcher.new(scheduler: RailsEventStore::ActiveJobScheduler.new(serializer: JSON)), + RubyEventStore::Dispatcher.new), ) @@module_event_store = RailsEventStore::Client.new( repository: RailsEventStoreActiveRecord::EventRepository.new( - Shipping::ApplicationRecord), - mapper: RubyEventStore::Mappers::Default.new(serializer: JSON) + Shipping::ApplicationRecord, serializer: JSON), + dispatcher: RubyEventStore::ComposedDispatcher.new( + RubyEventStore::ImmediateAsyncDispatcher.new(scheduler: RailsEventStore::ActiveJobScheduler.new(serializer: JSON)), + RubyEventStore::Dispatcher.new), ) # Subscribe public event handlers below From c8683d1ff2c9c9fac4fce3aec0d124678afa6d70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miros=C5=82aw=20Prag=C5=82owski?= Date: Tue, 15 Sep 2020 10:32:19 +0200 Subject: [PATCH 35/35] Align with models factory introduced recently --- .../orders/lib/orders.rb | 2 +- .../payments/lib/payments.rb | 2 +- .../shipping/lib/shipping.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb index 3c3ccfd494..dc6f64cee8 100644 --- a/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb +++ b/contrib/multiple_databases_repository_sample_app/orders/lib/orders.rb @@ -37,7 +37,7 @@ def self.setup(config) ) @@module_event_store = RailsEventStore::Client.new( repository: RailsEventStoreActiveRecord::EventRepository.new( - Orders::ApplicationRecord, serializer: JSON), + model_factory: RailsEventStoreActiveRecord::WithAbstractBaseClass.new(Orders::ApplicationRecord), serializer: JSON), dispatcher: RubyEventStore::ComposedDispatcher.new( RubyEventStore::ImmediateAsyncDispatcher.new(scheduler: RailsEventStore::ActiveJobScheduler.new(serializer: JSON)), RubyEventStore::Dispatcher.new), diff --git a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb index fe8372d2d5..2d6b430a15 100644 --- a/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb +++ b/contrib/multiple_databases_repository_sample_app/payments/lib/payments.rb @@ -40,7 +40,7 @@ def self.setup(config) ) @@module_event_store = RailsEventStore::Client.new( repository: RailsEventStoreActiveRecord::EventRepository.new( - Payments::ApplicationRecord, serializer: JSON), + model_factory: RailsEventStoreActiveRecord::WithAbstractBaseClass.new(Payments::ApplicationRecord), serializer: JSON), dispatcher: RubyEventStore::ComposedDispatcher.new( RubyEventStore::ImmediateAsyncDispatcher.new(scheduler: RailsEventStore::ActiveJobScheduler.new(serializer: JSON)), RubyEventStore::Dispatcher.new), diff --git a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb index 3b57e04c7b..4db2c0e274 100644 --- a/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb +++ b/contrib/multiple_databases_repository_sample_app/shipping/lib/shipping.rb @@ -29,7 +29,7 @@ def self.setup(config) ) @@module_event_store = RailsEventStore::Client.new( repository: RailsEventStoreActiveRecord::EventRepository.new( - Shipping::ApplicationRecord, serializer: JSON), + model_factory: RailsEventStoreActiveRecord::WithAbstractBaseClass.new(Shipping::ApplicationRecord), serializer: JSON), dispatcher: RubyEventStore::ComposedDispatcher.new( RubyEventStore::ImmediateAsyncDispatcher.new(scheduler: RailsEventStore::ActiveJobScheduler.new(serializer: JSON)), RubyEventStore::Dispatcher.new),