From the translator: On the project where I work, now there is an active rewriting of logic, previously implemented as a rich domain model (using Active Record and Unit of Work). The new approach includes classes of entities with no behavior and stateless services that interact through interfaces — in fact, it is an anemic model, with the prospect of a further transition to the microservice architecture. Observing in real time how the “macaroni monster” of about one and a half million LOC gradually takes shape, how to simplify testing, scaling and customizing the system for different customers, I was quite surprised to learn that this approach is often considered as an architectural anti-pattern . Trying to figure out the reasons for this, I stumbled upon this article and post its translation here to discuss the pros and cons of the approach with the community.
Original: The Anaemic Domain Model is No Anti-Pattern, it's a SOLID design
Speaking of object-oriented software development, design patterns understand repetitive and effective ways to solve common problems. Thanks to the formalization and description of such patterns, developers get a set of “proven in battle” architectural solutions for certain classes of problems, as well as a common dictionary for their description, understandable to other developers. This term was first introduced by Erich Gamma in his book Object-Oriented Design Techniques. Design patterns ”[5], where he described several frequently used templates. As the new concept gained popularity, the vocabulary of design patterns replenished ([6], [17]).
Following the growing popularity of the concept of design patterns, the idea of ​​“anti-patterns” was introduced ([7], [8]). As the name implies, anti-pattern is the opposite of a pattern. He also describes a recurring way to solve a problem that often arises, however, as a rule, it is a non-working or ineffective solution that has a negative effect on the health of the system (in terms of ease of support, extensibility, reliability, etc.). Anti-templates serve the same purposes as templates: when describing an anti-template, they show typical implementation options, reveal the context in which it is applied, and explain to which problems in the developed software this leads to.
But the concept of anti-patterns has its drawback: reducing the criticality of perception in the question of the applicability of a particular pattern. Architectural solutions that are not applicable in one situation may turn out to be a reasonable choice in another, however, if the solution is recognized as an anti-pattern, it can be rejected without discussion, even if in fact it was quite suitable for the problem to be solved.
I am convinced that one of these undeservedly rejected anti-patterns is the Anemic Domain Model (AMAP, Anaemic Domain Model), described by Martin Fowler [1] and Eric Evans [2]. Both authors describe this pattern as the inability to model the subject area in an object-oriented style, which is why business logic is described in a procedural style. Such an approach is contrasted with the Rich Domain Model (BMP, Rich Domain Model) [1], [20] - in it the classes representing entities of the domain contain both data and all business logic. Yes, an anemic model can be a bad choice for some systems, but it’s not at all true that the same is true for any system. In this article, I consider the arguments put forward against the anemic model, and justify why in a number of scenarios the AMPO looks like a reasonable choice in terms of compliance with the SOLID principles, formulated by Robert Martin ([3], [4]), to the principles that contain the recommendations to achieve a balance between simplicity, scalability and reliability in software development. Solving a hypothetical problem and comparing the anemic and rich models, I intend to show that AMPO fits better with the SOLID principles. Thus, I want to challenge the categorical opinion about this approach, imposed by the authorities, and to show that the use of AMPS is, in fact, a suitable architectural solution.
Fowler [1] and Evans [2] described AMPO as a set of non-behavior classes containing data necessary for domain modeling. In these classes, there is practically no (or not at all) logic to validate the data for compliance with business rules. Instead, business logic is enclosed in a services layer, which consists of types and functions that process model elements in accordance with business rules. The main argument against this approach is that the data and methods for their processing are separated, which violates one of the fundamental principles of the object-oriented approach, since does not allow the model to provide its own invariants. In contrast, although BUMO consists of the same set of types containing data about the subject area, but all business logic is also contained in these entities, being implemented as class methods. Thus, BUMO is in good agreement with the principles of encapsulation and information hiding. As noted by Michael Scott in [9]: “Thanks to encapsulation, developers can combine data and processing operations in one place, as well as hide unnecessary details from users of the generic model.”
In BMPP, the service layer is extremely thin, and sometimes completely absent [20], and all rules relating to the subject area are implemented through the model. Thus, it is argued that the entities of the subject area are able to fully provide for their own invariants, which makes such a model complete from the point of view of the object-oriented approach.
It should not be forgotten, however, that the ability of a model to enforce certain constraints imposed on data is only one of the many properties that a system must have. Let AMPO sacrifice the possibility of validation at the level of individual business entities, but instead it gives an incredible flexibility and simplicity of supporting the system as a whole, due to the fact that the implementation of logic is made into highly specialized classes, and access to them is provided through interfaces. These advantages are particularly important in languages ​​with static typing, such as Java or C # (in which class behavior cannot be changed during program execution), because improve the testability of the system by introducing explicit "seams" ([10], [11]) in order to eliminate excessive coupling.
Let's imagine the server part of an online store, where a customer can both buy goods and put up goods for sale for other customers from all over the globe. The purchase of goods leads to a decrease in funds in the account of the buyer. We will consider how to implement the process of placing an order for the purchase of goods by the client. According to the requirements, the client can place an order if he has a) enough funds in the account, and b) the product is available in the client's region. When using BUMO, the Customer class will describe the “Customer” entity; it will include all customer properties and methods such as PurchaseItem (Item item ). Similarly, the Item and Order classes represent domain models that describe the Goods and Order entities, respectively. The implementation of the Customer class (on pseudo-C #) can be something like this:
/*** ***/
class Customer : DomainEntity // , CRUD-
{
//
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();
}
}
}
/*** ***/
Active Record [17], Create/Read/Update/Delete ( ), (, ). , PurchaseItem , (, HTTP-, ). , «» 1) , 2) -, 3) «» 4) , Active Record. , «» , .
, , :
/*** ***/
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;
//
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);
}
}
}
/*** ***/
, . , (IPurchaseService IItemPurchasableService) (IOrderFactory, ICustomerRepository IOrderRepository), . , ( — ! — ). , - ?
, , SOLID [12]. «S» « » (Single Responsibility Pronciple, [13]), , - — . , . «O» — « /» (Open/Closed Principle, [14]), , « , ». , , , .
, Customer «», . , , , - , . «» , , , . CRUD-, , «» , . , , Customer .
, . «» [18], - (, ..) ( ). , .
, Customer .
, , . -, , , . , (, « » , ), IIsItemPurchasableService, Customer. — PurchaseService [17], [19], ; , -, . , Order , IOrderFactory , PurchaseService. , ( ).
, - , . RefundItem «», , , , Customer. , Customer, . , -, . RefundService, , . (.. ), . RefundService ( ), , .
, , I D SOLID. , , « » (Interface Segregation Principle, [15]) « » (Dependency Inversion Principle, [16]). , , ( — ). , , , — IItemShippingRegionService IIsItemPurchasableService , . , .
, , . , , «», mock-. , , , . «» . , IsItemPurchasable.
, , , , . , , , , , , . , , (Customer) (Item), , , , . , customer.IsItemPurchasable(item) false. IsItemPurchasable ShipsToRegion Item. -, , . , , Customer, ShipsToRegion, «», . - , , , , - .
, IsItemPurchasable , ( IItemShippingRegionService.ShipsToRegion). IItemShippingRegionService, ShipsToRegion, false. - , . , «» , , , .
, , , «» . , — (DTO, Data Transfer Object, [17], [18]), . Order Customer — , , [5]! , SOLID , . SOLID, : « » (CustomerPurchase) « . » (CustomerRefund). , - -, , , , . , , . , , SOLID, !
, , SOLID, . , SOLID: , . - «» . , , «».
, SOLID - , , , -; .
[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.
Source: https://habr.com/ru/post/346016/
All Articles