4

Recently lots of people adopted DI in their projects (I am working with aspnet core). The problem I have is that DI turns my code towards procedural paradigm. For example in OOP way I would do:

class Something
{
    Something(Other object, Dependency dep); // created manually or by factory obj

    DoStuff();
}

However with DI I cannot create Something class directly, because I need to satisfy dependencies. Now my code generally looks like that:

class Something
{
    Something(Dependency dep); // resolved from di container

    DoStuff(Object obj); // executed manually
}

As a result my objects have no methods, they are just bags of properties that are processed by different services. On the other hand I would have to create giant objects with lots of dependencies just to execute simple task, that needs 1% of created object graph.

How to fix my design? This approach has obvious downsides, for instance I cannot pass Something class around, because it needs Object to DoStuff, so I have to pack it into some other class. There are many other problems, like functionality scattered across many files, difficulties in implementing security (because you can request "inner" service from DI) or encapsulation (Object must have all properties public because they are manipulated by external class.


Edit: Small example for better illustration:

Api I would write before DI:

class ShoppingCartFactory {
    ShoppingCart Create(id);
}

// encapsulates cart logic
class ShoppingCart {
    // internal dependencies passed to cart from factory
    ShoppingCart(DeliveryLoader, ItemMetadataLaoder);


    // methods encapsulation functionality
    Modify(item, quantity);
    Save();
    Empty();
    GetDeliveryOptions(); // loads possible transports from db
    LoadItemMetadata(); // loads prices, availability etc. for items

    // public properties
    ImmutableList<CartItem> items; // immutable list of items
    Delivery selectedDelivery; // transport selected by client
}

When client clicks shopping cart delivery options are displayed only once, and subsequent requests do not need them. Therefore with this approach I have to create ShoppingCartDeliveryCreator dependency for each request.

So instead I have to write something like that:

// instead of having properties on cart Object now I have 3 separate classes
// in previous example user of api had no idea how cart works inside
// now he needs to find all classes associated with cart
// also service class needs to take id in each method
// or needs some method like `Contextualize(id)`
class ShoppingCartService {
    // executes logic on db
    ShoppingCart Read(id);
    ShoppingCart Modify(id, item, quenatity);
    ShoppingCart Empty(id);
    ShoppingCart Save(id, shoppingCart);
}
class ShoppingCartDeliveryCreator (...)
class ShoppingCartItemMetadataLoader (...)

// no logic
class ShoppingCart {
    List<CartItem> items; // cannot be immutable, because Service needs to manipulate it
    Delivery selectedDelivery; // same
}

You cannot do anything with ShoppingCart class, You always need one of the services. Also as Derek Elkins pointed out

One of the consequences of systematically using Dependency Injection is that your class hierarchy becomes completely flat.

I am not sure if this is good thing. I made lots of research and experiments, however I am not convinced to this style of programming. To get any idea of how something works You need to examine all that code, guess what it is doing and then you may have thin idea how it works. With old example you could just look on the class and You would see all the options, or You could check all classes that take ShoppingCart.

  • What kind of application are you developping ? Typical industry application which just manage data with a lot of form look like this because there is no need to encapsulate of business data, you encapsulate persistence and so on, which is logic. On the opposite if you program a game engine, anything internal to the engine should not be exposed. If you need to redesigned for DI, this likely means that your design was wrong from the beginning. – Walfrat May 17 '17 at 7:51
  • 3
    It seems you are intermixing "use of a DI container" with "DI" itself. Can you please edit your question and clarify where you actually mean DI and where you mean "use of a DI container"? These are two different things. – Doc Brown May 17 '17 at 7:52
  • 1
  • 1
    @DocBrown I am not sure if my problems are caused by DI container, or DI itself. I really like automatic dependency handling by DI container, which is really easy to configure in web application. I've seen this blog before, but I think that it introduces too much bloat. Basically all my Somethings would turn into SomethingFactory creating Something. I think it is too much bloat, my coworkers already complain on too much bloat. There is no simpler way? – Shadow May 17 '17 at 8:38
  • 1
    Relevant quote from the blog post of DocBrown : Rich vs anemic domain models is a whole other discussion. For the record, I think that using an anemic model is a perfectly fine approach, just the code needs to be organized in a different way. "it depends" ;). – Walfrat May 17 '17 at 8:45
13

As Doc Brown mentioned, using Dependency Injection containers is not Dependency Injection. Dependency Injection is just parameterization, and Dependency Injection containers are just meant to "simplify" wiring up dependencies. Your issue seems to be mostly driven by (perceived) limitations of whatever DI container framework you're using.

I don't understand how your objects being "bags of properties" is a "result" of dependency injection. Some objects are going to just be "bags of properties" and thus will have no dependencies. These are often called "POJO/POCO" objects for "Plain Old Java/C# Objects". Obviously, this can't be all your objects as nothing would need dependencies then.

You also mention "giant objects with lots of dependencies" that "needs [only] 1% of [the] created object graph". This is somewhat contradictory. If it only needs 1% of the "created object graph", why is it depending on that remaining 99%? If your dependencies need dependencies for things other than what you need of the dependencies, this suggests that your dependencies aren't well-factored. In OOD terms, a violation of the Single Responsibility Principle. One of the consequences of systematically using Dependency Injection is that your class hierarchy becomes completely flat. Any "hierarchy" that is needed arises from how things are wired together (which no longer needs to be strictly hierarchical). Each of your classes should depend on only what it needs via relatively narrow interfaces. If each class provides only one "service", then dependencies will never instantiate more of the object graph than they need. There are plenty of good reasons for class to provide multiple "services", and this can lead to dependencies that are extraneous to some downstream consumer of the "service", but this will be very obvious and presumably done for some explicit reason. If not, factor the class into multiple classes that each provide only one of the "services". (The "services" usually correspond pretty closely to the interfaces a class implements. So if you have a class that implements multiple interfaces, this is an indication that the class is providing multiple "services".)

At any rate, for the limited example of code you provided, there's a pretty clear solution. Firstly, it's quite possible that whatever DI container framework you're using explicitly provides for this, but let's assume not. What do consumers of Something actually need? The need something that will give them Somethings when given Others. (Or to be more precise, something that will give them an instances of an interface representing whatever "services" Somethings provide. They shouldn't care if they actually are Somethings.). That is, they need a SomethingFactory. So classes that need Somethings should depend on SomethingFactory and then use that to create Somethings. In code:

class Something : ISomething {
    public Something(Other object, Dependencies dependencies) {...}
    // ...
}

interface ISomethingFactory {
    ISomething CreateSomething(Other object);
}

class SomethingFactory : ISomethingFactory {
    private readonly Dependencies _dependencies;
    public SomethingFactory(Dependencies dependencies) {
        _dependencies = dependencies;
    }
    public ISomething CreateSomething(Other object) {
        return new Something(object, _dependencies);
    }
}

class SomethingConsumer {
    public SomethingConsumer(ISomethingFactory somethingFactory) {...}
}

This approach gives you almost exactly the same experience as directly writing new Something(object) plus not having to worry about extraneous dependencies. There is no reason from what you've described (or generally) why Dependency Injection would lead to you needing to make all properties public or use a "procedural style" or anything like that. You could take any OO program written "naturally" and mechanically convert it to one using dependency injection. Simply go through what you would "naturally" write in "OO" style, and replace any uses of new with either passed in dependencies or factories as I've illustrated above. Other than having to pass around some extra parameters, the structure of the code will be practically identical to what it was before. If you feel you need to make much more extensive changes than this, you are misunderstanding something. (This may suggest more extensive changes, but they should not be necessary.)

As a note, the fact that you are using any particular DI container should not be evident from the vast majority of your code. You should not be passing around the DI container and instantiating objects with it willy-nilly. The DI container should only be used (if used at all) to simplify the top-level code that wires everything together. I'm not sure what you mean by "you can request 'inner' service from DI", but it sounds like you're doing something like this.

0

The problem I have is that DI turns my code towards procedural paradigm.

Your example does not show a "procedural approach".


As a result my objects have no methods, they are just bags of properties that are processed by different services. On the other hand I would have to create giant objects with lots of dependencies just to execute simple task, that needs 1% of created object graph.

How to fix my design?

maybe the single layer of abstraction pattern helps here:

Usually we apply the SLA-pattern to methods, meaning that they either do actual calculations and/or changing the objects state or call other methods (on dependencies or in same class).

But we can extend that patter for classes too:

Split your classes in "workers" and "dispatchers" so that you end up with classes that do not have dependencies to be injected but do calculations on their parameters and/or members (the "workers", and other classes that have dependencies but do not calculate anything, just call methods on it's dependencies (branching and looping allowed - the "dispatchers")

0

You are confusing DI and DI container frameworks.

Of course you can create your object manually. You just pass in the dependencies it needs. You don' have to always auto populate them from a container.

public class myController
{
    IDependency dep;
    public myController(IDependency dep) // injected by di container
    {
        this.dep = dep;
    }
    public void Whatever()
    {
        var obj = new Object();
        var s = new Something(obj, dep); // injected manually
        s.DoSomething();
    }
}
  • @docbrown heh just read your comment. – Ewan May 17 '17 at 8:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.