Skip to content

Instantly share code, notes, and snippets.

@bestie
Last active December 19, 2015 02:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bestie/5884059 to your computer and use it in GitHub Desktop.
Save bestie/5884059 to your computer and use it in GitHub Desktop.
Another service layer example, arguing for dynamically instantiated services rather than static class methods.

OO Service Layer Example

The revised (OO) AnalyticsService has an SRP violation. It knows:

  • How to capture analytics
  • When it should and shouldn't turn itself off

A more OO approach would to remove the second responsibilty and have something like the above where you have an that is object repsonsible for configuration.

I like to make use of the 'null object' pattern in these scenarios where I have a an innocuous object, in this case a service, that that doesn't do anything.

If that seems heavy weight, it is more ceremony than the typical Rails way but having the interface on services be #call we can substitute the NullAnalyticsService for a lambda, elimating the need for another object.

AnalyticsService = Class.new do
def initialize(dependencies)
# you will probably need to configure something.
end
def call(user, event)
# record analytics
end
end
NullAnalyticsService = Class.new do
def call(user, event)
# doesn't do anything
end
end
Application = Class.new do
def initialize(env)
@env = env
end
def buy_shoes(request)
BuyShoesService.new(
:analytics_service => analytics_service,
).call(request)
end
private
attr_reader :env
def analytics_service
analytics_enabled? ?
AnalyticsService.new(some_dependencies) :
NullAnalyticsService.new
end
def analytics_enabled?
!!env.fetch("ANALYTICS_ENABLED", false)
end
end
ShoesController = Class.new(ApplicatonController) do
def buy
app.buy_shoes(self)
end
private
def app
Application.new(ENV)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment