Object as a Service
October 30, 2014

Мне нравится концепция сервис объектов для гурманов, на работе мы её часто используем.
Оригинальный материал можно найти по ссылке , у нас же прижились два правила:
- У сервисного объекта есть метод call (обязательно).
- Название начинается с глагола (за этим не следим, но так само получается).
В проекте среднего размера сервисные объекты хорошо заменяют архитектуру, организуя код в компактные классы. Приведу пару примеров:
ООП:
class AuthenticateUser
def self.call(*args)
new(*args).call
end
# NOTE: пользуемся keyword arguments
# http://brainspec.com/blog/2012/10/08/keyword-arguments-ruby-2-0/
def initialize(cookies, user, permanent: true)
@cookies = cookies
@user = user
@permanent = permanent
end
def call
if @user == "admin"
set_cookie("admin")
elsif @user.is_a?(Customer)
set_cookie("c-#{@user.id}")
elsif @user.is_a?(Reseller)
set_cookie("r-#{@user.id}")
else
raise "unknown user: #{@user.inspect}"
end
end
private
def set_cookie(value)
if @permanent
@cookies.signed.permanent[:uid] = value
else
@cookies.signed[:uid] = value
end
end
end
Простой сервисный объект:
class GetCurrentUser
def self.call(cookies)
if cookies.signed[:uid] == 'admin'
'admin'
else
user_type, user_id = cookies.signed[:uid].to_s.split('-', 2)
if user_type == 'c'
Customer.find(user_id)
elsif user_type == 'r'
Reseller.find(user_id)
end
end
end
end
Сервисными объектами легко думать. Когда я вижу запись
AuthenticateUser.call(cookies, @user)
, сразу догадываюсь, что это сервисный объект,
у которого одна единственная обязанность - логинить пользователя. Не заглядывая в код ясно, что там все в порядке.