Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice in dependency injection

This is a question about how best to do DI, so it's not tied to any particular DI/IoC framework because, well, framework should be chosen based on pattern and practice rather than the other way around, no?

I'm doing a project where repository has to be injected into services, a service may require multiple repositories and I'm curious about the pros and cons between following approaches:

  1. Inject repositories in service constructor

    public class SomeService : ISomeService
    {
        private IRepository1 repository1;
        private IRepository2 repository2;
    
        public SomeService(IRepository1 repository1, IRepository2 repository2)
        {
              this.repository1 = repository1;
              this.repository2 = repository2;
        }
    
        public void DoThis()
        {
              //Do something with repository1
        }
        public void DoThat()
        {
              //Do something with both repository1 and repository2
        }
    }
    
  2. Inject a custom context class that include everything any service may need but lazy instantiated (the IServiceContext will be a protected field in BaseService)

    public class SomeService : BaseService, ISomeService
    {
        public SomeService(IServiceContext serviceContext)
        {
              this.serviceContext= serviceContext;
        }
    
        public void DoThis()
        {
              //Do something with serviceContext.repository1
        }
        public void DoThat()
        {
              //Do something with both serviceContext.repository1 and serviceContext.repository2
        }
    }
    
  3. Inject into methods that need them only

    public class SomeService : ISomeService
    {
        public void DoThis(IRepository1 repository1)
        {
              //Do something with repository1
        }
        public void DoThat(IRepository1 repository1, IRepository2 repository2)
        {
              //Do something with both repository1 and repository2
        }
    }
    

Some pointers would be appreciated, moreover what're the aspects that I should consider in evaluating alternative like these?

like image 613
Yellowmoon Avatar asked Feb 03 '23 13:02

Yellowmoon


2 Answers

The preferred way of injecting dependencies is Constructor Injection.

Method Injection is less ideal, because this will quickly result in having to pass around many dependencies from service to service and it will cause implementation details (the dependencies) to leak through the API (your method).

Both options 1 and 2 do Constructor Injection, which is good. If you find yourself having to inject too many dependencies in a constructor, there is something wrong. Either you are violating the Single Responsibility Principle, or you are missing some sort of aggregate service, and this is what you are doing in option 2.

In your case however, your IServiceContext aggregate service is grouping multiple repositories together. Many repositories behind one class smells like a unit of work to me. Just add a Commit method to the IServiceContext and you will surely have a unit of work. Think about it: don't you want to inject an IUnitOfWork into your service?

like image 123
Steven Avatar answered Feb 05 '23 16:02

Steven


The first option seems to be the most natural from a DI perpective. The service class requires both repositories to perform its function, so making them required in order to construct an instance makes sense semantically (and practically).

The second option sounds a bit like Service Location, which is generally considered an anti-pattern (see http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx). In a nutshell, it creates implicit dependencies, where explicit dependencies are always preferred.

like image 24
Phil Sandler Avatar answered Feb 05 '23 16:02

Phil Sandler