Rich vs Anemic Domain Model [closed]
Asked Answered
S

10

127

I am deciding if I should use a Rich Domain Model over an Anemic Domain Model, and looking for good examples of the two.

I have been building web applications using an Anemic Domain Model, backed by a Service --> Repository --> Storage layer system, using FluentValidation for BL validation, and putting all of my BL in the Service layer.

I have read Eric Evan's DDD book, and he (along with Fowler and others) seems to think Anemic Domain Models are an anti-pattern.

So I was just really wanting to get some insight into this problem.

Also, I am really looking for some good (basic) examples of a Rich Domain Model, and the benefits over the Anemic Domain Model it provides.

Sapele answered 26/4, 2014 at 17:28 Comment(4)
You may also want to check out this blog that argues in favour of anaemic domain modelKammerer
DDD>ADM, ADM>DDD, DDD>ADM, ADM>DDD, ADM+DDD... DDD/ADM, or how not to agree about software design!Kaltman
Here is an example of how to avoid anemic domain model: medium.com/@wrong.about/…Bloodstain
It’s funny that this question could have been answered with a single link to a real world project funded by real organisation. After 5 years, no good answer, IMO. Talk is cheap. Show me the code.Lamprey
P
84

The difference is that an anemic model separates logic from data. The logic is often placed in classes named **Service, **Util, **Manager, **Helper and so on. These classes implement the data interpretation logic and therefore take the data model as an argument. E.g.

public BigDecimal calculateTotal(Order order){
...
}

while the rich domain approach inverses this by placing the data interpretation logic into the rich domain model. Thus it puts logic and data together and a rich domain model would look like this:

order.getTotal();

This has a big impact on object consistency. Since the data interpretation logic wraps the data (data can only be accessed through object methods) the methods can react to state changes of other data -> This is what we call behavior.

In an anemic model the data models can not guarantee that they are in a legal state while in a rich domain model they can. A rich domain model applies OO principles like encapsulation, information hiding and bringing data and logic together and therefore a anemic model is an anti pattern from an OO perspective.

For a deeper insight take a look at my blog https://www.link-intersystems.com/blog/2011/10/01/anemic-vs-rich-domain-models/

Pavyer answered 28/4, 2015 at 8:37 Comment(10)
Let's say calculating an order's total price involves: 1) Applying a discount which depends on the customer being member of one of possible many loyalty programs. 2) Applying a discount for orders that contain a specific group of items together depending on the current marketing campaign run by the store. 3) Calculating tax where amount of tax depends on each specific item of the order. In your opinion, where would all this logic belong? Could you please give a simple pseudo-code example. Thank you!Lott
@RenéLink With the anemic approach would you place the Service/Manager classes inside of the same Domain class library or in a separate class library. Additionally, should the Service/Managers ever make calls to the repository or be passed in everything they need with prior calls to the repository?Laccolith
@RenéLink I figure there might be some advantages if a WebAPI or MVC app first calls var order = _orderRepository.GetOrderById(id), then _orderManager.GetTotal(order). This way before processing the business logic a Not Found exception can be thrown if GetOrderById returns null.Laccolith
Don't Service layers abstract from the actual models, the classes, decoupling? That is a layer between the actual model and the consumer, like a ViewModel?Germanophobe
@Nik In the rich model, the Order would have a reference to the Customer object, and the Customer object would have reference to the Loyalty Program. Thus, the Order would have access to all of the information it needed without needing explicit references to things like services and repositories from which to fetch that information. However, it does seem easy to run into a case where cyclical references are happening. I.e. Order references Customer, Customer has a list of all Orders.I think this may have be partially why people prefer Anemic now.Kiley
@Kiley The approach you describe works really well. There is one catch. Likely we store the entities in a DB. So, to calculate an order's total we have to fetch the Order, Customer, Loyalty Program, Marketing Campaign, Taxes Table from the DB. Consider also, a Customer has a collection of Orders, a Loyalty Program has a collection of Customers and so forth. If we naively fetch all of these, we'll end up loading the whole DB in RAM. This isn't viable of course, so we resort to loading only relevant data from the DB... 1/2Lott
@Kiley And loading only relevant data from the DB seems to be fundamentally at odds with the Rich Domain Model. A rich model of the loyalty program could look like this: class LoyaltyProgram { abstract boolean isCustomerEligible(Customer c) }. But if we want to load only the loyalty programs a customer is eligible to from the DB, we cannot call the isCustomerEligible method as we haven't loaded LoyaltyProgram instances yet - it's a chicken and egg problem... 2/3Lott
@Kiley Contrast with an anemic model which could look like this: class LoyaltyProgram { int minNumberOfYears; decimal minTotalSumOfPurchases; }. To load the loyalty programs a customer is eligible to from the DB, we can perform a query like this: SELECT ... FROM LoyaltyPrograms WHERE minNumberOfYears <= {customerForHowManyYears} and minTotalSumOfPurchases <= {customerTotalSumOfPurchases} (strings in curly brackets are placeholders for actual values calculated for the customer) -- Please note, the code examples are simplified to save space. 3/3Lott
@Nik "If we natively fetch all of these, we'll end up loading the whole DB in RAM." That's one of the main drawbacks of the rich model in my mind as well. The rich model is nice until your domain grows large and complex, and then you start hitting infrastructure limitations. That is where lazy-load ORM's can come in to help, though. Find a good one, and you can retain rich models while not loading the entire DB into memory when you only needed 1/20th of it. That said, I tend to use the Anemic Model with CQRS myself after many years of going back and forth between anemic and rich.Kiley
Another thing to consider is where your business domain logic lives. More and more developers are shifting it out of the database, and to the applications where it belongs in my opinion. But if you are stuck in a situation where your company demands that business logic stay in the database layer (stored procedures), then you almost certainly don't benefit from also adding that logic in a rich domain model. In fact, you could just be setting yourself up to run into conflicts where the stored procedures have different rules than your application's domain layer...Kiley
D
61

Bozhidar Bozhanov seems to argue in favor of the anemic model in this blog post.

Here is the summary he presents:

  • domain objects should not be spring (IoC) managed, they should not have DAOs or anything related to infrastructure injected in them

  • domain objects have the domain objects they depend on set by hibernate (or the persistence mechanism)

  • domain objects perform the business logic, as the core idea of DDD is, but this does not include database queries or CRUD – only operations on the internal state of the object

  • there is rarely need of DTOs – the domain objects are the DTOs themselves in most cases (which saves some boilerplate code)

  • services perform CRUD operations, send emails, coordinate the domain objects, generate reports based on multiple domain objects, execute queries, etc.

  • the service (application) layer isn’t that thin, but doesn’t include business rules that are intrinsic to the domain objects

  • code generation should be avoided. Abstraction, design patterns and DI should be used to overcome the need of code generation, and ultimately – to get rid of code duplication.

UPDATE

I recently read this article where the author advocates of following a sort of hybrid approach - domain objects can answer various questions based solely on their state (which in the case of totally anemic models would probably be done in the service layer)

Demetria answered 26/4, 2014 at 17:33 Comment(3)
I can't extract from that article that Bozho seems to argue in favor of the anemic domain model. the service (application) layer isn’t that thin, but doesn’t include business rules that are intrinsic to the domain objects. What I understand is, domain objects should contain the business logic that are intrinsic to them, but they should not contain any other infrastructure logic. This approach does not seem like an anemic domain model to me at all.Extragalactic
Also this one: domain objects perform the business logic, as the core idea of DDD is, but this does not include database queries or CRUD – only operations on the internal state of the object. These statements do not seem like to favor the anemic domain model at all. They only state that infrastructure logic should not be coupled to domain objects. At least that is what I understand.Extragalactic
@Extragalactic In my view it seems fairly clear that Bozho advocates a sort of hybrid between the two models, a hybrid which I would say is closer to the anemic model than the rich model.Demetria
T
47

My point of view is this:

Anemic domain model = database tables mapped to objects (only field values, no real behavior)

Rich domain model = a collection of objects that expose behavior

If you want to create a simple CRUD application, maybe an anemic model with a classic MVC framework is enough. But if you want to implement some kind of logic, anemic model means that you will not do object oriented programming.

*Note that object behavior has nothing to do with persistence. A different layer (Data Mappers, Repositories e.t.c.) is responsible for persisting domain objects.

Tuberous answered 13/5, 2014 at 10:55 Comment(3)
Sorry for my ignorance, but how does a rich domain model can follow SOLID principe if you put all the Entity related logic in the class. This violates SOLID principe, The 'S' exactly, that stands for single responsability, that says a class should only do one thing and do it right.Hiltonhilum
@Hiltonhilum It depends on how you define "one thing". Consider a class with two properties and two methods: x, y, sum and difference. That's four things. Or you could argue it's addition and subtraction (two things). Or you could argue that it's math (one thing). There are many blog posts out there about how you find a balance in applying SRP. Here's one: hackernoon.com/…Promotive
In DDD, single-resposibility means a class/model can manage it's own state without causing any side effects to the rest of the system as a whole. Any other definition just results in tedious philosophical debates in my experience.Trailblazer
T
15

When I used to write monolithic desktop apps I built rich domain models, used to enjoy building them.

Now I write tiny HTTP microservices, there's as little code as possible, including anemic DTOs.

I think DDD and this anemic argument date from the monolithic desktop or server app era. I remember that era and I would agree that anemic models are odd. I built a big monolithic FX trading app and there was no model, really, it was horrible.

With microservices, the small services with their rich behaviour, are arguably the composable models and aggregates within a domain. So the microservice implementations themselves may not require further DDD. The microservice application may be the domain.

An orders microservice may have very few functions, expressed as RESTful resources or via SOAP or whatever. The orders microservice code may be extremely simple.

A larger more monolithic single (micro)service, especially one that keeps it model in RAM, may benefit from DDD.

Turnstile answered 16/8, 2018 at 21:50 Comment(2)
Do you have any code examples for HTTP micro-services that represents your current state of the art? Not asking for you to write anything up, just share links if you have anything you could point to. Thanks.Jarrod
Good catch! I agree with this argumentation, it confirms my experience with building applications from small single responsibility components, often hosted in serverless environments and orchestrated by events.Vitality
S
12

First of all, I copy pasted the answer from this article http://msdn.microsoft.com/en-gb/magazine/dn385704.aspx

Figure 1 shows an Anemic Domain Model, which is basically a schema with getters and setters.

Figure 1 Typical Anemic Domain Model Classes Look Like Database Tables

public class Customer : Person
{
  public Customer()
  {
    Orders = new List<Order>();
  }
  public ICollection<Order> Orders { get; set; }
  public string SalesPersonId { get; set; }
  public ShippingAddress ShippingAddress { get; set; }
}
public abstract class Person
{
  public int Id { get; set; }
  public string Title { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string CompanyName { get; set; }
  public string EmailAddress { get; set; }
  public string Phone { get; set; }
}

In this richer model, rather than simply exposing properties to be read and written to, the public surface of Customer is made up of explicit methods.

Figure 2 A Customer Type That’s a Rich Domain Model, Not Simply Properties

public class Customer : Contact
{
  public Customer(string firstName, string lastName, string email)
  {
    FullName = new FullName(firstName, lastName);
    EmailAddress = email;
    Status = CustomerStatus.Silver;
  }
  internal Customer()
  {
  }
  public void UseBillingAddressForShippingAddress()
  {
    ShippingAddress = new Address(
      BillingAddress.Street1, BillingAddress.Street2,
      BillingAddress.City, BillingAddress.Region,
      BillingAddress.Country, BillingAddress.PostalCode);
  }
  public void CreateNewShippingAddress(string street1, string street2,
   string city, string region, string country, string postalCode)
  {
    ShippingAddress = new Address(
      street1,street2,
      city,region,
      country,postalCode)
  }
  public void CreateBillingInformation(string street1,string street2,
   string city,string region,string country, string postalCode,
   string creditcardNumber, string bankName)
  {
    BillingAddress = new Address      (street1,street2, city,region,country,postalCode );
    CreditCard = new CustomerCreditCard (bankName, creditcardNumber );
  }
  public void SetCustomerContactDetails
   (string email, string phone, string companyName)
  {
    EmailAddress = email;
    Phone = phone;
    CompanyName = companyName;
  }
  public string SalesPersonId { get; private set; }
  public CustomerStatus Status { get; private set; }
  public Address ShippingAddress { get; private set; }
  public Address BillingAddress { get; private set; }
  public CustomerCreditCard CreditCard { get; private set; }
}
Steppe answered 30/9, 2014 at 4:27 Comment(2)
There is a problem with methods that both create an object and assign a property with newly created object. They make code less extensible and flexible. 1) What if code consumer wants to create not Address, but ExtendedAddress, inherited from Address, with several additional properties? 2) Or change CustomerCreditCard constructor parameters to take BankID instead of BankName?Scarab
What is creating an address requires additional services than what composes the object? You are left with method injection to get those services. What if it's a lot of services?Kiley
C
11

One of the benefit of rich domain classes is you can call their behaviour (methods) everytime you have the reference to the object in any layer. Also, you tend to write small and distributed methods that collaborate together. In anemic domain classes, you tend to write fat procedural methods (in service layer) that are usually driven by use case. They are usually less maintainable compared to rich domain classes.

An example of domain classes with behaviours:

class Order {

     String number

     List<OrderItem> items

     ItemList bonus

     Delivery delivery

     void addItem(Item item) { // add bonus if necessary }

     ItemList needToDeliver() { // items + bonus }

     void deliver() {
         delivery = new Delivery()
         delivery.items = needToDeliver()
     }

}

Method needToDeliver() will return list of items that need to be delivered including bonus. It can be called inside the class, from another related class, or from another layer. For example, if you pass Order to view, then you can use needToDeliver() of selected Order to display list of items to be confirmed by user before they click on save button to persist the Order.

Responding To Comment

This is how I use the domain class from controller:

def save = {
   Order order = new Order()
   order.addItem(new Item())
   order.addItem(new Item())
   repository.create(order)
}

The creation of Order and its LineItem is in one transaction. If one of the LineItem can't be created, no Order will be created.

I tend to have method that represent a single transaction, such as:

def deliver = {
   Order order = repository.findOrderByNumber('ORDER-1')
   order.deliver()       
   // save order if necessary
}

Anything inside deliver() will be executed as one single transaction. If I need to execute many unrelated methods in a single transaction, I would create a service class.

To avoid lazy loading exception, I use JPA 2.1 named entity graph. For example, in controller for delivery screen, I can create method to load delivery attribute and ignore bonus, such as repository.findOrderByNumberFetchDelivery(). In bonus screen, I call another method that load bonus attribute and ignore delivery, such as repository.findOrderByNumberFetchBonus(). This requires dicipline since I still can't call deliver() inside bonus screen.

Caught answered 29/4, 2014 at 2:58 Comment(7)
How about transaction scope?Alula
Domain model behaviors shouldn't contains persistence logic (including transaction). They should be testable (in unit test) without connected to database. Transaction scope is the responsibility of service layer or persistence layer.Caught
How about lazy-loading then?Alula
When you create domain classes instances in unit test, they're not in managed state because they're plain objects. All behaviors can be tested properly.Caught
And what happens when you're expecting the domain object from the service layer? Isn't it managed then?Alula
Yes, if you use domain object inside a transaction in service layer, lazy loading will work. But if you pass the domain object to another layer outside this transaction, calling behaviour that access unloaded attributes will not work. You may want to avoid lazy loading. Or, by using a flexible object graph to define which nodes to load when querying an object (such as JPA 2.1 named entity graph), you can control which attribute to load.Caught
Please see my updated my answer about transaction scope and lazy-loading.Caught
M
5

I think the root of the problem is in false dichotomy. How is it possible to extract these 2 models: rich and "anemic" and to contrast them to each other? I think it's possible only if you have a wrong ideas about what is a class. I am not sure, but I think I found it in one of Bozhidar Bozhanov videos in Youtube. A class is not a data + methods over this data. It's totally invalid understanding which leads to the division of classes into two categories: data only, so anemic model and data + methods - so rich model (to be more correct there is a 3rd category: methods only even).

The true is that class is a concept in some ontological model, a word, a definition, a term, an idea, it's a DENOTAT. And this understanding eliminates false dichotomy: you can not have ONLY anemic model or ONLY rich model, because it means that your model is not adequate, it's not relevant to the reality: some concepts have data only, some of them have methods only, some of them are mixed. Because we try to describe, in this case, some categories, objects sets, relations, concepts with classes, and as we know, some concepts are processes only (methods), some of them are set of attributes only (data), some of them are relations with attributes (mixed).

I think an adequate application should include all kinds of classes and to avoid to fanatically self-limited to just one model. No matter, how the logic is representing: with code or with interpretable data objects (like Free Monads), anyway: we should have classes (concepts, denotats) representing processes, logic, relations, attributes, features, data, etc. and not to try to avoid some of them or to reduce all of them to the one kind only.

So, we can extract logic to another class and to leave data in the original one, but it has not sense because some concept can include attributes and relations/processes/methods and a separating of them will duplicate the concept under 2 names which can be reduced to patterns: "OBJECT-Attributes" and "OBJECT-Logic". It's fine in procedural and functional languages because of their limitation but it's excessive self-restraint for a language that allows you to describe all kinds of concepts.

Megalocardia answered 4/11, 2019 at 9:10 Comment(0)
S
2

Anemic domain models are important for ORM and easy transfer over networks (the life-blood of all comercial applications) but OO is very important for encapsulation and simplifying the 'transactional/handling' parts of your code.

Therefore what is important is being able to identify and convert from one world to the other.

Name Anemic models something like AnemicUser, or UserDAO etc so developers know there is a better class to use, then have an appropriate constructor for the none Anemic class

User(AnemicUser au)

and adapter method to create the anemic class for transporting/persistence

User::ToAnemicUser() 

Aim to use the none Anemic User everywhere outside of transport/persistence

Suzisuzie answered 5/4, 2018 at 8:58 Comment(0)
L
0

The classical approach to DDD doesn't state to avoid Anemic vs Rich Models at all costs. However, MDA can still apply all DDD concepts (bounded contexts, context maps, value objects, etc.) but use Anemic vs Rich models in all cases. There are many cases where using Domain Services to orchestrate complex Domain Use Cases across a set of domain aggregates as being a much better approach than just aggregates being invoked from application layer. The only difference from the classical DDD approach is where does all validations and business rules reside? There’s a new construct know as model validators. Validators ensure the integrity of the full input model prior to any use case or domain workflow takes place. The aggregate root and children entities are anemic but each can have their own model validators invoked as necessary, by it’s root validator. Validators still adhered to SRP, are easy to maintain and are unit testable.

The reason for this shift is we’re now moving more towards an API first vs an UX first approach to Microservices. REST has played a very important part in this. The traditional API approach (because of SOAP) was initially fixated on a command based API vs. HTTP verbs (POST, PUT, PATCH, GET and DELETE). A command based API fits well with the Rich Model object oriented approach and is still very much valid. However, simple CRUD based APIs, although they can fit within a Rich Model, is much better suited with simple anemic models, validators and Domain Services to orchestrate the rest.

I love DDD in all that it has to offer but there comes a time you need stretch it a bit to fit constantly changing and better approach to architecture.

Longdrawnout answered 2/7, 2019 at 15:28 Comment(0)
F
-2

Here is a example that might help:

Anemic

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

Non-anemic

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

    public Box(int height, int width)
    {
        if (height <= 0) {
            throw new ArgumentOutOfRangeException(nameof(height));
        }
        if (width <= 0) {
            throw new ArgumentOutOfRangeException(nameof(width));
        }
        Height = height;
        Width = width;
    }

    public int area()
    {
       return Height * Width;
    }
}
Franckot answered 6/8, 2017 at 7:20 Comment(4)
This looks like it can be converted to a ValueObject vs an Entity.Longdrawnout
Just a copy paste from Wikipedia without any explanationGinder
who wrote sooner? @GinderFranckot
@AlirezaRahmaniKhalili according to Wikipedia history, they have been first... Unless, I didn't understand your question.Ginder

© 2022 - 2024 — McMap. All rights reserved.