# The Anaemic Domain Model is no Anti-Pattern, it’s a SOLID design

Design Patterns, Anti-Patterns and the Anaemic Domain Model

In the context of Object-Oriented software engineering, a “design pattern” describes a frequently recurring and effective solution to a commonly encountered problem. The utility of formalising and sharing design patterns is to provide a set of “battle-tested” designs as idiomatic solutions for classes of problems, and to increase the shared vocabulary among software engineers working on collaboratively developed software. The term was coined in the seminal book by Gamma et al [5], which named and described a set of common design patterns. The lexicon of design patterns grew from the initial set specified in the book, as the notion gained in popularity [6], [17].

Following the increasing popularity of design patterns as a concept, the idea of “design anti-patterns” entered popular discourse [7][8]. As implied by the name, an anti-pattern is the opposite of a pattern; while it too describes a recurring solution to a commonly encountered problem, the solution is typically dysfunctional or ineffective, and has negative impacts on the “health” of the software (in terms of maintainability, extensibility, robustness, etc.). Anti-patterns serve a similar purpose to patterns; the description of the anti-pattern might illustrate a typical implementation of the anti-pattern, explain the context it generally occurs in, and show how the implementation results in problems for the software.

A potential problem with the concept of a design anti-pattern is that it might discourage critical thought about the applicability of the pattern. A design that may be inappropriate in some contexts may be a sensible decision in others; a solution might be discarded after being recognised as an anti-pattern, even though it would be a good fit for the problem at hand.

Why is the Anaemic Domain model considered by some to be an Anti-Pattern?

Fowler [1] and Evans [2] describe an ADM as consisting of a set of  behaviour-free classes containing business data required to model the domain. These classes typically contain little or no validation of the data as conforming to business rules; instead, business logic is implemented by a domain service layer. The domain service layer consists of a set of types and functions which process the domain models as dictated by business rules. The argument against this approach is that the data and methods are divorced, violating a fundamental principle of Object-Oriented design by removing the capability of the domain model to enforce its own invariants. In contrast, while an RDM consists of the same set of types containing necessary business data, the domain logic is also entirely resident on these domain entities, expressed as methods. The RDM then aligns well with the related concepts of encapsulation and information hiding; as Michael L. Scott states in [9], “Encapsulation mechanisms enable the programmer to group data and the subroutines that operate on them together in one place, and to hide irrelevant details from the users of an abstraction”.

In an RDM, the domain service layer is either extremely thin or non-existent [20], and all domain rules are implemented via domain models. The contention is that domain entities in a RDM are then entirely capable of enforcing their invariants, and therefore the system is sound from an Object-Oriented design perspective.

However, the capability of a domain entity to enforce local data constraints is only a single property in a set of desirable qualities in a system; while the ADM sacrifices this ability at the granularity of the individual domain entities, it does so in exchange for greater potential flexibility and maintainability of the overall implementation by allowing the domain logic to be implemented in dedicated classes (and exposed via interfaces). These benefits are particularly significant in statically typed languages such as Java and C# (where class behaviour cannot simply be modified at run-time) for improving the testability of the system by introducing “seams” [10], [11] to remove inappropriate coupling.

A Simple Example

Consider the back end of an e-commerce website in which a customer may purchase items, and offer items for sale to other customers across the globe. Purchasing an item reduces the purchaser’s funds. Consider the implementation of how a customer places a purchase order for an item. The domain rules state that the customer can only place an order if they have enough funds, and the item must be available in region for that customer. In an RDM, a Customer class would represent the domain entity for the customer; it would encapsulate all the attributes for the customer, and present a method such as PurchaseItem(Item item). Like Customer, Item and Order are domain models representing purchasable items and customer orders for items respectively. The implementation of the Customer (in pseudo-C#) might be something like;

/*** BEGIN RDM CODE ***/

class Customer : DomainEntity // Base class providing CRUD operations
{
// Private data declared here

public bool IsItemPurchasable(Item item)
{
bool shippable = item.ShipsToRegion(this.Region);
return this.Funds >= item.Cost && shippable;
}

public void PurchaseItem(Item item)
{
if(IsItemPurchasable(item))
{
Order order = new Order(this, item);
order.Update();
this.Funds -= item.Cost;
this.Update();
}
}
}

/*** END RDM CODE ***/

The domain entities here implement the Active Record pattern [17], exposing Create/Read/Update/Delete methods (from a framework/base class) to modify records in the persistence layer (e.g., a database). It can be assumed that the PurchaseItem method is invoked in the context of some externally managed persistence layer transaction (perhaps initiated by the HTTP request handler/controller, which has extracted a Customer and an Item from the request data). The role of the Customer domain entity in this RDM is then to model the business data, implement the business logic operating on that data, construct Order objects for purchases, and interface with the persistence layer via the Active Record methods; the model is Croesus-like in its richness, even in this trivial use case.

The following example demonstrates how the same functionality might be expressed using an ADM, in the same hypothetical context;

/*** BEGIN ADM CODE ***/

class Customer { /* Some public properties */ }
class Item { /* Some public properties */ }

class IsItemPurchasableService : IIsItemPurchasableService
{
IItemShippingRegionService shipsToRegionService;

public bool IsItemPurchasable(Customer customer, Item item)
{
bool shippable = shipsToRegionService.ShipsToRegion(item);
return customer.Funds >= item.Cost && shippable;
}
}

class PurchaseService : IPurchaseService
{
ICustomerRepository customers;
IOrderFactory orderFactory;
IOrderRepository orders;
IIsItemPurchasableService isItemPurchasableService;

// constructor initialises references

public void PurchaseItem(Customer customer, Item item)
{
if(isItemPurchasableService.IsItemPurchasable(customer, item))
{
Order order = orderFactory.CreateOrder(customer, item);
orders.Insert(order);
customer.Balance -= item.Cost;
customers.Update(customer);
}
}
}

/*** END ADM CODE ***/

Contrasting the example with respect to the SOLID Principles

At first glance, the ADM is arguably worse than the RDM; there are more classes involved, and the logic is spread out over two domain services (IPurchaseService and IItemPurchasableService) and a set of application services (IOrderFactory, ICustomerRepository and IOrderRepository) rather than resident in the domain model. The domain model classes no longer have behaviour, but instead just model the business data and allow unconstrained mutation (and therefore lose the ability to enforce their invariants!). Given these apparent weaknesses, how can this architecture possibly be better than the altogether more Object-Orientation compliant RDM?

The reason that the Anaemic Domain Model is the superior choice for this use case follows from consideration of the SOLID principles, and their application to both of the architectures [12]. The ‘S’ refers to the Single Responsibility Principle [13], which suggests that a class should do one thing, and do it well, i.e., a class should implement a single abstraction. The ‘O’ refers to the Open/Closed Principle [14], a similar but subtly different notion that a class should be “open for extension, but closed for modification”; this means that, in so far as possible, classes should be written such that their implementation will not have to change, and that the impact of changes is minimised.

Superficially, the Customer class in the RDM appears to represent the single abstraction of a customer in the domain, but in reality this class is responsible for many things. The customer class models the business data and the business logic as a single abstraction, even though the logic tends to change with higher frequency that the data. The customer also constructs and initialises Order objects as a purchase is made, and contains the domain logic to determine if a customer can make an order. By providing CRUD operations through a base class, the customer domain entity is also bound to the persistence model supported by this base implementation. By enumerating these responsibilities it is clear that even in this trivial example, the RDM Customer entity exhibits a poor separation of concerns.

The ADM, on the other hand, decomposes responsibilities such that each component presents a single abstraction. The domain data is modelled in “plain-old” language data structures [18], while the domain rules and infrastructural concerns (such as persistence and object construction) are encapsulated in their own services (and presented via abstract interfaces). As a consequence, coupling is reduced.

Contrasting the flexibility of the RDM and ADM architectures

Consider scenarios in which the RDM Customer class would have to be modified; a new field might be introduced (or the type of an existing field may need changed), or the Order constructor may require an additional argument, or the domain logic for purchasing an item may become more complex, or an alternative underlying persistence mechanism might be required which is unsupported by the hypothetical DomainEntity base class.

Alternatively, consider scenarios in which the ADM types must change. The domain entities which are responsible for modelling the business data will only need modified in response to a requirements change for the business data. If the domain rules determining if an item is purchasable become more complex (e.g., an item is specified to only be sold to a customer above a certain “trust rating” threshold), only the implementation of IsItemPurchasableService must change, while in the RDM the Customer class would require changing to reflect this complexity. Should the ADM persistence requirements change, different implementations of the repository [17], [19] interfaces can be provided to the PurchaseService by the higher-level application services without requiring any changes whereas in the RDM, a base class change would impact all derived domain entities. Should the Order constructor require another argument, the IOrderFactory [5] implementation may be able to accommodate this change without any impact on the PurchaseService. In the ADM each class has a single responsibility and will only require modification if the specific domain rules (or infrastructural requirements) which concern the class are changed.

Now consider a new business requirement was added to support refunds for purchases with which a customer is unsatisfied. In the RDM, this might be implemented by adding a RefundItem method to the Customer domain entity, given the simplistic argument that domain logic related to the Customer belongs as a member function of the Customer domain entity. However, refunds are largely unrelated to purchases, for which the Customer domain entity is already responsible, further mixing the concerns of this type. It can be observed that in an RDM, domain entity classes can accumulate loosely related business logic, and grow in complexity. In an ADM, the refund mechanism could be implemented by introducing a RefundService class, solely concerned with the domain logic for processing refunds. This class can depend on the narrow set of abstractions (i.e., interfaces of other domain and infrastructural services) required to implement its single concern. The new RefundService can be invoked at high level (in response to some refund request), and this new domain behaviour has been implemented without impacting any of the existing functionality.

In the example, the ADM solves the problem of bundling unrelated concerns into the same module identified in the RDM by taking advantage of the ‘I’ and ‘D’ in SOLID, namely the Interface Segregation Principle [15] and the Dependency Inversion Principle [16]. These principles state that an interface should present a cohesive set of methods, and that these interfaces should be used to compose the application (i.e., the domain service layer in the ADM). The interface segregation principle tends to result in small narrowly focussed interfaces such as our IItemShippingRegionService and IIsItemPurchasableService, as well as abstract repository interfaces; the dependency inversion principle compels us to depend on these interfaces, to decouple the implementation of a service from the details of the implementation of another.

The Anaemic Domain Model better supports Automated Testing

As well as more flexible and malleable application composition, adoption of these principles allows the ADM to extract the indirect benefits over RDM of simpler automated testing; this is because highly cohesive, loosely coupled components which communicate via abstract interfaces and are composed via dependency injection allow for trivial mocking of dependencies. This means that in the ADM it is simple to construct a scenario in an automated test which might be more complicated to construct in an RDM, so the maintainability of the automated tests is improved; the effect of this is that automated testing has a lower cost, so developers will be more inclined to create and maintain tests. To illustrate this, consider the example above, such that unit tests are to be written for the IsItemPurchasable.

The (current) domain rules for an item being purchasable are that the customer has sufficient funds, and is in a region that the item ships to. Consider writing a test that checks that when a customer has sufficient funds but is not in a shipping region for the item, the item is not purchasable. In the RDM this test might be written by constructing a Customer and an Item, configuring the customer to have more funds than the item costs, and configuring the customer region to be outside the regions the item ships to, and asserting that the return value of customer.IsItemPurchasable(item) is false. However, the IsItemPurchasable method depends on the implementation details of the ShipsToRegion method of the Item domain entity. A change to the domain logic in Item might change the result of the test. This is undesirable, as the test should be exclusively testing the logic of the customer’s IsItemPurchasable method; a separate test should cover the specifics of the item’s ShipsToRegion method. As domain logic is expressed in the domain entity, and the concrete domain entity exposes the interface to the domain logic, implementations are tightly coupled such that the effects of changes cascade, which in turn makes automated tests brittle.

The ADM, on the other hand, expresses the IsItemPurchasable domain logic on a dedicated service, which depends on an abstract interface (the ShipsToRegion method of IItemShippingRegionService). A stubbed, mock implementation of IItemShippingRegionService can be provided for this test, which simply always returns false in the ShipsToRegion method. By decoupling the implementations of the domain logic, each module is isolated from the others and is insulated from changes in the implementation of other modules. The practical benefits of this are that a logic change will likely only result in the breakage of tests which were explicitly asserting on the behaviour which has changed, which can be used to validate expectations about the code.

Refactoring the RDM to apply SOLID tends to result in an ADM

A proponent of the RDM architecture might claim that the hypothetical example provided is not representative of an true RDM. It might be suggested that a well implemented Rich Domain Model would not mix persistence concerns with the domain entity, instead using Data Transfer Objects (DTO’s) [18, 17] to interface with the persistence layer. The inclusion of directly invoking the Order constructor might be viewed as constructing a straw man to attack; of course no domain model implementation would bind itself directly to the constructor of another object, using a factory is just common sense [5]! However, this appears to be an argument for applying the SOLID principles to the application level infrastructural services, and disregarding the SOLID principles for domain design. As the hypothetical RDM is refactored to apply the SOLID principles, more granular domain entities could be broken out; the Customer domain entity might be split into CustomerPurchase and CustomerRefund domain models. However, these new domain models may still depend on atomic domain rules which may change independently without otherwise affecting the domain entity, and might be depended on by multiple domain entities; to avoid duplication and coupling, these domain rules could then be further factored out into their own modules and accessed via an abstract interface. The result is that as the hypothetical RDM is refactored to apply the SOLID principles, the architecture tends towards the ADM!

Conclusion

By exploring the implementation of a straightforward example, we have observed that an Anaemic Domain Model better adheres to the SOLID principles than a Rich Domain Model. The benefits of adherence to the SOLID principles in the context of domain design were considered, in terms of both loose coupling and high cohesion and resulting increased flexibility of the architecture; evidence of this flexibility was that testability was improved by being able to trivially provide stubbed test implementations of dependencies. By considering a how the benefits of adherence to the SOLID principles might be gained in the RDM, the refactoring tended to result in an architecture resembling an ADM. If adherence to the SOLID principles is a property of well engineered Object-Oriented programs, and an ADM adheres better to these principles than an RDM, the ADM cannot be an anti-pattern, and should be considered a viable choice of architecture for domain modelling.

References

[1] Fowler, Martin. Anaemic Domain Model. http://www.martinfowler.com/bliki/AnemicDomainModel.html, 2003.

[2] Evans, Eric. Domain-driven design: tackling complexity in the heart of software. Addison-Wesley Professional, 2004.

[3] Martin, Robert C. The Principles of Object-Oriented Design. http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod, 2005.

[4] Martin, Robert C. Design principles and design patterns. Object Mentor, 2000: 1-34.

[5] Erich, Gamma, et al. Design patterns: elements of reusable object-oriented software. Addison Wesley Publishing Company, 1994.

[6] Wolfgang, Pree. Design patterns for object-oriented software development. Addison-Wesley, 1994.

[7] Rising, Linda. The patterns handbook: techniques, strategies, and applications. Vol. 13. Cambridge University Press, 1998.

[8] Budgen, David. Software design. Pearson Education, 2003.

[9] Scott, Michael L. Programming language pragmatics. Morgan Kaufmann, 2000.

[10] Hevery, Miško. Writing Testable Code. http://googletesting.blogspot.co.uk/2008/08/by-miko-hevery-so-you-decided-to.html, Google Testing Blog, 2008.

[11] Osherove, Roy. The Art of Unit Testing: With Examples in. Net. Manning Publications Co., 2009.

[12] Martin, Robert C. Agile software development: principles, patterns, and practices. Prentice Hall PTR, 2003.

[13] Martin, Robert C. SRP: The Single Responsibility Principle. http://www.objectmentor.com/resources/articles/srp.pdf, Object Mentor, 1996.

[14] Martin, Robert C. The Open-Closed Principle. http://www.objectmentor.com/resources/articles/ocp.pdf, Object Mentor, 1996.

[15] Martin, Robert C. The Interface Segregation Principle. http://www.objectmentor.com/resources/articles/isp.pdf, Object Mentor, 1996.

[16] Martin, Robert C. The Dependency Inversion Principle, http://www.objectmentor.com/resources/articles/dip.pdf, Object Mentor, 1996.

[17] Fowler, Martin. Patterns of enterprise application architecture. Addison-Wesley Longman Publishing Co., Inc., 2002.

[18] Fowler, Martin. Data Transfer Object. http://martinfowler.com/eaaCatalog/dataTransferObject.html, Martin Fowler site, 2002.

[19] Fowler, Martin. Repository. http://martinfowler.com/eaaCatalog/repository.html, Martin Fowler site, 2002.

[20] Fowler, Martin. Domain Model. http://martinfowler.com/eaaCatalog/domainModel.html, Martin Fowler site, 2002.

## 15 thoughts on “The Anaemic Domain Model is no Anti-Pattern, it’s a SOLID design”

1. lanzz says:

bool shippable = shipsToRegionService.ShipsToRegion(item);

You probably mean  bool shippable = shipsToRegionService.ShipsToRegion(item, customer.Region);

2. Kim A. Betti says:

Am I the only one who finds it ironic that the object oriented SOLID principles are being used to advocate procedural code?

I mean, is it not a key premise of object oriented programming that classes should have methods operating on its own private data – not reaching into the guts of other classes working directly with their data?

3. Jordan says:

I’m not really convinced of this, and I think I know why.

1. In you RDM example you hot glued your entire data access layer to your business logic layer. I wouldn’t say ” this appears to be an argument for applying the SOLID principles to the application level infrastructural services, and disregarding the SOLID principles for domain design.” I’d say that it’s arguing to apply solid principles to both your application and your infrastructure.

2. You have mixed your nouns and verbs. When a customer goes to the store he holds items and brings them to the checkout to purchase them. Your RDM should be modeled accordingly. Your customers don’t hold a basket of “buying milk”. They just hold milk. You take your milk to the checkout, the checkout evaluates a customer and the items that the customer has. It’s not the customers responsibility to say if he can purchase his items, it’s the responsibility of the attendant at the checkout. Take real note of how the RDM models physical interaction and how it relates to real world usage. Also note how the ADM has no physical counterpart and can only exist as a construct of mathematics. I know which one I’d rather maintain.

4. Peter Verhas says:

I can not fully agree with your statements. While I understand that there are problems with the RDM approach and your examples clearly show issues, the consequence may not lead directly to solve the issue following ADM pattern. I may happen that a different RDM is fitting.

I usually try to avoid thinking about programming objects when looking at a design. I try to imagine how things would work in real life. For example looking at the Customer/Item/Purchase triangle I would set up the question to myself: Would I ask a customer about an item if the item is deliverable to him/her? Certainly not. This feature is more tied to the Item rather than the user. I would look up the allowed target delivery data sheet of the item (I imagine one printed on paper) and compare that to the requested order sheet containing some of the details of the customer (probably not all data). The implementing method then should be in Item and different types of items (different classes extending some abstract or non-abstract Item class) may even implement different algorithms.

While this is true that RDM ties the business algorithms more tightly to the objects, this is not a drawback but rather an advantage so long as long the algorithms are tied to the proper object.

5. Arturo Hernandez says:

I think the strongest argument you give here is for the single responsibility principle. Although there is some interpretation to be made of the principle. Some people may say that you are not modeling data but you are modeling an object. But there could be a slippery slope in that argument. An object can certainly be very complex.

I personally think there is a tension between the information processing nature of software, and the underlying mechanical execution on hardware. And also I can’t help but to think that there is something deeper at play. Where, two apparently opposite points of view have compelling arguments to their favor.

6. Max says:

Good article, thank you. But what you’ve actually described under “right way of using Anaemic model” is the Functional approach to domain modeling vs the OOP approach (RDM). Functional approach definitely has many advantages, so I think it would be better to use functional language for that instead of creating classes with only behavior and no data. That is just not an OOP. Choosing the right tool for the job, right?

7. Suamere says:

Well described. I’ve been using ADM for a long time and have frequently been told how some dude named Martin Fowler disagrees with it. (Granted, I do like most of Marty’s ideas, he’s pretty legit.) But many of these have always been my argument. As my company moved toward requiring Unit Testing, they came to see how tightly coupled their previous models were and had to do some brownfield shims/fakes to seperate them out. When they were done, they had a messy RDM which might as well have been ADM. At this point, many Unit Testing and SOLID Principles are contradicting some teachers’ words against ADM design. I’ve always been a fan. Thanks for the clarity.

8. coder says:

Separating behavior from the model has helped a lot to have smaller unit tests. The requirements can be met in a very well organized and isolated manner. Otherwise, the model becomes too heavy and easy to break.

In the following scenario, I prefer covering the case with an easy unit test rather than having to create an instance of a model with a thousand items.

public bool HasExceedAmountOfItems(int numberOfItems)
{
return numberOfItems > 1000;
}

There is nothing bad in a procedural design. Actually, the code becomes very easy to follow!

Thank you for the great article. It gets really difficult in a team arguing against someone of the reputation of Martin Fowler.
http://www.martinfowler.com/bliki/AnemicDomainModel.html

9. Taras German says:

Thank you for great article. Great to see confirmation of ideas we are currently applying in our company. In order to improve testability of our code we are making one more step forward using ideas from functional programming and organize our business logic as single ‘operations’ with clearly defined global and call-context dependencies. This way mocking dependencies in unit tests has become so much easier. Also integration tests have become much easier to program. Overall understanding and reasoning about our code has improved. Decoupling and decomposing bigger projects in smaller components also improved. We use BDD and the above approach facilitated translation of user stories/scenarios into unit tests/integration tests. If anything I wonder why we were blinded by OOP dogmas for so long time. Again thanks for thoughtful article.

10. Al Rose says:

This is an interesting argument but the example used isn’t convincing. The main issues that the need to be covered in the discussion are composition, and abstraction.

I can see, for example a case for checking that an item can be purchased in the service layer as part of an ordering service. The result may involve stock availability checks by an inventory, shipping legality checks by a shipping regulator, a funds balance from a customer fund…. . I don’t see why the service layer would anything but co-ordinate this though. I’d expect the inventory to reduce stock not the service. I’d expect the order to have a total method, not the service. I’d also expect the customer fund to be part of the composition of the customer.

With (rich) domain classes, behaviour can already vary by inheritance, and composition patterns (including strategy and decorator patterns) so the SOLID arguments does not convince me with the example used. Would you be OK to elaborate on the case for method less domain objects? I, as with many programmers, am always open to new ways of doing things but also know the dangers of going down a route that goes brittle under load.

11. Robert says:

Thank you very much for writing and sharing such a great and exact article! I have finally found the ultimate answer to my dilemma ADM vs. RDM.

12. Christian says:

Well, you are partly right but also missed the point of a rich domain model. An anemic domain model isn’t an anti pattern for all applications. For simple CRUD applications – like you showed in your example – it is perfecly ok. In that case ADM is simple and easy to use, just KISS. But when you mention Eric Evans you should also know in wich context he is talking about the rich domain model. He talks about it in the context of Domain Driven Design which should never be applied to simple applications but only to large applications with complex domain logic. And that’s exaclty the point: many software developers are also applying adm in that context which leads to architectures called Bug Ball of Mud (one of the most used architectual patterns I’ve seen so far).

So, what’s the problem with ADM for applications with complex domain logic?

The business logic is scattered all over different services probably located in different packages. Now imagin you have to extend the domain object (in your example it would be the Item class): you either have to include if-else to type check in your service classes which clearly violates the O in SOLID or you have to search all services that are using the extended class and which should reflect the change – simple in a small application but this can get really hard in a large one.

You also get very little knowledge by looking at your domain class. Do you know why a developer added a specific property to the class, under which condition it should be used, what restictions exist for the property by just looking at the class? No. You only can if you search all the service classes scattered all over the code and look on how the property is really used.

Also the Active Record Pattern from your example is not recommended for RDM and Evans clearly votes against it. In the best case the RDM should be persistent ignorant. There should be no dependency to the infrastructure – just business login! This way it WILL be easy to test.

The benefit of a RDM is you have the business logic and all the constraints at one place. The problem domain can be understood by just looking at the domain model.
On the other hand an ADM is simpler and easy to implement. That’s why ADM is the prefered way (and therefore not an antipattern) for small and some medium applications.

13. Star says:

In a quick read I see two problems with the example code with which you intend to prove your point:
1) Your design is based on Active Record: that pattern alone breaks a los of principles and is not a RDM feature.
2) The validation “IsItemPurchasable” can be implemented with the Specification pattern (a particular instantiation of the strategy pattern) if you think this “rule” is likely to change.

But more importantly, one potentially poor design (as, IMO, you present here) isn’t enough to prove wrong a general idea, even if it’s sold as a very well sourced paper.

14. Star says:

In a quick read I see two problems with the example code with which you intend to prove your point:
1) Your design is based on Active Record: that pattern alone breaks a lots of principles and is not a RDM feature.
2) The validation “IsItemPurchasable” can be implemented with the Specification pattern (a particular instantiation of the strategy pattern) if you think this “rule” is likely to change.

But more importantly, one potentially poor design (as, IMO, you present here) isn’t enough to prove wrong a general idea, even if it’s sold as a very well sourced paper.