| Both sides previous revision Previous revision Next revision | Previous revision |
| arch:adm [2021/10/18 10:36] – [VI. Procedural programming is easier to catch] khdzhambongtenke | arch:adm [2026/08/29 07:59] (current) – external edit 127.0.0.1 |
|---|
| |
| Why despite its high propagation, some high figures in software engineering like Martin Fowler [2] still consider ADM as being anti-pattern? Well, there are multiple reasons: | Why despite its high propagation, some high figures in software engineering like Martin Fowler [2] still consider ADM as being anti-pattern? Well, there are multiple reasons: |
| * ADM violates the **encapsulation** and **abstraction** principles of OOP, since the models contain only data and not logic associated with operating those data (except for some getters and setters). | |
| * In the context of model/database mapping, ADM is prone to difficulties, especially when the model will evolve and become more complex. | * ADM violates the **encapsulation** and **abstraction** principles of OOP, since the models contain only data and not logic associated with operating those data (except for some getters and setters). |
| * ADM transforms domain models into mere domain data carriers. | * In the context of model/database mapping, ADM is prone to difficulties, especially when the model will evolve and become more complex. |
| * ADM is a failed attempt to use object-oriented programming, that ended on procedural programming. | * ADM transforms domain models into mere domain data carriers. |
| | * ADM is a failed attempt to use object-oriented programming, that ended on **procedural programming**. |
| All those reasons seem good enough for developers to avoid this anti-pattern, right? Well, not exactly. In the next few sections, we will consider the good aspects of ADM and why software engineers still use it. | All those reasons seem good enough for developers to avoid this anti-pattern, right? Well, not exactly. In the next few sections, we will consider the good aspects of ADM and why software engineers still use it. |
| |
| In ADM, we observe **highly cohesive, loosely coupled** components which communicate via abstract interfaces. Those components are composed via dependency injection allowing for trivial mocking of dependencies. Therefore, scenario construction is made easier during automated test. In RDM, this process is more complicated to construct since tight coupling proliferates, making ADMs’ automated tests more maintainable. For illustration, let’s consider the following Reach Domain Model: | In ADM, we observe **highly cohesive, loosely coupled** components which communicate via abstract interfaces. Those components are composed via dependency injection allowing for trivial mocking of dependencies. Therefore, scenario construction is made easier during automated test. In RDM, this process is more complicated to construct since tight coupling proliferates, making ADMs’ automated tests more maintainable. For illustration, let’s consider the following Reach Domain Model: |
| |
| {{ :arch:reach_domaon_model.jpg?600 |}} | [[arch:_detail:arch:reach_domaon_model.jpg|]] |
| |
| Suppose we ought to write unit tests on the //IsItemPurchasable(Item item)// method. The domain rules requires that the customer has enough funds and is in an allowed shipping region for that specific item for an item to be purchasable. Let’s consider a test checking the scenario when a customer has sufficient funds but is not in an allowed shipping region for that specific item (meaning the item is not purchasable). In accordance with the RDM this test shall be written as follow: | Suppose we ought to write unit tests on the //IsItemPurchasable(Item item)// method. The domain rules requires that the customer has enough funds and is in an allowed shipping region of that specific product for an item to be purchasable. Let’s consider a test checking the scenario when a customer has sufficient funds but is not in an allowed shipping region of that specific product (meaning the item is not purchasable). In accordance with the RDM this test shall be written as follow: |
| * construct a //Customer// and an //Item//, | |
| * configure that customer to have enough funds, | |
| * configure the customer region to be outside the allowed shipping regions for that item, | |
| * at the end, we need to assert that //customer.IsItemPurchasable(item)// returned value is false. | |
| |
| However, the //IsItemPurchasable// method is directly dependent on the implementation of the //ShipsToRegion// method of the Item domain model. Any change in the domain logic in Item will change the result of the test. This is not the desired behavior, as the test should only be testing the customer’s //IsItemPurchasable// method logic. This bad behavior is the result of tight coupling and result on difficult code testability, which is familiar while using RDM. This simple example used few methods and already we see difficulties being draws. Imagine this same RDM with dozens of tightly coupled domain models. Testing in this case becomes a nightmare. | * construct a //Customer// and an //Item//, |
| | * configure that customer to have enough funds, |
| | * configure the customer region to be outside the allowed shipping regions for that item, |
| | * at the end, we need to assert that //customer.IsItemPurchasable(item)// returned value is false. |
| | |
| | However, the //IsItemPurchasable// method is directly dependent on the implementation of the //ShipsToRegion// method of the //Item// domain model. Any change in the domain logic in Item will change the result of the test. This is not the desired behavior, as the test should only be testing the customer’s //IsItemPurchasable// method logic. This bad behavior is the result of tight coupling and result on difficult code testability, which is familiar while using RDM. This simple example used few methods and already we see difficulties being drawn. Imagine this same RDM with dozens of tightly coupled domain models. Testing in this case becomes a nightmare. |
| |
| On the other hand, ADM requires us to: | On the other hand, ADM requires us to: |
| * express the //IsItemPurchasable// logic within a separate service, which loosely depend on an abstract interface (the //ShipsToRegion// method of //IItemShippingRegionService//), | |
| * provide a stubbed, mock implementation of //IItemShippingRegionService// for this test, which will always return false in the //ShipsToRegion// method. | * express the //IsItemPurchasable// logic within a separate service, which loosely depend on an abstract interface (the //ShipsToRegion// method of //IItemShippingRegionService//), |
| | * provide a stubbed, mock implementation of //IItemShippingRegionService// for this test, which will always return false in the //ShipsToRegion// method. |
| |
| ===== V. Anemic Domain represents developers’ view of OOP ===== | ===== V. Anemic Domain represents developers’ view of OOP ===== |
| |
| ===== Conclusion ===== | ===== Conclusion ===== |
| In this essay, we took a quick look into the notion of design patterns, then we made a critical analysis of the so called Anemic Domain Model (ADM). We assessed why it is being labelled as an “anti-pattern” by some. After that, we considered the good aspects of ADM and why people still use it. Despite its multiple cons, I personally consider ADM to be a great approach in software design. It helps build applications with domain models that follow the SOLID principles. Three scenarios when ADM is preferable: | |
| * When working in presence of a ORM library to operate data with the persistence layer. | |
| * When the system’s business logic implies strong coupling between models. | |
| * When testability is important, but we want to spend minimal time modifying / refactoring tests in each new model modification. | |
| |
| | In this essay, we took a quick look into the notion of design patterns, then we made a critical analysis of the so called Anemic Domain Model (ADM). We assessed why it is being labelled as an “anti-pattern” by some. After that, we considered the good aspects of ADM and why people still use it. Despite its multiple cons, I personally consider ADM to be a great approach in software design. It helps build applications with domain models that follow the SOLID principles. Three scenarios when ADM is preferable: |
| |
| ===== References ===== | * When working in presence of a ORM library to operate data with the persistence layer. |
| | * When the system’s business logic implies strong coupling between models. |
| | * When testability is important, but we want to spend minimal time modifying / refactoring tests in each new model modification. |
| |
| - https://martinfowler.com/bliki/DomainDrivenDesign.html | ===== References ===== |
| - https://martinfowler.com/bliki/AnemicDomainModel.html | |
| - https://blog.codecentric.de/en/2019/10/ddd-vs-anemic-domain-models/ | |
| - https://blog.inf.ed.ac.uk/sapm/2014/02/04/the-anaemic-domain-model-is-no-anti-pattern-its-a-solid-design/ | |
| - https://medium.com/oceanize-geeks/the-active-record-and-data-mappers-of-orm-pattern-eefb8262b7bb | |
| |
| | - [[https://martinfowler.com/bliki/DomainDrivenDesign.html|https://martinfowler.com/bliki/DomainDrivenDesign.html]] |
| | - [[https://martinfowler.com/bliki/AnemicDomainModel.html|https://martinfowler.com/bliki/AnemicDomainModel.html]] |
| | - [[https://blog.codecentric.de/en/2019/10/ddd-vs-anemic-domain-models/|https://blog.codecentric.de/en/2019/10/ddd-vs-anemic-domain-models/]] |
| | - [[https://blog.inf.ed.ac.uk/sapm/2014/02/04/the-anaemic-domain-model-is-no-anti-pattern-its-a-solid-design/|https://blog.inf.ed.ac.uk/sapm/2014/02/04/the-anaemic-domain-model-is-no-anti-pattern-its-a-solid-design/]] |
| | - [[https://medium.com/oceanize-geeks/the-active-record-and-data-mappers-of-orm-pattern-eefb8262b7bb|https://medium.com/oceanize-geeks/the-active-record-and-data-mappers-of-orm-pattern-eefb8262b7bb]] |