Setting Up Zero Friction Projects - Dependency Injection

time to read 3 min | 515 words

And then there is the issue of managing the application components. We want to keep the single responsibility principal, and we want to reduce dependencies in our application. Let us assume that we are talking about a MonoRail application. In this case, we tend to have the following distinct components:

  • Controllers
  • Domain Services
  • Infrastructure Services

I am not counting the domain classes here because they are not components in the strict sense of the word.

I am big believer in convention over configuration, and in this case Binsor allows me to easy define convention for the project. Here is a sample of a Binsor configuration file from my current project (MonoRail application):

facility MonoRailFacility
facility  "RhinoTransactionFacility", RhinoTransactionFacility 
facility "RegisterResolversFacility", RegisterResolversFacility 

component "LoggerFactory", ILoggerFactory, Log4netFactory:
	configFile: Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log4net.config")

# Data Access
component "active_record_repository", IRepository, ARRepository
component "active_record_unit_of_work", IUnitOfWorkFactory, ActiveRecordUnitOfWorkFactory:
	assemblies: ( Assembly.Load("My.App.Model"), )

# Controllers
for type in AllTypesBased[of Controller]("My.App.Web"):
	component type

# services
for type in AllTypesIn("My.App", "My.App.Web").WithNamespaceContaining("Services"):
	continue if type.GetInterfaces().Length == 0:
	interfaceType = type.GetInterfaces()[0]
	interfaceType = interfaceType.GetGenericTypeDefinition() if interfaceType.IsGenericType
	component type.FullName, interfaceType, type

This means that all controllers are automatically registered, and that all the services are automatically registered using their first interface. The immediate result is that I don't need to think about IoC or DI, I just create a service and a service interface, and I am done.

For a smart client app, the situation is a bit more complex, here is the entire Binsor configuration for the smart client portion of the project:

facility WcfClientFacility:
	services = {
			ISchedulingService : SchedulingService	
		}	

for type in Assembly.Load("My.App.Scheduling").GetTypes():

	continue unless type.Namespace == "My.App.Scheduling.Controllers"
	continue if type.IsAbstract or type.IsInterface:
	continue if IoC.Container.Kernel.HasComponent(type.FullName)

	if type.GetInterfaces().Length == 0:
		component type.FullName, type
	else:
		component type.FullName, type.GetInterfaces()[0], type


for type in Assembly.Load("My.App.Scheduling").GetTypes():

	continue unless type.Namespace == "My.App.Scheduling.Views"
	continue if type.IsAbstract or type.IsInterface:
	continue if IoC.Container.Kernel.HasComponent(type.FullName)

	viewInterface as Type = null
	for i in type.GetInterfaces():
		continue unless i.Namespace == "My.App.Scheduling.Views"
		continue if i.Name == "IView"
		viewInterface = i
	
	if viewInterface is null:
		raise "All views must implement a view interface, but ${type.FullName} doesn't."
	
	component type.FullName, viewInterface, type

As an aside, I am also using this script to enforce the conventions, all views must implement an interface, and it is an error if they are not doing so, as an example.

Using this approach, you get seamless IoC experience. It is just the natural thing to do. For that matter, here is the constructor of a class that I never touched:

public NewServicePlanController(
	ApplicationShell shell,
	ApplicationContext context,
	ISelectServiceOrdersView selectServiceOrdersView,
	ISelectSystemUsersView selectSystemUsersView,
	INewServicePlanView newServicePlanView,
	IAccountServicePlanActionsView accountServicePlanActionsView,
	IAccountServicePlanSummaryView accountServicePlanSummaryView,
	IServicePlanCalendarView<AccountGetService> calendarView,
	ISchedulingService schedulingService)

At this point, I think that we have too much segregation in the views, and we could use a presenter to encapsulate some of them, but that is a good place to be, in my opinion.