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 |