13

I'm just learning about dependency injection, and am stuck on something. Dependency Injection recommends sending dependent classes through the constructor, but I'm wondering if this is necessary for data objects. Since the Unit-Testability is one of the main benefits of DI, would a data object, that only stores data, and not any procedures ever be unit tested, making DI an unnecessary layer of complexity, or does it still help in showing dependencies even with data objects?

Class DO{
    DO(){
        DataObject2List = new List<DO2>();
    }

    public string Field1;
    public string Field2;
    public List<DO2> DataObject2List;
}

Class DO2{
    public DateTime Date;
    public double Value;
}
6
  • 1
    What exactly do you mean by "data object"? That's not a standard term. Are you talking about a DTO or are you referring to just any class with no methods (such as a particularly boring part of a domain model)? There's a huge difference between the two.
    – Aaronaught
    Commented Jun 10, 2011 at 18:01
  • 1
    Sure, I mean just a class with no methods, a class that just stores data. If Data Object isn't the correct term for this, is there one, or is it just called a class with no methods?
    – sooprise
    Commented Jun 10, 2011 at 19:00
  • @sooprise This is a good question. Good thinking. Commented Jun 10, 2011 at 19:01
  • @sooprise Perhaps my answer is off base. Where will you put the CRUD methods? In a separate data access class that would take the Data Objects and persist them into a database table? I.e. DataAccess.Create(<DataObject>)? Commented Jun 10, 2011 at 19:18
  • 1
    @Matthew, this would be a Data Access Object - if that is in fact what the OP is talking about, then that's not clear at all. Modern implementations tend to move away from this pattern anyway, relying on repositories and/or units of work.
    – Aaronaught
    Commented Jun 10, 2011 at 20:10

4 Answers 4

8

I'd like to suggest a clarification of some of the terminology you are using here, specifically "dependency" and "dependency injection".

Dependency:

A "dependency" is typically a complex object that performs some functionality that another class may need to depend on. Some classic examples would be a logger or a database accessor or some component that processes a particular piece of business logic.

A data only object like a DTO or a value object are not typically referred to as a "dependency", since they don't perform some needed function.

Once you look at it this way, what you are doing in your example (composing the DO object with a list of D02 objects through the constructor) should not be considered "dependecy injection" at all. It's just setting a property. It's up to you whether you provide it in the constructor or some other way, but simply passing it in through the constructor does not make it dependency injection.

Dependency Injection:

If your DO2 class were actually providing some additional functionality that the DO class needs, then it would truly be a dependency. In that case, the dependent class, DO, should depend on an interface (like ILogger or IDataAccessor), and in turn rely on the calling code to provide that interface (in other words, to 'inject' it into the DO instance).

Injecting the dependency in such a way makes the DO object more flexible, since each different context can provide its own implementation of the interface to the DO object. (Think unit testing.)

8

I'm going to do my best to cut through the confusion in the question.

First of all, "Data Object" is not a meaningful term. If the only defining characteristic of this object is that it doesn't have methods, then it shouldn't exist at all. A useful behaviour-less object should fit into at least one of the following subcategories:

  • Value Objects or "records" have no identity at all. They should be value types, with copy-on-reference semantics, assuming the environment supports it. Since these are fixed structures, a VO should only ever be a primitive type or a fixed sequence of primitives. Therefore, a VO shouldn't have any dependencies or associations; any non-default constructor would exist solely for the purpose of initializing the value, i.e. because it can't be expressed as a literal.

  • Data Transfer Objects are often mistakenly confused with value objects. DTOs do have identities, or at least they can. The sole purpose of a DTO is to facilitate the flow of information from one domain to another. They never have "dependencies". They may have associations (i.e. to an array or collection) but most people prefer to make them flat. Basically, they're analogous to rows in the output of a database query; they're transient objects that usually need to be persisted or serialized, and therefore can't reference any abstract types, as this would make them unusable.

  • Finally, Data Access Objects provide a wrapper or façade to a database of some kind. These obviously do have dependencies - they depend on the database connection and/or persistence components. However, their dependencies are almost always externally managed and totally invisible to callers. In the Active Record pattern it's the framework that manages everything through configuration; in older (ancient by today's standards) DAO models you could only ever construct these via the container. If I saw one of these with constructor injection, I would be very, very worried.

You may also be thinking of a entity object or "business object", and in this case you do want to support dependency injection, but not in the way that you think or for the reasons you think. It's not for the benefit of user code, it's for the benefit of an entity manager or ORM, which will silently inject a proxy which it intercepts to do fancy things like query comprehension or lazy loading.

In these, you usually don't provide a constructor for injection; instead, you just need to make the property virtual and use an abstract type (e.g. IList<T> instead of List<T>). The rest happens behind the scenes, and nobody's the wiser.

So all in all I would say that a visible DI pattern being applied to a "data object" is unnecessary and probably even a red flag; but in large part, that is because the very existence of the object is a red flag, except in the case when it is specifically being used to represent data from a database. In almost every other case it's a code smell, typically the beginnings of an Anemic Domain Model or at the very least a Poltergeist.

To reiterate:

  1. Don't create "data objects".
  2. If you must create a "data object", then make sure it has a clearly defined purpose. That purpose will tell you whether or not DI is appropriate. It's impossible to make any meaningful design decisions about an object that shouldn't exist in the first place.

Fin.

0

In your example, DO doesn't have any functional dependencies (basically because it doesn't do anything). It does have a dependency on the concrete type DO2, so you might want to introduce an interface to abstract DO2, so that the consumer could implement their own concrete implementation of the child class.

Really, what dependency would you inject here?

3
  • Per another question of his that I answered, I think the question refers to a Data Object with incorporated CRUD operations. How/where is the database dependency injected into the Data Object? In the methods? In the constructor? Some other way? The assumption is that the dependency shouldn't be hidden away in the body of the methods -- that disables separating out the database dependency from the Data Object so that the Data Object can be unit tested. Commented Jun 10, 2011 at 18:55
  • @Matthew Rodatus - I see. Yes, in that case, you have two choices: inject the persistence service or create another class called DOPersister that knows how to persist DO and leave it as a strictly data-only object (better in my opinion). In the latter case, DOPersister would be injected with the database dependency. Commented Jun 10, 2011 at 19:29
  • After re-reading his question, I'm less sure. My analysis may be wrong. He did say in his question that his DO would not have any procedures. That would mean the persistence does NOT happen in the DO. In that case, your answer is right -- there are no dependencies to inject. Commented Jun 10, 2011 at 19:36
0

Since this is a Data Object in the data access layer, it should depend directly on a database service. You could specify a DatabaseService to the constructor:

DataObject dataObject = new DataObject(new DatabaseService());
dataObject.Update();

But, the injection doesn't have to be in the constructor. Alternatively, you could provide the dependency via each CRUD method. I prefer this method to the previous because your Data Object doesn't need to know where it will persist until you actually need to persist it.

DataObject dataObject = new DataObject();
dataObject.Update(new DatabaseService());

You definitely do not want to hide the construction away in the CRUD methods!

public void Update()
{
    // DON'T DO THIS!
    using (DatabaseService dbService = new DatabaseService())
    {
        ...
    }
}

An alternative option would be to construct the DatabaseService via an overridable class method.

public void Update()
{
    // GetDatabaseService() is protected virtual, so in unit testing
    // you can subclass the Data Object and return your own
    // MockDatabaseService.
    using (DatabaseService dbService = GetDatabaseService())
    {
        ...
    }
}

A final alternative is to use a singleton-style ServiceLocator. Although I dislike this option, it is unit testable.

public void Update()
{
    // The ServiceLocator would not be a real singleton. It would have a setter
    // property so that unit tests can swap it out with a mock implementation
    // for unit tests.
    using (DatabaseService dbService = ServiceLocator.GetDatabaseService())
    {
        ...
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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