Browse Source

Bootstrap with event_sourcery_cli

main
Bèr Kessels 1 year ago
parent
commit
8c4593ba61

+ 1
- 0
.ruby-version View File

@@ -0,0 +1 @@
2.7.2

+ 19
- 0
Gemfile View File

@@ -0,0 +1,19 @@
source 'https://rubygems.org'

gem 'event_sourcery', git: 'https://github.com/envato/event_sourcery.git'
gem 'event_sourcery-postgres', git: 'https://github.com/envato/event_sourcery-postgres.git'

gem 'rake'
gem 'sinatra'
# NOTE: pg is an implicit dependency of event_sourcery-postgres but we need to
# lock to an older version for deprecation warnings.
gem 'pg', '0.20.0'

group :development, :test do
gem 'pry'
gem 'rspec'
gem 'rack-test'
gem 'database_cleaner'
gem 'better_errors'
gem 'shotgun'
end

+ 81
- 0
Gemfile.lock View File

@@ -0,0 +1,81 @@
GIT
remote: https://github.com/envato/event_sourcery-postgres.git
revision: 9fa5cec446e9335edb5b8d4aa2517d383c73b076
specs:
event_sourcery-postgres (0.8.1)
event_sourcery (>= 0.14.0)
pg
sequel (>= 4.38)

GIT
remote: https://github.com/envato/event_sourcery.git
revision: 343a0e40040920be9c0fe3692dfde50d5f9407cc
specs:
event_sourcery (0.23.1)

GEM
remote: https://rubygems.org/
specs:
better_errors (2.8.3)
coderay (>= 1.0.0)
erubi (>= 1.0.0)
rack (>= 0.9.0)
coderay (1.1.3)
database_cleaner (1.8.5)
diff-lcs (1.4.4)
erubi (1.9.0)
method_source (1.0.0)
mustermann (1.1.1)
ruby2_keywords (~> 0.0.1)
pg (0.20.0)
pry (0.13.1)
coderay (~> 1.1)
method_source (~> 1.0)
rack (2.2.3)
rack-protection (2.1.0)
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rake (13.0.1)
rspec (3.9.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0)
rspec-mocks (~> 3.9.0)
rspec-core (3.9.3)
rspec-support (~> 3.9.3)
rspec-expectations (3.9.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-mocks (3.9.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-support (3.9.3)
ruby2_keywords (0.0.2)
sequel (5.37.0)
shotgun (0.9.2)
rack (>= 1.0)
sinatra (2.1.0)
mustermann (~> 1.0)
rack (~> 2.2)
rack-protection (= 2.1.0)
tilt (~> 2.0)
tilt (2.0.10)

PLATFORMS
ruby

DEPENDENCIES
better_errors
database_cleaner
event_sourcery!
event_sourcery-postgres!
pg (= 0.20.0)
pry
rack-test
rake
rspec
shotgun
sinatra

BUNDLED WITH
2.1.4

+ 2
- 0
Procfile View File

@@ -0,0 +1,2 @@
web: ./script/server
processors: bundle exec rake run_processors

+ 39
- 0
README.md View File

@@ -0,0 +1,39 @@
# Roost

[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)

## Get started

Ensure you have Postgres and Ruby 2.3 or higher installed, then run the setup script:

```sh
$ ./script/setup
```

## Using the Application

Start the web server and processors (reactors and projectors):

```sh
$ foreman start
```

## Adding features

Generate a new aggregate, command and event:

```sh
$ eventsourcery generate:command recipe add
```

Generate a query and projection that subscribes to events:

```sh
$ eventsourcery generate:query active_recipes recipe_added
```

Generate a reactor that subscribes to events:

```sh
$ eventsourcery generate:reactor recipe_publisher recipe_added recipe_deleted
```

+ 69
- 0
Rakefile View File

@@ -0,0 +1,69 @@
$LOAD_PATH.unshift '.'

task :environment do
require 'config/environment'
end

desc 'Run Event Stream Processors'
task run_processors: :environment do
puts 'Starting Event Stream processors'

event_source = Roost.event_source
tracker = Roost.tracker
db_connection = Roost.projections_database

# Need to disconnect before starting the processors
# to ensure each forked process has its own connection
db_connection.disconnect

# Show our ESP logs in foreman immediately
$stdout.sync = true

processors = [
# Add your processors here, like so:
#
# EventSourceryTodoApp::Projections::CompletedTodos::Projector.new(
# tracker: tracker,
# db_connection: db_connection,
# ),
]

EventSourcery::EventProcessing::ESPRunner.new(
event_processors: processors,
event_source: event_source,
).start!
end

namespace :db do
desc 'Create database'
task create: :environment do
url = Roost.config.database_url
database_name = File.basename(url)
database = Sequel.connect URI.join(url, '/template1').to_s
begin
database.run("CREATE DATABASE #{database_name}")
rescue StandardError => e
puts "Could not create database '#{database_name}': #{e.class.name} #{e.message}"
end
database.disconnect
end

desc 'Drop database'
task drop: :environment do
url = Roost.config.database_url
database_name = File.basename(url)
database = Sequel.connect URI.join(url, '/template1').to_s
database.run("DROP DATABASE IF EXISTS #{database_name}")
database.disconnect
end

desc 'Migrate database'
task migrate: :environment do
database = EventSourcery::Postgres.config.event_store_database
begin
EventSourcery::Postgres::Schema.create_event_store(db: database)
rescue StandardError => e
puts "Could not create event store: #{e.class.name} #{e.message}"
end
end
end

+ 16
- 0
app.json View File

@@ -0,0 +1,16 @@
{
"name": "Roost",
"description": "Roost, an EventSourcery app",
"keywords": ["Roost", "ruby", "event_sourcery", "cqrs"],
"formation": {
"web": {
"quantity": 1
},
"processors": {
"quantity": 1
}
},
"scripts": {
"postdeploy": "bundle exec rake db:migrate"
}
}

+ 0
- 0
app/aggregates/.gitkeep View File


+ 0
- 0
app/commands/.gitkeep View File


+ 0
- 0
app/events/.gitkeep View File


+ 0
- 0
app/projections/.gitkeep View File


+ 0
- 0
app/reactors/.gitkeep View File


+ 59
- 0
app/web/server.rb View File

@@ -0,0 +1,59 @@
require 'sinatra'

Dir.glob(__dir__ + '/../commands/**/*.rb').each { |f| require f }
Dir.glob(__dir__ + '/../projections/**/query.rb').each { |f| require f }

module Roost
class Server < Sinatra::Base
BadRequest = Class.new(StandardError)
UnprocessableEntity = Class.new(StandardError)

# Ensure our error handlers are triggered in development
set :show_exceptions, :after_handler

configure :development do
require 'better_errors'
use BetterErrors::Middleware
BetterErrors.application_root = __dir__
end

error UnprocessableEntity do |error|
body "Unprocessable Entity: #{error.message}"
status 422
end

error BadRequest do |error|
body "Bad Request: #{error.message}"
status 400
end

before do
content_type :json
end

def json_params
# Coerce this into a symbolised Hash so Sinatra data
# structures don't leak into the command layer

request_body = request.body.read
unless request_body.empty?
params.merge!(JSON.parse(request_body))
end

Hash[
params.map{ |k, v| [k.to_sym, v] }
]
end

# Add your API routes here!
#
# eg.
# get '/todos' do
# ...
# end
#
# post '/todo/:id' do
# ...
# end
end
end

+ 6
- 0
config.ru View File

@@ -0,0 +1,6 @@
$LOAD_PATH << '.'

require 'config/environment'
require 'app/web/server'

run Roost::Server.new

+ 64
- 0
config/environment.rb View File

@@ -0,0 +1,64 @@
require 'event_sourcery'
require 'event_sourcery/postgres'

Dir.glob(__dir__ + '/../app/events/*.rb').each { |f| require f }
Dir.glob(__dir__ + '/../app/reactors/*.rb').each { |f| require f }
Dir.glob(__dir__ + '/../app/projections/**/projector.rb').each { |f| require f }

module Roost
class Config
attr_accessor :database_url
end

def self.config
@config ||= Config.new
end

def self.configure
yield config
end

def self.environment
ENV.fetch('RACK_ENV', 'development')
end

def self.event_store
EventSourcery::Postgres.config.event_store
end

def self.event_source
EventSourcery::Postgres.config.event_store
end

def self.tracker
EventSourcery::Postgres.config.event_tracker
end

def self.event_sink
EventSourcery::Postgres.config.event_sink
end

def self.projections_database
EventSourcery::Postgres.config.projections_database
end

def self.repository
@repository ||= EventSourcery::Repository.new(
event_source: event_source,
event_sink: event_sink
)
end
end

Roost.configure do |config|
config.database_url = ENV['DATABASE_URL'] || "postgres://127.0.0.1:5432/roost_#{Roost.environment}"
end

EventSourcery::Postgres.configure do |config|
database = Sequel.connect(Roost.config.database_url)

# NOTE: Often we choose to split our events and projections into separate
# databases. For the purposes of this example we'll use one.
config.event_store_database = database
config.projections_database = database
end

+ 38
- 0
test/spec_helper.rb View File

@@ -0,0 +1,38 @@
ENV['RACK_ENV'] = 'test'

require 'rack/test'
require 'securerandom'
require 'database_cleaner'

$LOAD_PATH << '.'

require 'config/environment'
require 'app/web/server'
require 'spec/support/request_helpers'

RSpec.configure do |config|
config.include(Rack::Test::Methods, type: :request)
config.include(RequestHelpers, type: :request)

config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end

config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end

config.shared_context_metadata_behavior = :apply_to_host_groups
config.disable_monkey_patching!
config.order = :random
Kernel.srand config.seed

EventSourcery.configure do |config|
config.logger = Logger.new(nil)
end

config.before do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean
end
end

+ 10
- 0
test/support/request_helpers.rb View File

@@ -0,0 +1,10 @@
module RequestHelpers
def app
@@app ||= Roost::Server
end

def last_event(aggregate_id)
Roost.event_store
.get_events_for_aggregate_id(aggregate_id).last
end
end

+ 54
- 0
test/test_helper.rb View File

@@ -0,0 +1,54 @@
# frozen_string_literal: true

require 'minitest/autorun'
require 'database_cleaner'

require 'awesome_print'
require 'byebug'
require 'ostruct'

ENV['APP_ENV'] = ENV['RACK_ENV'] = 'test'
$LOAD_PATH << '.'

require 'config/environment'
require 'app/web/server'

require_relative 'support/data_helpers.rb'
require_relative 'support/event_helpers.rb'
require_relative 'support/file_helpers.rb'
require_relative 'support/location_helpers.rb'
require_relative 'support/request_helpers.rb'
require_relative 'support/time_helpers.rb'
require_relative 'support/web_test_helpers.rb'

Minitest::Test.make_my_diffs_pretty!

module Minitest
class Spec
include EventHelpers
include DataHelpers
include FileHelpers
include LocationHelpers
include RequestHelpers
include TimeHelpers

EventSourcery.configure do |config|
config.logger = Logger.new(nil)
end

DatabaseCleaner.strategy = :truncation,
{ except: %w[spatial_ref_sys query_addresses] }

VCR.configure do |config|
config.cassette_library_dir = 'test/fixtures/vcr_cassettes'
config.hook_into :faraday
end

before :each do
DatabaseCleaner.start
end
after :each do
DatabaseCleaner.clean
end
end
end

Loading…
Cancel
Save