Jseb
Jseb

Reputation: 1934

Service Layer Dependency Injection

I am creating a Web API that would call a service layer, and I am trying to learn Dependency Injection, ( i am hoping to use ninject), but I am unsure on how to create dependency on the service layer.

This is what the web api would call.

enter image description here

Here the issues, when calling IPersonService, the person would have a gender defined, it would have a name, a role, and ethnicity. I am using constructor injection and unsure if I should call the GenderService or should I call the business layer ( in this case defined by Core).

enter image description here

Should I be calling the service like the picture above, or below

enter image description here

This is what my person service look like

namespace Service.Services
{
    public class PersonService : IPersonService
    {
        private IPersonCore personCore = null;
        private INameService nameService = null;
        private IRoleService roleService = null;
        private IGenderService genderService = null;
        private IEthnicityService ethnicityService = null;
        private IPrefixService prefixService = null;
        private Person currUser;

        public PersonService(IPersonCore _personcore, INameService _namecore, IRoleService _roleservice, IGenderService _genderservice, IEthnicityService _ethnicityservice, IPrefixService _prefixservice )
        {
            this.personCore = _personcore;
            this.nameService = _namecore;
            this.roleService = _roleservice;
            this.genderService = _genderservice;
            this.ethnicityService = _ethnicityservice;
            this.prefixService = _prefixservice;
        }

        public IEnumerable<Person> GetAllPerson()
        {
            if (isAuthorized())
            {
                return this.personCore.GetPersons();
            }
            return null;
        }

        public Person GetPersonByID(int id)
        {
            if (isAuthorized())
            {
                return this.personCore.GetPersonByID(id);
            }
            return null;
        }

        public Person GetPersonByEmail(string email)
        {
            if (isAuthorized())
            {
                return this.personCore.GetPersonByEmail(email);
            }
            return null;
        }

        public IEnumerable<Person> GetPersonByName(string first, string last, string middle)
        {
            if(isAuthorized())
            { 
                Name newname = this.nameService.CreateName(first, last, middle);
                return this.personCore.GetPersonByName(newname);
            }
            return null;
        }

        public IEnumerable<Person> GetPersonWithRoles(IEnumerable<Roles> r)
        {
        }

        public IEnumerable<Person> GetPersonWithDOB(DateTime d)
        {
            if (isAuthorized())
            {
                return this.personCore.GetPersonWithDOB(d);
            }
            return null;
        }

        public Person SetPersonRole(int id, Roles r)
        {
        }

        public Person SetGender(int id, Gender g)
        {
        }

        public Person SetEthnicity(int id, Ethnicity e)
        {
        }

        public Person SetPrefix(int id, Prefix p)
        {
        }

        public Person CreatePerson(Person p)
        {
            if (isAuthorized())
            {
                return personCore.AddPerson(p);
            }
            return null;
        }

        public Person UpdatePerson(Person p)
        {
            if (isAuthorized())
            {
                return personCore.UpdatePerson(p);
            }
            return null;
        }

        public Person ActivatePerson(int id)
        {
            if (isAuthorized())
            {
                return personCore.ActivatePerson(id);
            }
            return null;
        }

        public Person DeactivatePerson(int id)
        {
            if (isAuthorized())
            {
                return personCore.DeactivatePerson(id);
            }
            return null;
        }

        public bool DeletePerson(int id)
        {
            if (isAuthorized())
            {
                return personCore.DeletePerson(id);
            }
            return false;
        }

        protected bool isAuthorized()
        {
            //Probably move to common
            return true;
        }
    }
}

When calling from the Web API my issue with it, is its sound like a lot of dependency for finding something about a certain person.

Upvotes: 0

Views: 2097

Answers (2)

Faycal Hassani
Faycal Hassani

Reputation: 51

You can simplify this in 2 ways:

  1. Your PersonService does not look to depend itself on the Role, Gender, Ethnicity and Prefix services as you do not call them from its methods. It's rather a shell that your client code calls instead of calling directly these services themselves. If that is the case, then you can simplify your PersonService by getting these 4 dependencies out:

    private IRoleService roleService = null;
    private IGenderService genderService = null;
    private IEthnicityService ethnicityService = null;
    private IPrefixService prefixService = null;
    

    And these methods out to their respective services:

    public Person SetPersonRole(int id, Roles r)
    {
    }
    public Person SetGender(int id, Gender g)
    {
    }
    public Person SetEthnicity(int id, Ethnicity e)
    {
    }
    public Person SetPrefix(int id, Prefix p)
    {
    }
    
  2. If you need absolutely to keep these methods inside your IPersonService, then you would inject the dependencies in the methods rather than the constructor.

    Regarding the dependency on the Service or the Core, it depends on what your service does. If your service is just calling the Core, then go to the Core yourself. If your Service is doing some validation or anything else, you may want to depend on it to avoid copying the same code in the PersonService.

Upvotes: 1

Steven
Steven

Reputation: 172606

The PersonService class contains many dependencies, because you are violating the Single Responsibility Principle. This class has many responsibilities and you'll end up changing this class every time you are adding a new feature (which is a Open/Closed Principle violation). Besides, the isAuthorized method is a cross-cutting concern, which this class should have no notion of.

On top of that, you are injecting the current logged in user into the PersonService. This is runtime data, and constructing application components using runtime data is an anti-pattern.

There are many ways to fix this, but it all comes down to understanding the SOLID principles. Doing so however might be a daunting task, especially if you're just starting with DI and software design. There are many books you can read about this, such as the amazing work from Robert C. Martin and books about Dependency Injection like Mark Seemann's Dependency Injection in .NET.

Designs that helped me out a lot the last few years are message based architectures, where use cases are described by messages, and implementations are described by generic abstractions (read this and this). These designs have proven to be very flexible and maintainable, since they allow adding cross-cutting concerns transparently and allow adding new features without changing any existing code. Such design also allows reducing your Web API layer to a simple piece of infrastructure that doesn't require changing when new features are being added. You can read about this concept here (note: that article is about WCF, but the concept for Web API is the same) and here is a Github repository that shows how to implement this both in WCF and Web API.

I wish you the best of luck in your quest to software mastery.

Upvotes: 1

Related Questions