5

Even after reading a bunch I'm still falling into the same trap. I have a class, usually an enity. I need to implement more than one, similar operations on this type. It feels wrong to (seemingly arbitrarily) choose one of these operations to belong inside the entity and push the others out to a separate class; I end up pushing all operations to service classes and am left with an anaemic domain.

As a crude example, imagine the typical Employee class with numeric properties to hold how many paid days the employee is entitled to for both sickness and holiday and a collection of days taken for each.

public class Employee
{
    public int PaidHolidayAllowance { get; set; }

    public int PaidSicknessAllowance { get; set; }

    public IEnumerable<Holiday> Holidays { get; set; }

    public IEnumerable<SickDays> SickDays { get; set; }
}

I want two operations, one to calculate remaining holiday, another for remaining paid sick days. It seems strange to include say, CalculateRemaingHoliday() in the Employee class and bump CalculateRemainingPaidSick() to some PaidSicknessCalculator class. I would end up with a PaidSicknessCalculator and a RemainingHolidayCalculator and the anaemic Employee entity as seen above. The other alternative would be to put both operations in the Employee class and kick Single Responsibility to the curb. That doesn't make for particularly maintainable code.

I suppose the Employee class should have some initialisation/validation logic (not accepting negative alowances etc.) So maybe I just stick to basic initialisation and validation in the entities themselves and be happy with my separate calculator classes. Or maybe I should be asking myself if Anaemic Domain is actually causing me some tangible problems with my code.

2
  • I wanted to clarify. I think people are getting hung up on my particular example and if it does/does not contain more than a single responsibility. Assume that I have a class which requires multiple operations that are definitely separate responsibilities (reasons to change) but are all conceptually similar. How do I decide which single responsibility belongs in the class and which go elsewhere? I'm starting to feel like the answer is.. it doesn't matter. Pick one. Commented Jun 26, 2013 at 10:39
  • Anemic has to do with the class not having behavior. Your example shows no behavior whatsoever; that class is just a data container.. which is fine if that's its responsibility. The problem with anemic models is that you really aren't doing OOP (which is behavior + data), you're doing procedural programming (passing data structures to functions).
    – Andy
    Commented Aug 9, 2018 at 1:26

5 Answers 5

5

Since we're talking about SRP, I think it's a good idea to dissect exactly what a responsibility can mean in the context of an entity. To me their responsibilities come in 2 kinds :

Primary responsibilities of an Entity

  • Identifying itself
  • Composing itself with objects given to it (adding sub-entities/value objects etc.)
  • If you're into DDD : for an Aggregate Root, enforcing aggregate invariants

Secondary responsibilities of an Entity

  • Any other behavior defined in the domain language that naturally seems to belong in the entity

In your example, CalculateRemainingHoliday() and CalculateRemainingPaidSick() clearly fall in the second category.

Even if primary responsibilities taken together can be considered as one responsibility (that of "being an Entity"), I think we can all agree that most rich domain entities still tend to not adhere to SRP strictly speaking, because of secondary responsibilities - these are a growing crowd by nature, and each time you add or remove one of them, the entity effectively changes for a different reason. However, you can design your entities so that they comply with a lesser kind of SRP : changing something inside one secondary responsibility shouldn't change the entity class itself.

In other words, you should design your entities so that secondary responsibilities are delegated to other objects. These can be sub-entities, value objects, or objects that were injected into the entity. For instance, Employee could have a HolidayCalculationStrategy and a SickDaysCalculationStrategy that take care of these concerns. Although they are closely related to an Employee and in fact manipulate Employee data, these objects will be the only ones modified when said strategies need to change, the entity itself remaining untouched.

In summary, rich domain entities can't respect SRP to the letter, but it's necessary evil to ensure cohesion and avoid anemicness. They can still be SRP-ish enough that you can tweak non-quintessential domain behaviors exposed by an entity without touching the code of the entity itself.

Interesting links about SRP vs Rich Domain Model :

https://stackoverflow.com/questions/10454593/can-a-rich-domain-model-violate-the-single-responsibility-principle

http://blog.pluralsight.com/2012/03/02/whats-the-single-responsibility-of-an-entity-in-domain-driven-design/

2
  • "What’s [an entities] single responsibility? Being findable." The second link gives me something I can work and experiment with so I'm marking it as the answer. Thanks. Commented Jun 26, 2013 at 13:40
  • You give examples of some behaviors which should be pushed to other classes, but do you have examples of what you think the primary responsibility of the Employee would be (besides just being a property bag)?
    – Andy
    Commented Aug 9, 2018 at 1:28
3

If you have several very similar operations, one approach is to make them a single parameterized operation, and another is to split them into a dedicated class and aggregate several instances.

parameterized pseudo-code example:

public class Employee
{
    public enum PaidAbsenceType { Vacation, Sickness, MPternity, Training, Travel };

    public void setAllowance(PaidAbsenceType type, int amount);
    public int getAllowance(PaidAbsenceType type);

    public void useAllowance(PaidAbsenceType type, int amount);
    public int getUsed(PaidAbsenceType type);
}

aggregated pseudo-code example:

public class Employee
{
    public class PaidAbsence {
        public int allowance { get; set; }
        public int used { get; set; }
    }

    public PaidAbsence Vacation;
    public PaidAbsence Sickness;
    public PaidAbsence MPternity;
    public PaidAbsence Training;
    public PaidAbsence Travel;
}

In both cases, the responsibility of the containing class is to aggregate these members, and if necessary to co-ordinate them.

Hard-coding two versions of the same logic into your class, unless there are actual behavioural differences, smells a bit odd.

Note that in either of the above cases, a single calculator would suffice (either passing the PaidAbsenceType of interest, or directly passing the appropriate PaidAbsence object) - again assuming the actual logic is the same.

2

The other alternative would be to put both operations in the Employee class and kick Single Responsibility to the curb. That doesn't make for particularly maintainable code.

Why? It seems you have an excessively narrow concept what a "responsibility" means in the single responsibility principle. It certainly does not mean each class is only allowed to have a single method containing logic.

If the Employee class contains all the data those methods need, put both of them into the class. They're really a single responsibility: tracking paid free days. If the Employee class gets more complex, you might want to move that aspect into a class of its own - but one that represents a sub-entity of Employee, not a service class.

As a completely non-technical sidenote: I find the concept of a "PaidSicknessAllowance" to be really insane and broken.

2
  • 3
    Holiday and sickness calculations seem like completely separate responsibilities to me. Both have separate business reasons to change. It's very likely the business would change one and not the other. In this example there's only 2 operations but that could easily be 5 or 6 that operated on the same Employee data but were clearly more than a single responsibility. Which one belongs to the employee? The decision seems arbitrary. Commented Jun 26, 2013 at 10:00
  • 1
    @thecapsaicinkid: if they all operate on the same Employee data and only on that data - lovely! High cohesion, low coupling, that's exactly what you want. Don't over-apply the SRP to the point where it leads to fragmented code. Commented Jun 26, 2013 at 10:16
1

You must have read about Strategy pattern. By implementing this pattern, you are moving responsibility of calculating remaining days of vacation and sickness into separate object. But this object still remains related to the Employee. This could also allow you implement "Vacation" as single class, but have "SickDays" and "Holidays" properties of this class inside and employee. Thus reducing code and behavior duplication.

1

If splitting off a responsibility seems nonsensical to you, chances are you are already following the SRP fairly well, even if you haven't consciously applied it.

To me, it's very difficult to determine if I'm following the SRP by simply asking what the responsibility is, for the reason you've already discovered: you can define a responsibility to be either very large or very small, when the ideal is somewhere in between.

The easiest way to spot an SRP violation is by noticing that you are tending to group methods within a class. Some people even put section comments, like this:

/******** Time off calculations ********/

If you catch yourself doing that, or being tempted to do that, that's a sign you're most likely violating the single responsibility principle.

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.