Is Domain Entity violating Single Responsibility Principle

domain-driven-designsingle-responsibility

Single responsibility ( reason to change ) of an entity should be to uniquely identify itself, in other words, its responsibility is to be findable.

Eric Evan's DDD book, pg. 93:

most basic responsibility of Entities is to establish continuity so
that behavior can be clear and predictable. They do this best if they
are kept spare. Rather than focusing on the attributes or even the
behavior, strip the Entity object's definition down to the most
intrinsic characteristics, particularly those that identify it or are
commonly used to find or match it. Add only behavior that is essential
to the concept and attributes that are required by that behavior.

Beyond that, look to remove behavior and attributes into other objects
associated with the core Entity .Beyond identity issues, Entities tend
to fulfill their responsibilities by coordinating the operations of
objects they own.

1.

… strip the ENTITY object's definition down to the most intrinsic
characteristics, particularly those that identify it or are commonly
used to find or match it. Add only behavior that is essential to the
concept …

Once an entity is assigned a unique ID, its identity is established and so I would assume such an entity doesn't need any behavior to maintain its identity or to help it identify itself. Thus, I don't understand what kind of behavior is author referring to ( besides find and match operations ) with "behavior that is essential to the concept"?

2.

… strip the ENTITY object's definition down to the most intrinsic
characteristics, particularly those that identify it or are commonly
used to find or match it. … Beyond that, look to remove behavior and
attributes into other objects associated with the core ENTITY.

So any behavior that doesn't help identify the entity, but we'd still characterize that behavior as being an intrinsic characteristic of that entity ( i.e. barking is intrinsic to dogs, flying is intrinsic to airplanes, laying eggs is intrinsic to birds … ), should be put into other objects associated with that entity ( example: we should put barking behavior into an object associated with a dog entity )?

3.

Beyond that, look to remove behavior and attributes into other objects
associated with the core ENTITY.

a) MyEntity delegates responsibilities A_resp and B_resp to objects a and b, respectively.

Even though most of A_resp and B_resp work is done by a and b instances, clients are still served A_resp and B_resp through MyEntity, which means that from the client's perspective the two responsibilities belong to MyEntity. Thus, doesn't that mean MyEntity also has A_resp and B_resp responsibilities and as such is violating SRP?

b) Even if we assume that A_resp and B_resp don't belong to MyEntity, MyEntity still has a responsibility AB_resp of coordinating the operations of objects a and b. So doesn't MyEntity violate SRP since at minimum it has two responsibilities – to uniquely identify itself and also AB_resp?

class MyEntity
{
    private A a = ...
    private B b = ...


    public A GetA()
    { ... }

    public B GetB()
    { ... }

    /* coordinates operations of objects a and b */
    public int AworkB()
    { ... }
}

/* A encapsulates a single responsibility resp_A*/
/* A is value object */
class A
{ ... }

/* B encapsulates a single responsibility resp_B*/
/* B is value object */
class B
{ ... }

UPDATE:

1.

Behavior in this context is refering to semantic behavior. For
example, a Property on a class (i.e. attribute on a domain object)
that is used to uniquely identify it has a behavior. While this is not
represented in code directly. The expected behavior is that there will
not be any duplicate values for that property.

So in code we would almost never need to actually implement a behavior ( i.e. an operation ) that would somehow maintain entity's identity, since as you explained such a behavior only exist as a concept in a domain model ( in the form of a ID attribute of an entity), but when we translate this ID attribute into code, part of its semantics is lost ( i.e. the part which implicitly makes sure the ID value is unique is lost )?

2.

Furthmore, a property such as Age has no context outside of a Person
Entity, and as such, makes no sense to move into a different object
… However that information could easily be stored in a separate
location that the unique identifier, hence the confusing reference to
behavior. Age could be a lazy loaded value.

a) If Age property is lazy loaded, then we may call it a behavior, even though semantically Age is just an attribute?

3.

You could easily have operations specific to Address such as
verification that it is a valid address. You may not know that at
design time, but this whole concept is to break down objects into
their smallest parts

While I agree that we'd lose context by moving Age into different object, the context wouldn't be lost if we moved DateOfBirth property into a different object, but we usually don't move it.

What is the main reason that we'd move Address into another object, but not DateOfBirth? Because DateOfBirth is more intrinsic to Person entity or because there are less chances that somewhere in the future we may need to define operations specific to DateOfBirth?

4.
I must say I still don't know whether MyEntity also has A_resp and B_resp responsibilities and why MyEntity also having AB_resp isn't considered a violation of SRP

EULERFX

1)

The behaviors that the author is referring to are behaviors associated
with the entity. These are the behaviors that modify the state of the
entity

a) If I understand you correctly, you're saying that entity should only contain those behaviors that modify its attributes ( i.e. its state )?

b) And what about the behaviors that don't necessarily modify the state of the entity, but are still considered as being an intrinsic characteristic of that entity ( example: barking would be intrinsic characteristic of a Dog entity, even if it didn't modify Dog's state )? Should we include these behaviors in an entity or should they be moved to other objects?

2)

As far as moving behavior to other objects, the author is referring to
value objects specifically.

Though my quote doesn't include it, but author does mention in the same paragraph that in some cases behaviors ( and attributes ) will also get moved into other entities ( though I understand the benefits of moving behaviors to VOs )

3) Assuming MyEntity ( see question 3. in my original post ) doesn't violate SRP, would we say that a responsibility of MyEntity is among other things also comprised of:

a. A_resp + B_resp + AB_resp ( AB_resp coordinates objects a and b )

or

b. AB_resp + delegating A_resp and B_resp to objects ( a and b ) associated with MyEntity?

4)
Eric Evan's DDD book, pg. 94:

CustomerID is the one and only identifier of the Customer ENTITY (
figure 5.5 ), but phone number and address would often be used to
find or match a Customer. The name does not define a person's
identity, but it is often used as part of the means of determining it.

In this example, the phone and address attributes moved into Customer,
but on a real project, that choice would depend on how the domain's
customers are typically matched or distinguished. For example, if a
Customer has many contact phone numbers for different purposes, then
the phone number is not associated with identity and should stay with
the Sales Contact.

a)

CustomerID is the one and only identifier of the Customer ENTITY (
figure 5.5 ), but phone number and address would often be used to
find or match a Customer. The name does not define a person's
identity, but it is often used as part of the means of determining it.

Quote states that only attributes associated with identity should stay in an entity. I assume author means that entity should contain only those attributes that are often used to find or match this entity, while ALL other attributes should be moved?

b) But how/where should other attributes be moved? For example ( assumption here is that address attribute isn't used to find or match Customer and thus we want to move address attribute out of Customer ):

if instead of having Customer.Address ( of type string ) we create a property Customer.Address of type Address, did we move the address attribute into an associated VO object ( which is of type Address ) or would we say that Customer still contains address attribute?

c)

In this example, the phone and address attributes moved into Customer,
but on a real project, that choice would depend on how the domain's
customers are typically matched or distinguished. For example, if a
Customer has many contact phone numbers for different purposes, then
the phone number is not associated with identity and should stay with
the Sales Contact.

Isn't author in the wrong here, since if we assume each of the many contact phone numbers that Customer has only belong to that particular Customer, then I'd say these phone numbers are associated with identity just as much as when Customer only had one phone number?

5)

The reason the author suggests stripping the entity down is that when
one initially creates a Customer entity, there is a tendency to
populate it with any attribute that one can think of being associated
with a customer. This is a data-centric approach that overlooks
behaviors ultimately leading to an anemic domain model.

Off topic, but I thought anemic domain model results from moving behavior out of an entity, while your example is populating an entity with lots of attributes, which would result in Customer having too much behavior ( since we'd probably also include in Customer the behaviors which modify these additional attributes ) and thus in violation of SRP?

thanks

Best Answer

Behavior in this context is refering to semantic behavior. For example, a Property on a class (i.e. attribute on a domain object) that is used to uniquely identify it has a behavior. While this is not represented in code directly. The expected behavior is that there will not be any duplicate values for that property. Something such as an Address which may have its own identity, but does not exist outside of the context of a Person Entity should still be moved out into it's own object. Thus promoting the Entity into an Aggregate Root.

Furthmore, a property such as Age has no context outside of a Person Entity, and as such, makes no sense to move into a different object. The context would be lost, and therefore you can safely determine that it is a value that is essential to the Person Entity. You could not locate the value otherwise. However that information could easily be stored in a separate location that the unique identifier, hence the confusing reference to behavior. Age could be a lazy loaded value.

So to answer your question. No, it does not violate the Single Responsibility Principle. It is mearly stating that a Person should have only person stuff, and not something like Address, which is more complex and related to a person, should exist as its own entity.

You could easily have operations specific to Address such as verification that it is a valid address. You may not know that at design time, but this whole concept is to break down objects into their smallest parts so that something like this is relatively simple when done after the fact.

Update: 1) In most cases this identity validation is done upon saving an object out to a data store. Which means that the code representing the entity validation exists, but it exists elsewhere. It usually exists with the code that is responsible for issuing the identity value. Which is why I state that the uniqueness is not represented directly in the code for the entity.

2) The correct statement would be that Age is an attribute that has behavior. You would need to document the fact that Age is lazy loaded so that a developer consuming that property can make an accurate decision on how to consume that property

3) DateOfBirth usually is a different object; A date object that already has predefined operations on it. In some languages the date object already has a whole domain model defined on it. For example in c# you can specifiy the timezone, if the date is UTC, add and subtract dates to get a timespan. So your assumption about moving DateOfBirth would be correct.

4) If the only thing that MyEntity does is delegation and cooridination then no it does not violate SRP. Its single responsibility is to delegate and coordinate and is refered to as the Facade pattern

Related Topic