Home > Mobile >  calling a service method from inside another service class [closed]
calling a service method from inside another service class [closed]

Time:09-16

Is it a good idea to call a service method from another service class ?

class AbcService
  def initialize(user_id)
    @user = User.find_by_id user_id
  end

  def find_abc
    # some code here
    XyzService.new().find_xyz
  end
end

class XyzService
  def find_xyz
    # some code here
  end
end

CodePudding user response:

Yes, It is okay to call a service method from another service class. In order to keep your code DRY you can do that.

def find_xyz
   # some code here
end

If you need this code, multiple times and you want to keep your code structure clean you can do this.

CodePudding user response:

Yes, in this case the service XyzService is called a dependency of AbcService.

One common pattern is to provide dependencies as optional initialization arguments, and wrapped with getters, in order to ease testing and allow more configurability:

class AbcService
  def initialize(user_id, xyz_service: nil)
    # set up dependencies
    @xyz_service = xyz_service

    # rest of initialization
    @user = User.find_by_id user_id
  end

  def find_abc
    # some code here
    xyz_service.find_xyz
  end

  def xyz_service
    @xyz_service ||= XyzService.new
  end
end

And a further step is to allow dependency control from a centralized repository

# Dependency repository
module Repo
  DEPS = {
    xyz_service: ->{ XyzService.new },
    # ...
  }

  # returns a module with one accessor:
  # 
  # Repo[:xyz_service]
  #
  # is the same as
  #
  # module (Anonymous)
  #   def xyz_service
  #     @xyz_service ||= Repo::DEPS[:xyz_service].call
  #   end
  # end
  def self.[](dependency_name)
    Module.new do
      define_method(dependency_name) do
        @_deps_cache ||= {}
        @_deps_cache[dependency_name] ||= Repo::DEPS[dependency_name].call
      end
    end
  end
end


class AbcService
  include Repo[:xyz_service]

  def initialize(user_id)
    @user = User.find_by_id user_id
  end

  def find_abc
    # some code here
    xyz_service.find_xyz
  end
end

With both the above patterns, you can do the following in your tests:

it 'calls the xyz_service' do
  xyz = double(:xyz_service)
  allow(subject).to receive(:xyz_service).and_return(xyz)
  allow(xyz).to receive(:find_xyz).and_return('foo')

  expect(subject.find_abc).to eq('foo')
end

in other words, you can replace dependencies with mocks during testing.

  • Related