Other Articles in the Series
- Overview
- Add new Permission
- Project structure
- Multi-Languages (i18n)
- DI & IoC - Why and Why not?
- RESTful & WebApi
- Manage Application Lifecycle
- Build & Deploy Application
- New version of TinyERP with Angular 2 (typescript)
- CQRS: Avoiding performance issues in enterprise app (basic)
- Multiple data-stores: Scale your repository (Part 1)
- Multiple data-stores: Scale your repository (Part 2)
- Basic authentication (user name/ password) with OWIN
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 patternt that help us inejcting 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 inejcted 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
)
{
}
public void SomeMethod(){
}
}
}
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, ...
- 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.
- 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:
namespace App.Services.Impl
{
public class DistributionListService : IDistributionListService
{
public DistributionListService()
{
}
public void SomeMethod(){
IResitory1 repo1 = IoC.Resolve<IResitory1>();
IResitory2 repo2 = IoC.Resolve<IResitory2>();
IResitory3 repo3 = IoC.Resolve<IResitory3>();
}
}
}
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.