Jump to content

Anemic domain model

From Wikipedia, the free encyclopedia

The anemic domain model is described as a programming anti-pattern where the domain objects contain little or no business logic like validations, calculations, rules, and so forth. The business logic is thus baked into the architecture of the program itself, making refactoring and maintenance more difficult and time-consuming.

Overview

[edit]

This anti-pattern was first described[1] by Martin Fowler, who considers the practice an anti-pattern. He says:

The fundamental horror of this anti-pattern is that it's so contrary to the basic idea of object-oriented designing; which is to combine data and process together. The anemic domain model is just a procedural style design, exactly the kind of thing that object bigots like me ... have been fighting since our early days in Smalltalk. What's worse, many people think that anemic objects are real objects, and thus completely miss the point of what object-oriented design is all about.

In an anemic domain design, business logic is typically implemented in separate classes which transform the state of the domain objects. Fowler calls such external classes transaction scripts. This pattern is a common approach in Java applications, possibly encouraged by technologies such as early versions of EJB's Entity Beans,[1] as well as in .NET applications following the Three-Layered Services Application architecture where such objects fall into the category of "Business Entities" (although Business Entities can also contain behavior).[2]

Fowler describes the transaction script pattern thus:

Most business applications can be thought of as a series of transactions. A transaction may view some information as organized in a particular way, another will make changes to it. Each interaction between a client system and a server system contains a certain amount of logic. In some cases this can be as simple as displaying information in the database. In others it may involve many steps of validations and calculations. A Transaction Script organizes all this logic primarily as a single procedure, making calls directly to the database or through a thin database wrapper. Each transaction will have its own Transaction Script, although common subtasks can be broken into subprocedures.[3]

In his book "Patterns of Enterprise Application Architecture", Fowler noted that the transaction script pattern may be proper for many simple business applications, and obviates a complex OO-database mapping layer.

An anemic domain model might occur in systems that are influenced from Service-Oriented Architectures, where behaviour does not or tends to not travel, such as messaging/pipeline architectures, or SOAP/REST APIs. Architectures like COM+ and Remoting allow behaviour, but increasingly the web has favoured disconnected and stateless architectures.

Criticism

[edit]

There is some criticism as to whether this software design pattern should be considered an anti-pattern, since many see also benefits in it, for example:

  • Clear separation between logic and data.[4]
  • Works well for simple applications.
  • Results in stateless logic, which facilitates scaling out.
  • Avoids the need for a complex OO-Database mapping layer.
  • More compatibility with mapping and injection frameworks expecting dumb properties rather than a specific constructor or property population order.[4]

A common criticism is the idea that anemic domain model makes it easier to follow the SOLID principles:

"The ‘S’ refers to the Single Responsibility Principle, which suggests that a class should do one thing, and do it well (...)".[5]

But, according to Robert C. Martin, this is a misunderstanding of that principle:

"Of all the SOLID principles, the Single Responsibility Principle (SRP) might be the least well understood. That’s likely because it has a particularly inappropriate name. It is too easy for programmers to hear the name and then assume that it means that every module should do just one thing. Make no mistake, there is a principle like that. A function should do one, and only one, thing. We use that principle when we are refactoring large functions into smaller functions; we use it at the lowest levels. But it is not one of the SOLID principles—it is not the SRP. (...) the final version of the SRP is: A module should be responsible to one, and only one, actor.[6]"

Liabilities

[edit]

Certain liabilities the programmer must consider are introduced by using an anemic domain model:

  • Logic cannot be implemented in a truly object-oriented way.
  • Violation of the encapsulation and information hiding principles.
  • Needs a separate business layer to contain the logic otherwise located in a domain model. It also means that domain model's objects cannot guarantee their correctness at any moment, because their validation and mutation logic is placed somewhere outside (most likely in multiple places).
  • Needs a service layer when sharing domain logic across differing consumers of an object model.
  • Makes a model less expressive. .

Example

[edit]

An anemic domain model would have one write code like the following (written in C#), which by itself does not implement any of the business concerns, in this case, that a height or a width cannot be zero or negative, or that somewhere else there is a requirement for the area of the rectangle. This means that those functionalities are implemented somewhere else, no longer on the "business" side of the program, but somewhere else hidden within its architecture.

class Rectangle
{
    public int Height { get; set; }
    public int Width { get; set; }
}

A non-anemic rewrite of the above class could look like the following. The business concerns are now handled in the domain object, while the architecture can be more domain-agnostic. This allows the program to assume certain attributes are true about objects without implementing validity checks elsewhere within the architecture.

class Rectangle
{
    public int Height { get; private set; }
    public int Width { get; private set; }

    public Rectangle(int height, int width)
    {
        SetHeight(height);
        SetWidth(width);
    }

    public void SetHeight(int height)
    {
        if (height <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(height));
        }

        Height = height;
    }

    public void SetWidth(int width)
    {
        if (width <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(width));
        }

        Width = width;
    }

    public int CalculateArea()
    {
        return Height * Width;
    }
}

See also

[edit]

References

[edit]
  1. ^ a b "Bliki: AnemicDomainModel".
  2. ^ "Application Architecture for .NET: Designing Applications and Services". Archived from the original on 2013-01-10. Retrieved 2013-02-13.
  3. ^ "P of EAA: Transaction Script".
  4. ^ a b "The Anaemic Domain Model is no Anti-Pattern, it's a SOLID design – SAPM: Course Blog". 4 February 2014.
  5. ^ "The Anaemic Domain Model is no Anti-Pattern, it's a SOLID design – SAPM: Course Blog". 4 February 2014. Retrieved 2022-09-14.
  6. ^ Martin, Robert C. (2018). Clean architecture : a craftsman's guide to software structure and design. Boston. ISBN 978-0-13-449432-6. OCLC 1003645626.{{cite book}}: CS1 maint: location missing publisher (link)
[edit]