This is how I usually write use-cases/service objects:
class UserRegistrationForm
def self.call(access_code)
new(access_code).call
end
class UserAPI | |
def find_by_emails(emails) | |
UserApi.fetch_users_by_emails(emails) | |
end | |
end |
class UserAPI | |
def self.find_by_emails(emails) | |
Parallel.map(emails, in_threads: 10) do |email| | |
UserApi.fetch_user_by_email(email) | |
end | |
end | |
end |
module Workflows | |
class OnboardProduction | |
include NetflixInteractor | |
def self.call(production_id:, repo: ProductionRepo.new) | |
super | |
end | |
def call | |
# validate inputs to check if it's okay to onboard a production? |
# using the default data source | |
repo = ProductionRepo.new | |
# swapping for a REST API data source | |
repo = ProductionRepo.new(data_source: MovieProductionAPI) |
class MovieProductionAPI | |
def self.search_by_title(title) | |
SwaggerClient::MovieProductionsApi.advanced_search( | |
search_param: title, | |
headers: { ... }, | |
... | |
) | |
end | |
end |
class MovieProduction < ApplicationRecord | |
def self.search_by_title(title) | |
where('title ILIKE ?', "#{sanitize_sql_like(title)}%") } | |
end | |
end |
class ProductionRepo < BaseRepo | |
entity_class ProductionEntity | |
def initialize(data_source: MovieProduction) | |
super | |
end | |
def find_by_id(id) | |
wrap(data_source.find_by_id(id)) | |
end |
module Workflows | |
class ProductionEntity < BaseEntity | |
attribute :id, AttrType::Strict::Integer | |
attribute :title, AttrType::Strict::String | |
attribute :logline, AttrType::Strict::String.optional | |
end | |
end |
class MemberEligibleForTierRule | |
attr_reader :member, :tier | |
def initialize(member:, tier:) | |
@member, @tier = member, tier | |
end | |
def satisfied? | |
(flew_enough_miles? || flew_enough_segments?) && spent_enough_money? | |
end |