Dependency Injection vs Dependency Lookup in Layered Architecture

dependency-injectiondependency-managementdesignobject-oriented-design

Given a layered architecture, where the business logic is isolated in a package from all other layers, like persistent storage, user interface, interfaces for various (vendor) remote services, etc, I am thinking to manage dependencies in the following way:

Within the library, all dependencies are injected manually via the constructor. This has the advantage (which in my opinion is big) that it makes tests more explicit, increasing their value as a documentation source.

For the other layers and plugins, I am thinking to inject manually only one object used for dependency lookup, which would make mocking easier. I doubt there would be only one object for all subsystems, I am merely thinking about really specific, small interfaces, and factory instances which implement one or more of those interfaces. How many interfaces such a factory implements is an implementation detail, I guess I would start with only one factory implementing all interfaces, and as the system evolves, I would pull out other factories and refactor accordingly (YAGNI). The interfaces (i.e. the signature of the constructors) would stay as they were before refactoring, thanks to interface segregation.

The business library gets its commands from outside via commands (think DDD).

  1. What do you think about this? Other advantages and disadvantages are welcome!
  2. Which other strategies for dependency management do you prefer in which situations and why?

Best Answer

You're going for something akin to (but not similar) Spring's ApplicationContextAware, where you pass ApplicationContext (which is a BeanFactory) via a setter, so it later can get beans it needs.

Generally it is a bad idea, if you only need to inject dependencies:

  • It is not explicit about dependencies it uses. So, it is actually harder to mock, not easier, because you're not sure what needs to be mocked unless you look into the whole code of a class and probably the whole code of its' super- and subclasses, if inheritance happens.
  • It is not type safe if you query dependencies by string keys, with all negative consequences of that. However, if you say you'll have specific interfaces to deal with that (like UserRepositoryFactory which has a method like getUserRepository, I guess), it will be type safe, but I don't see the benefit in this solution, because it seems much easier to just inject dependencies without medium getter/wrapper/injector-objects. I think it would be just duplication of DI functionality.

If I would make a plugin system, I would go with "stupid" plugins, which just implement some SomethingPlugin interface, and expect it's dependencies to be injected (via constructor or setters, which doesn't really matter) by the system, without any prior knowledge of anything outside.

Example:

public class UserRatingPlugin implements GenericPlugin,
        UserRepositoryAwarePlugin, PostResositoryAwarePlugin {

    // here are dependencies
    private UserRepository userRepository;
    private PostRepository postRepository

    @Override // from UserRepositoryAwarePlugin
    public void setUserRepository(UserRepository ur) {
        userRepository = ur;
    }

    @Override // from PostResositoryAwarePlugin
    public void setPostRepository(PostRepository pr) {
        postRepository = pr;
    }

    @Override // from GenericPlugin
    // this should be required to make this class a plugin
    public void doPluginMagic() { /* ... */ }
}

Then you register this plugin (via config file, I imagine), then somewhere in a "system level" code you instantiate this class, set dependencies (this part can be automated by Spring's or similar DI-container) and invoke doPluginMagic().

This way you still have small specific interfaces, but now as parts of a plugin API, which I think simplifies the whole thing. You can see what plugin can access by just looking on its' type declaration, and no additional "factories" have to be crafted.