Should domain objects have dependencies injected into them?
Asked Answered
K

3

25

I'm specifically referring to this question: DDD - How to implement factories

The selected answer has stated:

"factories should not be tied with dependency injection because domain objects shouldn't have dependencies injected into them."

My question is: what is the reasoning of not being able to inject dependencies in to your entities? Or am I just misunderstanding the statement? Can someone please clarify?

Knowhow answered 17/11, 2013 at 20:32 Comment(4)
Related: #4835546Ruelu
Don't agree with the statement "factories should not be tied with dependency injection because domain objects shouldn't have dependencies injected into them.". The aggregate, entities and value objects should not tied with tied with dependency injection. But Factories and Repositories could.Tiliaceous
Are you asking about injecting deps in Factories or in a Domain Entity?Rudy
Based on the statement, i'm assuming "domain objects" are the any of the building blocks in the domain layer(i.e. entities, factories, repos..). I could be wrong. My question relates specifically to entities, however i'm more concerned with either the statement being TRUE or FALSE. And WHY?Knowhow
C
7

Domain Objects aren't Factories, Repos, etc. They are only Entities, Value Objects, Domain Services and Aggregate Roots. That is, they must be classes which encapsulates the data your business domain uses, the relationships between them, and the behaviour(read modifications) that the domain can do on that data.

Repository is a pattern to abstract away the persistence infrastructure you are using. It's in DDD because it makes your app decoupled from your database, but not all DDD app need or even should use repository.

Factory is a pattern to isolate the construction logic of objects. It's also just a good practice that DDD recommends, but not really needed in all scenarios.

Domain Objects shouldn't depend on anything else, because they are the core of your app. Everything will depend on them. So keeping them free of other dependency makes a clear one way dependency chain, and reduces the dependency graph. They are the invariants, the model, the foundation. Change them, and you probably need to change a lot of stuff. So changing other things shouldn't force them to change.

Commonality answered 17/6, 2014 at 5:5 Comment(9)
Domain objects may be the ones representing entities of a domain but looking at them as dumb value objects with no proper behavior extracting that behavior to separate services only (as I understand your statement) is just not DDD. For me DDD is like decomposing a human being: you have an aggregate root called "Human" containing "Body", "LeftArm", "RightArm", "LeftLeg", "RightLeg" and a "Head" which themselves can have behavior (Left/RightLeg.moveForward(), Left/RightHand.makeAFist()) which then may be implemented by delegating the behavior to some muscles.Bidden
@MatthiasHryniszak Domain objects are a mix of entities, value objects, domain services and aggregate roots. You should read up on what exactly each of these entail. But together, they encapsulate all your business data, as well as all business behavior which needs to modify your business data. They should be modeled to map as close as possible to the real names, verbs and relationships that are common to the business domain they are modeling. Sorry if I wasn't clear enough.Commonality
@MatthiasHryniszak It doesn't really matter where your behavior is captured. You can put behavior as methods on entities, value objects, aggregate roots and domain services. Though only the Aggregate Root and the Domain Services can be used as the interfacing API from outside the domain model. So in your example, I can only get a Human, and I have to do Human.moveLeftArmForward(); But now you realize that in the business domain, no one ever asks you to do that, instead they say have him wave his hand. So you realize that the Aggregate should be Human.wave(), which internally can call arm.moveCommonality
@MatthiasHryniszak If doing OOP, a good way to model could be to have Positions as value objects. Positions have a moveUp, Left, Right, etc. method which returns a new immutable position. Arm is an entity, it has a name, a position and a model. Human is the aggregate and its entity the root. It exposes wave. It has two Arms, and Legs and a Body as well as a name. It has data about human schematic, and so it knows how to modify the position of Arm, so that it looks like the waving animation, and it also guarantees the arm remains connected to body at all time, and that it doesn't band weirdly.Commonality
@MatthiasHryniszak I just noticed you had also down voted my answer, but if you re-read it carefully, I think you'll find that you didn't understand it properly. If there's a particular part of it you want me to clarify, please mention so and I'll try to make it clearer.Commonality
Maybe you're right... I have seen people smash together abstractions like you wouldn't believe where a simple value would be sufficient - only because they knew OO patterns and thought it is what they should be doing. I prefer simple solutions, ones that read easier. Even if they breach the responsibities. I like simple apps :)Bidden
On that note: I am just working with an app that would need around 5-6 lines of a Groovy script with maybe 20 lines of map declaration - yet it uses Spring for IoC, 5 different property files, a dozen of different abstractions... It is a fricken mess! I hate when people do that.Bidden
@MatthiasHryniszak You're right, over-engineering is an issue. DDD is not an OOP pattern though. I mostly do functional programming nowadays and still use DDD modeling when appropriate. DDD is also not an abstract idea, it is a very precise modeling technique which is described in a book written by Eric Evans. The question isn't about general ways to model a domain, but the exact way that DDD defines. The over-engineering you describe has absolutly nothing to do with DDD either, that's just general misunderstanding and overuse of OOP patterns.Commonality
@MatthiasHryniszak Having said that, DDD could also be applied in situations where it shouldn't by engineers who don't fully understand its trade offs. It is an anti-pattern to use DDD on small projects for example. DDD is best used to model large enterprise applications that are modeling real business processes. This is something the book on DDD clearly mentions, so it's unfair to blame DDD for it, the engineers choosing the wrong modeling technique are to blame.Commonality
R
27

Kind of old, but I really want to address this because I've been running into this a lot, and express my opinion on it:

I often hear that Domain Objects should not "depend" on things. And this is true. I often see people extrapolate this to mean we should not inject things into domain object.

That's the opposite of what dependency means. The domain shouldn't depend on other projects, this is true. But, the domain can define its own interfaces which other projects may then implement, which can then be injected back into the domain. As we all know, that's what is called Dependency Inversion (DI).

That is literally the opposite of having a dependency. Not allowing DI into the domain completely hamstrings your ability to accurately model the domain, forces odd SRP violations, and pretty much kills the usability of domain services.

I really feel like I must be the crazy one here, because I feel like everyone reads "the Domain must not have any dependencies" then thinks "being injected with something means you are dependant on it" and comes to the conclusion "therefore we can't inject dependencies into the domain.

That leaves us with the wonderful logic:

Dependency Inversion == Dependency

Robedechambre answered 19/5, 2016 at 21:16 Comment(2)
a) not "Dependency inversion" but rather "Inversion of Control", b) you are describing a plugin architecture, c) I agree with both the fact that the domain can define extension points that others can use to enrich the domain (this is what the plugin architecture is for) and DI should be able to help with it) as well as with the fact that a domain should be able to decouple parts of itself and use DI to put them back together as needed.Bidden
I think you are confusing "Domain Objects" with "Domain Services" in your answer. Here "Domain Objects" refers to the entities (or models), and in Domain Driven Design, they should not have dependencies injected into them.Justinjustina
C
7

Domain Objects aren't Factories, Repos, etc. They are only Entities, Value Objects, Domain Services and Aggregate Roots. That is, they must be classes which encapsulates the data your business domain uses, the relationships between them, and the behaviour(read modifications) that the domain can do on that data.

Repository is a pattern to abstract away the persistence infrastructure you are using. It's in DDD because it makes your app decoupled from your database, but not all DDD app need or even should use repository.

Factory is a pattern to isolate the construction logic of objects. It's also just a good practice that DDD recommends, but not really needed in all scenarios.

Domain Objects shouldn't depend on anything else, because they are the core of your app. Everything will depend on them. So keeping them free of other dependency makes a clear one way dependency chain, and reduces the dependency graph. They are the invariants, the model, the foundation. Change them, and you probably need to change a lot of stuff. So changing other things shouldn't force them to change.

Commonality answered 17/6, 2014 at 5:5 Comment(9)
Domain objects may be the ones representing entities of a domain but looking at them as dumb value objects with no proper behavior extracting that behavior to separate services only (as I understand your statement) is just not DDD. For me DDD is like decomposing a human being: you have an aggregate root called "Human" containing "Body", "LeftArm", "RightArm", "LeftLeg", "RightLeg" and a "Head" which themselves can have behavior (Left/RightLeg.moveForward(), Left/RightHand.makeAFist()) which then may be implemented by delegating the behavior to some muscles.Bidden
@MatthiasHryniszak Domain objects are a mix of entities, value objects, domain services and aggregate roots. You should read up on what exactly each of these entail. But together, they encapsulate all your business data, as well as all business behavior which needs to modify your business data. They should be modeled to map as close as possible to the real names, verbs and relationships that are common to the business domain they are modeling. Sorry if I wasn't clear enough.Commonality
@MatthiasHryniszak It doesn't really matter where your behavior is captured. You can put behavior as methods on entities, value objects, aggregate roots and domain services. Though only the Aggregate Root and the Domain Services can be used as the interfacing API from outside the domain model. So in your example, I can only get a Human, and I have to do Human.moveLeftArmForward(); But now you realize that in the business domain, no one ever asks you to do that, instead they say have him wave his hand. So you realize that the Aggregate should be Human.wave(), which internally can call arm.moveCommonality
@MatthiasHryniszak If doing OOP, a good way to model could be to have Positions as value objects. Positions have a moveUp, Left, Right, etc. method which returns a new immutable position. Arm is an entity, it has a name, a position and a model. Human is the aggregate and its entity the root. It exposes wave. It has two Arms, and Legs and a Body as well as a name. It has data about human schematic, and so it knows how to modify the position of Arm, so that it looks like the waving animation, and it also guarantees the arm remains connected to body at all time, and that it doesn't band weirdly.Commonality
@MatthiasHryniszak I just noticed you had also down voted my answer, but if you re-read it carefully, I think you'll find that you didn't understand it properly. If there's a particular part of it you want me to clarify, please mention so and I'll try to make it clearer.Commonality
Maybe you're right... I have seen people smash together abstractions like you wouldn't believe where a simple value would be sufficient - only because they knew OO patterns and thought it is what they should be doing. I prefer simple solutions, ones that read easier. Even if they breach the responsibities. I like simple apps :)Bidden
On that note: I am just working with an app that would need around 5-6 lines of a Groovy script with maybe 20 lines of map declaration - yet it uses Spring for IoC, 5 different property files, a dozen of different abstractions... It is a fricken mess! I hate when people do that.Bidden
@MatthiasHryniszak You're right, over-engineering is an issue. DDD is not an OOP pattern though. I mostly do functional programming nowadays and still use DDD modeling when appropriate. DDD is also not an abstract idea, it is a very precise modeling technique which is described in a book written by Eric Evans. The question isn't about general ways to model a domain, but the exact way that DDD defines. The over-engineering you describe has absolutly nothing to do with DDD either, that's just general misunderstanding and overuse of OOP patterns.Commonality
@MatthiasHryniszak Having said that, DDD could also be applied in situations where it shouldn't by engineers who don't fully understand its trade offs. It is an anti-pattern to use DDD on small projects for example. DDD is best used to model large enterprise applications that are modeling real business processes. This is something the book on DDD clearly mentions, so it's unfair to blame DDD for it, the engineers choosing the wrong modeling technique are to blame.Commonality
A
3

Domain objects should not have many dependencies.

By Fowler's Tell-Don't-Ask principle (https://martinfowler.com/bliki/TellDontAsk.html), you would want the domain objects to do as much as possible. Including having dependencies. But in Clean Code (Uncle Bob) Chapter 6, it mentions it can be a good design to have data structures operated on by procedure/function classes (services). As long as you don't have hybrid objects which combine simple getters/setters as well as more complex tell-don't-ask operations.

Fowler disagreed with thin models and called it an antipattern - AnemicDomainModel. https://www.martinfowler.com/bliki/AnemicDomainModel.html

I disagree with Fowler. I strongly agree with the following quote from another article about this Fat-Models problem: "Following this logic basically every behaviour would end up in the model classes. This is something that we know (by experience) is a bad idea. Hundreds or thousands of lines of code crammed into a single class is a recipe for disaster. Service Objects grew out of this frustration." - https://tmichel.github.io/2015/09/14/oo-controversies-tell-dont-ask-vs-the-web/

We actually have a project with fat domain models which has this exact problem. As requirements change over time and code gets more complex, a huge, fat model is quite inflexible to perform different operations and handle new requirements. Instead of adding new service workflow paths (classes) acting differently on the same simple data model, you have to make expensive, difficult refactors on the enormous, complicated domain model. It encapsulates the data and prevents anyone from modifying the data in unexpected ways but at the same time, it makes it really difficult for new workflow to manipulate the data in new ways.

Anthropologist answered 29/1, 2020 at 17:20 Comment(1)
My co-worker arguing for anemic-domain-model complained about poor validation causing bad data and duplicated code getting fixed in some services but not others. But I would argue that those things can be fixed without putting everything in the domain object.Anthropologist

© 2022 - 2024 — McMap. All rights reserved.