Setting Up Zero Friction Projects - Dependency Injection
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.
Comments
Hm. I just allow to set an attribute on a type, specifying that it provides a service interface. Then all such types are collected.
V ery nice Ayende.
Do you have any plans to add the WcfClientFacility to castle like you did with the server side integration support? I was planning on adding something like that but didn't want to duplicate functionality.
cheers,
craig
Probably, it is a trivial facility, at any rate.
I also use convention over configuration whenever is possible, and Binsor is the right tool for the job. Do you have any plans to release Binsor as a project on its own (without all other rhino-commons stuff) so we can enjoy using it without the need to struggle with rhino-commons trunk?
Eduardo,
It is not dependent on Rhino Commons, so yes.
Feel free to submit a patch for this.
Comment preview