6

I am struggling to find the best place to locate my Ninject configuration "Modules" (the place where Type bindings are specified). I hope I am just missing some obvious trick, as this is starting to turn into a deal-breaker for me with using fluent configuration (and thus Ninject):

In a simple Web stack containing three separate projects: Web, BusinessLogic, DataAccess. I do not want the Web tier to have to directly reference the DataAccess tier, but I can't see a way around this because:

  • If I put the DataAccess configuration Module in the DataAccess layer, I have to reference DataAccess layer so I can access the configuration module when instantiating the Ninject Kernel in the Web tier

  • If I put the DataAccess configuration Module in the Web tier, I have to reference the DataAccess layer to have access to the types I want to bind

  • If I put the DataAccess configuration Module in a separate configuration project, I end up with circular reference issues when trying to specify bindings for both web and DataAccess tiers.

Part of the benefit of IOC is to allow loose coupling, but as far as I can see, use of Ninject would require me to add more direct project references that I currently have. What am I missing?

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
James
  • 7,877
  • 7
  • 42
  • 57
  • Possible duplicate: http://stackoverflow.com/questions/1475575/where-should-i-do-dependency-injection-with-ninject-2 – Mark Seemann Apr 20 '11 at 17:12
  • Related: http://stackoverflow.com/questions/5267525/dal-bll-gui-composition-root-how-to-setup-di-bindings – Mark Seemann Apr 20 '11 at 17:14
  • Thanks Mark - your "Application Model" approach from the second thread seems like an interesting way to go. – James Apr 20 '11 at 23:48
  • @Mark Seemann Seconding the duplicate - the only thing that stopped me doing so before was Remo's answer being too good - will ask him to move it. – Ruben Bartelink Aug 25 '12 at 07:09

4 Answers4

5

Ninject does not require that the assemblies are referenced! You can tell the Kernel to load all modules from the assemblies that match a certain pattern - see the Load() overloads! Using this mechanism you map the can expose your features as Modules as @Daniel Marbach suggested in the place where each feature is implemented. I do not like these huge modules defining every binding for an assembly. I'd rather have each in a specific small module for a certain feature.

This also allows one to enable/disable/replace implementations without recompilation of the other assemblies (at least in case you have the interfaces in separate assemblies).

So basically you have:

  • One or more web tier assemblies: containing the controllers, views and bindings for the web tier. Each of the assembly references some assemblies that define the interfaces it depends on.
  • One or more assemblies that define the interfaces for the dependencies of the web tier.
  • One or more business logic assemblies implementing all or some of the interfaces required by the web tier. Referencing some assemblies that contain the interfaces of the objects they depend on. Containing the modules that define the bindings for the components they provide.
  • One or more assemblies that define the interfaces for the dependencies of the business logic tier.
  • One or more assemblies that implement the the dependencies of the business logic tier and possibly some of the web tier (e.g. data that is directly provided with out the business logic). Containing the modules of the components they provide.
  • One bootstrapper loading the modules of these assemblies using kernel.Load("*.dll") or similar.

The advantage of this is:

  • No reference from the web tier to the business logic tier and data tier
  • No reference from the business logic tier to the data tier
  • Each layer is replaceable without any impact on the others
Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
Remo Gloor
  • 32,665
  • 4
  • 68
  • 98
  • Remo, thanks for your input. With regards to having Modules defined in with the feature, and not in the application root: Have you found that this makes setting up testing configurations harder to maintain? Suddenly, there are several locations that need to be aware of different test/dev/prod settings, and in which changes need to be coordinated when changing from one to another... do you just consider this lesser problem, or I am missing something? Thanks. – James Apr 22 '11 at 13:33
  • What kind of testing have you in mind? Manual testing only requires minimal changes like db connection strings which should be taken from the app config anyway. Automated integration testing can be done with some rebinds in a testing module. In any case testing should not require to change the production code. – Remo Gloor Apr 22 '11 at 23:42
  • Think this question needs to bv VTC'd in favor of http://stackoverflow.com/questions/1475575/where-should-i-do-dependency-injection-with-ninject-2 as the question comes up to often. Only reason I didnt VTC before is this answer is really good. Can you please move it over to the other question, VTC this one and I'll personally go upvote it there! – Ruben Bartelink Aug 25 '12 at 07:11
3

I typically create an assembly just for the IOC Container and configuration; That Assembly can then reference all of the other Assemblies and Ninject (or StructureMap in my case). Then the web application just has to reference the IOC assembly and include a couple lines of initialization code that directly use the IOC assembly.

One note however - My IOC assembly does not reference the web assembly (that would introduce a circular reference). Anything that needs to be injected is defined outside of the web assembly, so this is not a concern for me.

Chris Shaffer
  • 32,199
  • 5
  • 49
  • 61
  • Well, I went with this approach. All DI config in a config assembly, EXCEPT for DI config for Web Specific objects, which live in the Web project. Not great, but I have added copious comments describing what is going on, and why. This seems like an acceptable trade off for having Fluent Config. Mark's suggestion (in comment on original question) is probably the nicest option, but would take more refactoring that I want to do at this time. Thanks all for your responses. – James Apr 20 '11 at 23:51
  • Think this question needs to bv VTC'd in favor of http://stackoverflow.com/questions/1475575/where-should-i-do-dependency-injection-with-ninject-2 as the question comes up to often. Can you please move this answer over to the other question if you feel it fits, VTC this one and I'll personally go upvote your answer there! Thanks. – Ruben Bartelink Aug 25 '12 at 07:12
2

The best way to organize your modules is by feature! For example

  • AuthenticationModule
  • OrderModule
  • CustomerModule

Have fun!

Daniel Marbach
  • 2,294
  • 12
  • 15
  • This is an entirely different level of organization than the OP is talking about. He is mostly dealing with the concept of a layered architecture. – Domenic Apr 20 '11 at 21:37
  • I know. But I wanted to point into another direction. I'm not a fan of classical layers. I'm a huge fan of feature orientation. So in my point of view my statement is also a valid answer. – Daniel Marbach Apr 20 '11 at 21:42
  • 1
    Well, from my point of view you injected your ideas of how architecture should work into a question where someone is asking for help with layered architecture, so -1. – Domenic Apr 20 '11 at 21:44
  • +1 Modules should always be separated by feature. It probably does not answer the question but this approch together with loading the modules dynamically from all tiers is the way to go. – Remo Gloor Apr 21 '11 at 16:19
  • Think this question needs to bv VTC'd in favor of http://stackoverflow.com/questions/1475575/where-should-i-do-dependency-injection-with-ninject-2 as the question comes up to often. I found this answer personally helpful and insightful so I'd love for you to move it over onto the other question, VTC this one and I'll personally go upvote your answer there! Thanks. – Ruben Bartelink Aug 25 '12 at 07:13
0

I always put Ninject Modules Configuration into separate assembly like Acme.Common and reference to this from Acme.Data, Acme.Domain etc. so there is no circular dependencies, I can always replace Acme.Common after some modifications in registrations without troubles.

Dariusz
  • 15,573
  • 9
  • 52
  • 68