Search code examples
c#.net-coredependency-injectionsoftware-designn-tier-architecture

How will i resolve Dependency of interfaces which is on 3rd or 4rth layer


I am creating an architecture in Dotnet Core 3.1.

I have created Layers

API

-> Controller

-> Service Interface. (This will be used by Controller Layer)

-> Service Implementation

-> Data Interface. (This will be used by Service Implementation Layer as a dependency)

-> Data Implementation

I do not want to expose my Data Implementation to the Controller layer but it has to use in the constructor of the Service Implementation Layer.

The question is:

How to resolve the Data Implementation Classes?

And how to register these classes in IServiceCollection?


Solution

  • You can do that by:

    1. Defining Separate Projects for each Layer.
    2. Setting the Dependencies among Layers as much as you need.
    3. Model mapping should be in Service Layer rather than in Controller layer (Normally should be in Controller Layer but in your scenario is not applicable).
    4. Install Microsoft.Extensions.DependencyInjection NuGet in your Service Layer
    5. Define Extension Method for DependencyInjection in Service Layer.
    6. Call the extension method in Startup.cs

    I prepared an example explaining the answer.

    The Example:

    The Solution Skeleton with Dependencies: Solution Skeleton and Project Dependencies

    Example Products Endpoint interfaces and classes distribution over solution Example endpoint interfaces and classes distribution

    Dependency Injection for the endpoint services Dependency Injection for the endpoint services

    Controller Class Code:

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Logging;
    using TestProj.AppServices.Interfaces;
    
    namespace TestProj.Api.Controllers
    {
        [ApiController]
        [Route("[controller]")]
        public class ProductsController : ControllerBase
        {
            private readonly ILogger<ProductsController> _logger;
            private readonly IProductService _service;
    
            public ProductsController(ILogger<ProductsController> logger,
                                      IProductService service)
            {
                _logger = logger;
                _service = service;
            }
    
            [HttpGet("{id=1}")] //Set default value for example only, it should be [HttpGet("{id}")]
            public IActionResult Get(int id)
            {
                return Ok(_service.GetById(id));
            }
        }
    }
    

    ProductService

        using TestProj.AppServices.Interfaces;
        using TestProj.AppServices.Models;
        using TestProj.Data.Interfaces;
        
        namespace TestProj.AppServices.AppServices
        {
            public class ProductService : IProductService
            {
                private readonly IProductRepository _repository;
        
                public ProductService(IProductRepository repository)
                {
                    _repository = repository;
                }
                public Product GetById(int id)
                {
                    //Subject code here
        
                    //Dummy code:
                    var productFromDataLayer = _repository.GetById(id);
        
                    //Mapping (You can use AutoMapper NuGet)
                    var product = new Product
                    {
                        Id = productFromDataLayer.Id,
                        Name = productFromDataLayer.Name
                    };
        
                    return product;
                }
            }
        }
    

    IProductService

    using TestProj.AppServices.Models;
    
    namespace TestProj.AppServices.Interfaces
    {
        public interface IProductService
        {
            Product GetById(int id);
        }
    }
    

    Product Service Layer Model

    namespace TestProj.AppServices.Models
    {
        public class Product
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }
    }
    

    ProductRepository

    using TestProj.Data.Interfaces;
    using TestProj.Data.Model;
    
    namespace TestProj.Data.Data
    {
        public class ProductRepository : IProductRepository
        {
            public Product GetById(int id)
            {
                //Subject code here
    
                //Dummy code:
                var product = new Product
                {
                    Id = 1,
                    Name = "Product 1"
                };
    
                return product;
            }
        }
    }
    

    IProductRepository

    using TestProj.Data.Model;
    
    namespace TestProj.Data.Interfaces
    {
        public interface IProductRepository
        {
            Product GetById(int id);
        }
    }
    

    Data Layer Product Model (Entity)

    namespace TestProj.Data.Model
    {
        public class Product
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }
    }
    

    Dependency Injection Extension Class

    using Microsoft.Extensions.DependencyInjection;
    using TestProj.AppServices.AppServices;
    using TestProj.AppServices.Interfaces;
    using TestProj.Data.Data;
    using TestProj.Data.Interfaces;
    
    namespace TestProj.AppServices.Others
    {
        public static class DependencyInjection
        {
            public static void AddProjectServicesAndRepositoresDependencyInjection(this IServiceCollection services)
            {
    
                //Services
                services.AddTransient<IProductService, ProductService>();
    
                //Data
                services.AddTransient<IProductRepository, ProductRepository>();
            }
        }
    }
    

    Startup class

    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using TestProj.AppServices.Others;
    
    namespace TestProj.Api
    {
        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddProjectServicesAndRepositoresDependencyInjection();
    
                services.AddControllers();
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseHttpsRedirection();
    
                app.UseRouting();
    
                app.UseAuthorization();
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                });
            }
        }
    }
    

    I have uploaded the source code of the example to GitHub https://github.com/ualehosaini/LayeredArchitecturePreparedToAnswerForAStackOverflowQuestion . You can use it for your projects, you can define/add whatever component you need.