16

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

2
  • 2
    i would highly highly recommend robert martins clean code video series, cleancoders.com He goes into great detail on how the different principles can either cause problems OR balance each other. otherwise i think part of the formula for your example would be looking at the span of time the Person object is concerned with. if its for a brief time span like a Purchase, then billing address used for purchase would be part of it and unchangeable. if its for a Library account then address should be able to change.
    – cartalot
    May 13, 2013 at 21:32
  • 2
    I think this question might be violating the SRP... ;) May 25, 2017 at 21:56

5 Answers 5

7

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

2
  • Could you look at the update I made?
    – EdvRusj
    May 13, 2013 at 20:27
  • Updated my answer May 16, 2013 at 14:06
4

Very good question. The SRP shouldn't be taken quite so literrally. Identification/lookup isn't the responsibility of the entity in the regards to the SRP. Someone else is responsible for giving it an ID (namely the store) and for looking it up (namely the Repository).

The primary purpose of an entity is to represent the concepts uncovered by the model. The only difference between an Entity and a Value Object is that the Entity has meaning beyond its non-identifying attributes. For example if a Person changes his Name, he's still the same person, just with a different name.

0
3

TL;DR: you are over thinking this. However, I had fun overthinking it along with you. So buckle up....

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

No, that's not right. The single responsibility of an entity is continuity.

Identity is an emergent consequence of continuity. Modeling identity as a separable idea is a performance optimization.

Here's an example: A restaurant patron gives is car to the valet. An hour later, a restaurant patron asks for the car. Should the valet give it?

It's easy to say that the valet should give the car if the patron is the "same". But what does that really mean? The right way to determine that is to start with the "now" patron, and search backwards through that patron's history to see if the giving the car to the valet is part of that history.

We can't actually do that, of course. We have trouble tracking our own history precisely, never mind the history of something that wasn't with us the entire time. So instead of using the patron's history, we take short cuts. Does the patron possess the ticket stub that has the same number as the tag currently tied to the keys? Does the driver's license in the patron's wallet match the name on the title at the DMV, does the picture on the driver's license resemble the face of the patron. Etc.

In short: instead of checking the history of the patron, we check the current state of the patron, to see if the current state is consistent with a history that spans the period between the arrival of the car and the request for its return.

When modeling entities, we use an analogous optimization. We give all entities the common responsibilities of

  1. Ensuring that the beginning of the history includes an assignment of an immutable identifier to the state of the object
  2. Ensuring that the next state always includes a faithful copy of the identifier of the previous state.

I'm not describing a second responsibility of the entity here; the entity is still responsible for continuity -- making sure that the history is a consistent narrative. The identifier responsibilities are just a subset that are common to all entities.

We don't have any enforcement of uniqueness yet. That's not possible within a single entity, because uniqueness requires access to the state of all entities; where a single entity only has access to its own.

Once again, checking all the identifiers every time isn't practical, so instead we satisfy uniqueness the easy way: The code that generates the next identifier must never repeat.

In the end, this means that we can verify continuity by testing the equivalence of two different pieces of state in memory, saving a lot of bother in trying to query acyclic graphs.

You also seem to have confused the Single Responsibility Principle (which is a really good idea) with an atomic responsibility principle. Decomposing a responsibility into smaller, more easily managed parts is compatible with SRP.

2

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"?

If the identity is established then yes the entity needs nothing else to be identified. 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. For example, a Customer entity may have a MakePreferred behavior. 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.

As far as moving behavior to other objects, the author is referring to value objects specifically. The reason it is a good idea to move behavior to VOs is because VOs are usually "smaller" than entities, thereby more focused. Moreover, aspects such as immutability and closure of operations simplify reasoning about the code while also making it more SOLID.

Together with VOs, an entity serves as a sort of anchor which coordinates the various VOs which implement its behavior.

With regards to SRP, your confusion is not unwarranted. One problem with the stereotypical OOP implementation of entities is the conflation of identity and state. Indeed, from a behavioral perspective, identity has nothing to do with behaviors. In other words, the identity of an entity isn't requires for any of its behaviors. There are implementations where this conflation is eliminated, such as AggregateSource or a functional approach I describe here.

The other problem is that, to an extent, SRP can be a qualitative measure. Anyone can come up with a definition of single responsibility which some class violates. One can say that the responsibility of the entity is to implement the behaviors required of that entity. In that sense, it has a single responsibility. Furthermore, when an entity delegates behaviors to constituent VOs, it isn't violating SRP. SRP doesn't forbid composition of the sort. It does caution one to reduce coupling between objects to an absolute minimum, keep interfaces as bare as possible, etc.

UPDATE

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

Yes, though there are exceptions...

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?

It is acceptable for entities to contain factory methods for creating instances of entities would are effectively child entities, but where object references aren't used for traversal. In this case, the child entity needs to be persisted by the application service. The application service uses the parent entity to construct the child entity.

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:

You're looking at responsibility from the implementation perspective. Instead, consider the entity as a sort of black box with responsibilities. How it handles those isn't of interest to you as someone looking from the outside. Partitioning of responsibilities among VOs or even other entities is an implementation concern.

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?

More specifically, attributes which aren't required for behavior nor look up shouldn't be part of the entity. Why bother? Moreover, with something like the read-model pattern, entities don't need anything beyond attributes that are required for behavior.

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?

Yes, in effect, there is not difference between a string address or an Address VO address.

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?

I'm not 100% on author's intent, but I think he's just describing how entity lookup requirements can alter how the entity and its corresponding VOs are structures.

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?

Lots of attributes does not imply lots of behavior. In fact, it usually suggest the opposite. Lots of attributes with getters and setters, but no encapsulating behavior.

2
  • I did an update
    – EdvRusj
    May 16, 2013 at 16:54
  • I like the advice that behaviors could be moved to value objects. SRP remains intact because different stake holders who require different behaviors can use different value objects.
    – flipdoubt
    Apr 18, 2020 at 13:20
-3

Well, it depends on how you want to look at it.

Another way is: "Is Single Responsibility Principle violating Domain Entity?"

Both are guidelines. There is no "principle" anywhere in software design. There are, however, good designs and bad designs. Both these concepts can be used in different ways, to achieve a good design.

1
  • Unexplained downvotes == SRP fanboys
    – h bob
    Jul 27, 2016 at 14:40

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.