We have a very large, complex enterprise application that started life in 2005 before IOC containers were widespread in .NET. We would like to retrofit an IOC container as part of our migration to a complete event-driven architecture based on RabbitMQ (and easynetq). As a business we are agreed this will give us a commercial advantage over our competitors.
I feel it’s important to give some preamble as implementation strategy is key:
- 1.5 million+ lines of C# using .net 4, in 600+ projects, 30000+ classes, 40000+ Unit Tests deployed in over 50 different applications end points (command line exe, Windows services, web services etc.).
- The amount of simple CRUD the application has forms less than 10%. Most of the application is complex transaction processing where a single input value can easily pass through 20 to 30 dependencies which maybe talk to 2 or 3 other sub-systems and/or modules.
- We ship a major new release once a month to all customers with all various different configurations and the application is very stable but we still aggressively innovate. We need to migrate over a period of 12 to 36 months.
- Scalability and performance is very important to us. We have load tested to over 1500 transactions per second and 80ms response time. Every millisecond counts.
- Very stable and fairly consistent architecture that continuously evolves in a controlled manor – Development is fairly painless.
At the moment all the dependency injection is constructor based and hand rolled:
public sealed class TestCommandHandler
{
private readonly IUnitOfWork _unitOfWork;
private readonly ITestCommandValidator _testCommandValidator;
public TestCommandHandler(IUnitOfWork unitOfWork)
{
this._unitOfWork = unitOfWork;
this._testCommandValidator = new ITestCommandValidator(unitOfWork);
}
public TestCommandHandler(IUnitOfWork unitOfWork, IValidator testCommandValidator)
{
this._unitOfWork = unitOfWork;
this._testCommandValidator = testCommandValidator;
}
}
Unit of Work contains access to repositories which can easily be mocked:
public class UnitOfWork : IUnitOfWork, IDisposable
{
private IAccountRepository _accountRepository;
public IAccountRepository Account
{
get
{
if(this._accountRepository == null)
{
this._accountRepository = new AccountRepository(this);
}
return this._accountRepository;
}
set
{
this._accountRepository = value;
}
}
//Begin Tx, Commit Tx etc
}
At the moment all the test dependencies are conditionally compiled in debug mode. The release code uses the non-conditional code where we create the concrete dependency. The biggest object dependency is the UnitOfWork which is normally created around every business transaction and passed down the stack. E.g.,
using(var unitOfWork = new UnitOfWork())
{
}
This is normally wrapped inside another class that also supports IDisposable. We also plan to pass in the AccountID into the UnitOfWork so we can easily Shard against different databases moving forward using a mod function.
In order to move to a dependency framework it feels like we need to get the UnitOfWork sorted first but we need a baby steps. I am really looking for advice on the best way to achieve this with such a large application. We plan to do phase 1 over Xmas period where we have a good freeze and merge in all desperate branches into one mainline branch to make big changes.
We are open on which dependency inject framework to use. We have had a bit of a play with StructureMap. We see Ninject has high download stats on Nuget but read it scores poorly on performance. We don’t really want a further migration process to yet another dependency injection framework. So we are open to suggestions. This isn't a religious war on which is best, it’s more important that we have something to migrate into. The most important requirement being it’s fluently configured to avoid config hell.
Other concerns we have are in StructureMap terms, is how we declare the registries. Do we declare the registries per assembly? Any recommended naming standard around folders, class names in a large application? My rough guess is will have around 1000 registries. Also, any ideas on scanning strategies for such a large code base? Should we be concerned?
Thanks for ready this far but the background is important as it's not a 10-minute job.
Hubert