TU Tran

Technologies should serve for business purpose.

NAVIGATION - SEARCH

[TinyERP]DI & IoC - Why and Why not?

The new version for this was in progress.

Introduction

I have looked at IoC and DI in my code.

Hope this can help us use IoC and DI in a good manner.

I did not tell you that DI is not good or IoC is better than DI. From my view, each tech has it own pros and cons. So It is better if we know when we can use which techs for our business.

How to Get the Code

Please check out the code at https://github.com/techcoaching/TinyERP.

DI - Dependency Injection

DI is a good pattern that help us injecting required references:

class UserService : IUserService{
    private IUserRepository userRepository;
    public UserService(IUserRepository userRepository)
    {
        this.userRepository = userRepository;
    }
    public void UserRepositoryForSomeActio(){
        this.userRepository.<callSomeMethod>();
    }
}

As code above, we see that the userRepository instance will be injected automaticly, it was also ready for us in UserRepositoryForSomeActio method.

This is cool, as we can focus on business of function.

And this is good in the context of small application. Let look how is it in more complext application type (I mean in enterprise application now a day).

In Enterprise Application, we have many core object/ major object where it has many dependency on other objects.

For example: Order object, we can have dependency on those smaller object:

  • User (who created order)
  • Orderline
  • Category
  • Product
  • Promotion
  • Unit of Measurement (UOM)
  • Branch
  • ...

So OrderService may require injecting appropriated repository for all of dependency objects as code below:

class OrderService : IOrderService{
    private IUserRepository userRepository;
    private IBranchRepository branchRepository;
    private IOrderLineRepository orderlineRepository;
    private IOrderCategoryRepository orderCategoryRepository;
    private IProductRepository productRepository;
    private IUOMRepository uomRepository;
    public UserService(
        IUserRepository userRepository,
        IBranchRepository branchRepository,
        IOrderLineRepository orderlineRepository,
        IOrderCategoryRepository orderCategoryRepository,
        IProductRepository productRepository,
        IUOMRepository uomRepository,
    )
    {
        this.userRepository = userRepository;
        this.branchRepository = branchRepository;
        this.orderlineRepository = orderlineRepository;
        this.orderCategoryRepository = orderCategoryRepository;
        this.productRepository = productRepository;
        this.uomRepository = uomRepository;
        
    }
    public void UserRepositoryForSomeActio(){
        this.userRepository.<callSomeMethod>();
    }
}

There are some points, I want to raise here:

  • We need to define the list of private varaibles for holding the instance of appropriated repositories (such as: private IUOMRepository uomRepository;)
  • The constructor has many parameters.
  • The body of constructor mostly assigns the injected instances to private parameters (such as:  this.userRepository = userRepository;)

This is the real code for my application using Dependency Injection for one of the core entities of system:

namespace App.Services.Impl
{
    public class DistributionListService : IDistributionListService
    {
        private IDistributionListRepository _distributionListRepository;
        private IDistributionPeriodRepository _distributionPeriodRepository;
        private IGeoRouteNorpostRepository _geoRouteNorpostRepository;
        private IUnaddressedOrderRepository _unaddressedOrderRepository;
        private IAddressedOrderSummaryPerRouteRepository _addressedOrderSummaryPerRouteRepository;
        private IOrderUnaddressedGeoRouteRepository _orderUnaddressedGeoRouteRepository;
        private IUserRepository _userRepository;
        private IVehicleRepository _vehicleRepository;
        private IGeoRoutePackageRepository _routePackageRepository;
        private IGeoRoutePackageRouteRepository _geoRoutePackageRouteRepository;
        private IDepartmentRepository _departmentRepository;
        private IAddressedOrderItemsDeliveryTimeRepository _addressedOrderItemsDeliveryTimeRepository;
        private ISortingTimeRepository _sortingTimeRepository;
        private IOrderRepository _orderRepository;
        private IDepartmentDistributionPeriodExecutionStatusRepository _distributionPeriodExecutionStatusRepository;
        private IDistributionPeriodService _distributionPeriodService;
        private IGeoRouteHouseholdService _geoRouteHouseholdService;

        public DistributionListService(
            IDistributionPeriodService distributionPeriodService,
            IGeoRoutePackageRouteRepository geoRoutePackageRouteRepository,
            IGeoRouteHouseholdService geoRouteHouseholdService,
            IDistributionListRepository distributionListRepository,
            IDistributionPeriodRepository distributionPeriodRepository,
            IGeoRouteNorpostRepository geoRouteNorpostRepository,
            IUnaddressedOrderRepository unaddressedOrderRepository,
            IAddressedOrderSummaryPerRouteRepository addressedOrderSummaryPerRouteRepository,
            IOrderUnaddressedGeoRouteRepository orderUnaddressedGeoRouteRepository,
            IUserRepository userRepository,
            IVehicleRepository vehicleRepository,
            IGeoRoutePackageRepository geoRoutePackageRepository,
            IDepartmentRepository departmentRepository,
            IAddressedOrderItemsDeliveryTimeRepository addressedOrderItemsDeliveryTimeRepository,
            ISortingTimeRepository sortingTimeRepository,
            IDepartmentDistributionPeriodExecutionStatusRepository departmentDistributionPeriodExecutionStatusRepository
            )
        {
            //assign those injected to private variables
        }
        public void SomeMethod(){
            // Use 3 of above repositories
        }
    }
}

 

In specified method of those service class, we use just some of them (mostly around 3).

Let imaging the execution flow of the calling to "SomeMethod" of "DistributionListService"  above at the fist time.

  • The system looks for instance of IDistributionListService
  • System recognises that this implementation has dependency on 15 other repositories.
  • Those repositories will be created and passed as parameter of  DistributionListService cosntructor.
  • System invokes "SomeMethod" and consumes 3 of them for its business logic.

Review again, We create instance of 15 repositories. This process may require attention to create other dependency objects to those repositories.

Consequence, the long list of objects need to be create before doing the business logic.

In DI, we can manage the life time of the instance (search "IoC" or "Inversion Of Control" on internet ), for example: Single, Transient, ...

  1. With Singleton:
  • All registered types will be create once.
  • The next time, We can get it back from memory.

In the context of enterprise application, we will have many classes always existed in memory of server and only part of them frequenly used. It means, we did not use memory of server well enough.

  1. With Transient:
  • All registered types will be create once.

In the context of enterprise application, we will have core objects/ major objects that have many dependency object (as sample above). So creating instance of this type requires a lot of other dependency types created in subsequency and use some of them. It means, we did not use the power of server good enough.

IoC - Inversion Of Control

For improving above issue, I decide to use IoC in method. I mean, we will resolve and use appropriated repositories if need.

The code above can be changed to:

{
    public class DistributionListService : IDistributionListService
    {
        public DistributionListService()
        {
            //oh yeah, I'm free and can be removed from the class now
        }
        public void SomeMethod(){
            IResitory1 repo1 = IoC.Resolve<IResitory1>();
            IResitory2 repo2 = IoC.Resolve<IResitory2>();
            IResitory3 repo3 = IoC.Resolve<IResitory3>();
            // and use above repositories for logic business
        }
    }
}

In this cde, we can see:

  • it looks nicer.
  • We can add or remove unused dependency types without impact to other code.
  • We resolve what we need to use. all of those repos will be disposed as default.

So in this way, I think we can balance between memory and power of server in context of enterprise application.

For more information about other articles in this series

 

 

Thank you for reading,

Note: Please like and share to your friends if you think this is useful article, I really appreciate

 

 

Comments (3) -

Hi Mr.Tu Tran,

I read your post and understand what is the pros of IoC bring in project, but i would like to you can teaching step by step to build the IoC that we are using before.

Thanks,
Ho Tran.

Reply

Should we have a tutorial how can we implement DI by method?

Reply

Thank for asking me your question.
Please contact me on Skype. We can discuss in detail about this.
My Skype is tranthanhtu83.
Regards,

Reply

Add comment