Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IOC Container type resolution and injection location

Is it best practise to resolve and inject concrete types at the edge of the domain model and then have these fall down through the domain? For example, having the container inject concrete types into MVC controller constructors in a web app, or service endpoints in a service based app?

My understanding of container object graph wire up is a little ropey.

Is it ever appropriate to do the equivalent of Container.Resolve() within the domain?

like image 797
Ben Aston Avatar asked Feb 27 '23 13:02

Ben Aston


2 Answers

DI is really only a means to an end: loose coupling. It is a way to enable loose coupling by injecting interfaces (or base classes) into consumers so that you can vary both independently of each other.

As a general rule, nothing much is gained by injecting a concrete type. You can't swap the type with another type, so the main advantage of DI is lost.

You could argue that this means that you'd just as well just create the concrete instances from within the consumers, but a better alternative is to extract interfaces from those types (and then inject them).

And no: it's never appropriate to pull from the container from within the Domain Model. That is the Service Locator anti-pattern. The Hollywood Principle applies here as well:

Don't call the container; it'll call you

(That said, even with a concrete type there are some secondary benefits from injecting it. If it's non-sealed and has one or more virtual members, you can still override a bit of its behavior, and even if it's sealed, you still get to control its lifetime if you inject it - e.g. you can share the same instance between multiple consumers. However, these benefits are purely secondary and usually not the main reason we decide to inject anything.)


Another question (and the one you seem to be actually asking) is whether it's appropriate to inject services just to be passing them on to other services. No, it's not, since it would violate the Single Responsibility Principle and lead to Constructor Over-Injection.

It's better to wrap fine-grained service in more coarse-grained services. I call these Aggregate Services or Abstract Facades. While these in themselves will have dependencies (like the service endpoints you mention), these will be implementation details. From the point of view of the top-level consumer, they don't exist.

Not only does this nicely solve the issue around too many dependencies in the constructor, it also helps you have better isolation between application layers.

like image 60
Mark Seemann Avatar answered Mar 07 '23 16:03

Mark Seemann


Check out Krzysztof Koźmic's blog post(s) about the subject - I think he has some great opinions about this, and they pretty much sum up what seems to be the current "best practice".

like image 33
mookid8000 Avatar answered Mar 07 '23 15:03

mookid8000