Infrastructure Ignorance
Recently there have been a long discussion in the ALT.Net mailing list about Auto Mocking Containers. We covered a lot of ground there and it is interesting to boot.
What I wanted to talk about comes from this discussion. A while ago there was a big stick about Persistence Ignorance. I won't reiterate the arguments, but I want to expand the idea to other concepts as well. It is not just persistence that your code should be ignorant of, it is all the infrastructure concerns in general.
I don't care how good your infrastructure code is, it is still nasty pitfall of technicalities. I know that all of mine have been this way.
The IoC container that you use shouldn't appear anywhere in your code, nor should the persistence concerns mix themselves with the actual code. Invasive approaches, no matter what they do, should be frowned on.
A lot of people thinks that the model below is a good one to follow:
Personally, I think that it putting too much emphasis on the infrastructure. The infrastructure should not sit at the heart of the application. It should be shoved to some misbegotten corner and only see the light of day if there is no other choice.
The diagram below demonstrate how I think it should be.
There should be a lot more weight for the actual application code than the infrastructure code, and only at well defined locations will you have domain services call down to the infrastructure. In fact, I would like to isolate that as much as possible. Dependency Injection, AOP and minor amount of wrapping should allow you to get an application that is infrastructure ignorance.
The immediate result of which is that you get a lot more focus on actual business value than on implemented two phase commit using FAT12 file system.
That is not to say that infrastructure is not important, it is critically important. But the infrastructure should not be invasive.
Comments
1.) Love the graphics and ditto
2.) Add a bit more venom on UI ignorance. IPropertyNotifyChanged/IEditableObject/INeedToAddUglyCrapToMyCodeToAppeaseTheDataBindingGods is pure code noise that has no place in anything resembling a Domain Model.
What do you think of the Volta project? Its has lofty aims of stripping out just about anything that isn't application code and might be just the thing for improving infrastructure ignorance once its cooked.
Harry,
I don't like the concept, it looks to me like they are trying to make DCOM all over again.
Infrastructure Ignorance is all very nice, but out of process calls should never be hidden.
Harry,
I don't like the concept, it looks to me like they are trying to make DCOM all over again.
Infrastructure Ignorance is all very nice, but out of process calls should never be hidden.
"The IoC container that you use shouldn't appear anywhere in your code, nor should the persistence concerns mix themselves with the actual code. Invasive approaches, no matter what they do, should be frowned on."
haha good luck with that!
See, if I want to use an IoC container, and I'm forced to use a factory to produce my objects so I get stuff injected, I AM forced to work a given way BECAUSE of the infrastructure I use. So it's not called directly in your code, however it IS called indirectly in such a way that it is visible in your code:
SomeType s = new SomeType(); // no stuff gets injected
SomeType s = Factory.Produce<SomeType>(); // stuff gets injected
In a world you describe in the quoted remark, I should be able to use the first statement AND get stuff injected WITHOUT worries. I also shouldn't worry about the fine-print details of the infrastructure used (i.e.: infrastructure code requires virtual properties/methods, infrastructure code injects code which manages graphs etc. etc.).
Is that the world we're living in today? No, not at all. No matter what you do, you can't avoid infrastructure influence in your own code, not by a long shot. Remember: even if your code is influenced by just 1%, it has a connection with the infrastructure used so you can't do everything you wanted without consulting the infrastructure code's specs.
It would be great IMHO if people in the blog-o-sphere start describing the reality instead of describing a dreamworld which isn't there. (oh of course, the aspects I describe are only popping up when someone is doing something wrong or not using the 'right' tools... I get it)
Frans,
I am sorry, but you do realize that I am describing the reality on real projects. They just happen to be in production for over a year.
And there is a single place where they hit the container, and that is very deep in the infrastructure code.
Put simply, this is very bad code:
SomeType s = Factory.Produce<SomeType>();
Have it injected by the container, and you don't have a dependecies on the container.
I don't care if it is in production, there is a lot of bad code in production, that doesn't make it correct code.
"And there is a single place where they hit the container, and that is very deep in the infrastructure code.
Put simply, this is very bad code:
SomeType s = Factory.Produce<SomeType>();."
You missed my point. What I'm saying is that if I use 'new' in my code instead of calling some factory/repository or whatever you want to call it, I skip the infrastructure code and therefore don't benefit from its features.
Which means I HAVE TO use a given way to create objects instead of the 'new' operator. This thus implies that by using the IoC principle and not having ctor-driven DI, you have to use other ways to create objects. I'm NOT saying that that is BAD, on the contrary. I'm just highlighting that by using OTHER ways to create objects than the 'new' operator, you have to follow the rules forced upon you by the infrastructure, the framework you're using.
So saying that code should be written without ANY ties to the infrastructure used is in theory a great goal, however in practise undoable, as you will have code written and designed which strictly follows the rules dictated by the infrastructure.
When I see someone creating an object using a call to some other object (you can declare my stupid example as bad code, but you do have to create your IoC using code somewhere, so you have to call out to whatever you use as infrastructure, be it wrapped in repositories or something else, you can't use 'new') instead of using 'new' I fail to see how that ISN'T using code which is dictated by the infrastructure used.
Otherwise why not use 'new' as with any other class?
"Have it injected by the container, and you don't have a dependecies on the container"
How can I tell the container to do the DI for me if I can't call the container because that would be a dependency? Via a wrapper? Ok, though then I have an indirect dependency, and a direct dependency on the wrapper. You might think that the wrapper disconnects the two but you're mistaken: I can't continue with using 'new' instead of calling the wrapper/intermediate class/repository etc.
Again: I'm NOT saying that using repositories/factories etc. is bad, on the contrary. I'm just saying that the necessity to use them to get infrastructure working, so you've to give up the freedom to use 'new', IS forced upon you by the infrastructure. Perfectly fine by me, though it clashes with the picture painted by you in your post that you should write code which is unaware of infrastructure. I find that impossible to do if I have to give up the freedom to use 'new' BECAUSE of that infrastructure usage! Like giving up that freedom 'doesn't count' in the concept of 'code unaware of infrastructure'.
Let us take a MonoRail app as a good example.
The infrastructure makes a call to the container to get the relevant controller.
The controller is injected all its dependencies, and their dependencies, and their dependencies, etc.
At no point do I need to call the container for anything.
In a smart client app, we need more control, because we need to handle the lifetime of objects explicitly.
In this case, I'll have the ApplicationController have a method like ShowView<TView>(), which will call the container.
That is what I am talking about when I am saying that you have a very small bridge between the domain services and the infrastructure.
In this case, the app controller is that bridge.
And the interaction is highly limited, on purpose.
Have you taken a look at the ports-and-adapter architecture (a.k.a Hexagonal architecture)?
http://alistair.cockburn.us/index.php/Ports_and_adapters_architecture
It seems it provides the exact thing you are searching for. I'm afraid I don't have a lot of experience with this yet, but it looks interesting.
I think the difference between what Ayende and Frans are talking about is that even if you don't have any direct references to your IOC container throughout your code you still need to be aware that there is an container.
The fact we have a IOC container means we code our services in a certain way (pushing dependencies to the ctor and not using new()).
In summary, while we should strive for code that is ignorant of the container we still need to be aware that is is there and code appropriately.
To counter Frans' point Greg Young and co. were doing some additional steps post-build that would allow you to write this code:
SomeType s = new SomeType(); // no stuff gets injected
but get this result:
SomeType s = Factory.Produce<SomeType>(); // stuff gets injected
They went in and replaced the IL calls to new with whatever factory/container they wanted (all defined in configuration) so newbs could just keep writing "new this" and "new that" without worrying about all the extra stuff going on in the background.
I don't agree you need to be aware of any container. In fact, I would rather have all my developers know nothing of containers and such (other than "we use them" and the principle behind them) and focus on writing good, strong domain code.
Would be a good discussion to get Frans, Oren and others together to talk about this. Maybe soon... (DevTeach/ALT.NET/etc.)
Just out of curiosity, what do you use to generate those images? It looks very slick and I do presentations from time-to-time that I could use something like that for.
PowerPoint 2007
Comment preview