Concrete examples on why the 'Anemic Domain Model' is considered an anti-pattern [closed]
Asked Answered
C

6

30

I apologize if this is a duplicate, but I couldn't find any concrete examples on the topic in related questions.

After reading Martin Fowler's article on the 'Anemic Domain Model', I'm left wandering as to why is this considered an anti-pattern. Even does the majority of enterprise developers consider it an anti-pattern, since AFAIK probably 90% of the j2ee applications are designed in an 'anemic' way ?

Can someone recommend further reading on the topic (other than the 'Domain Driven Design' book), or even better, give a concrete examples on how this anti-pattern is affecting application design in a bad way.

Thanks,

Creepie answered 9/6, 2011 at 14:0 Comment(2)
I saw this very interesting post by a fellow SO user, which is worth a read: techblog.bozho.net/?p=180.Rarely
@Rarely I have read that and its indeed interesting, its one of the reasons I'm asking this question. Thanks.Creepie
T
16

Given the following two classes:

class CalculatorBean  
{  
    //getters and setters  
}  

class CalculatorBeanService  
{  
   Number calculate(Number first, Number second);  
    {  
       //do calculation  
    }  
} 

If I understand correctly, Fowler is stating that because your CalculatorBean is just a bunch of getters/setters you don't gain any real value from it and if you port that object to another system it will do nothing. The problem seems that your CalculatorBeanService contains everything that the CalculatorBean should be responsible for. Which is not the best as now the CalculatorBean delegates all of its responsibility to the CalculatorBeanService

Terza answered 9/6, 2011 at 14:10 Comment(25)
+1 that is exactly Martins argument, it gets even more valuabe when you write a Singel User Desktop Application (and not a DB driven enterprise application)Philipphilipa
yes this seems very understandable. However doesn't the idea of having logic and data in a single class violate the separation-of-concerns pricipal ? en.wikipedia.org/wiki/Separation_of_concernsCreepie
@Creepie not in this scenario. Because the object knows about its internal pieces it should be able to manipulate them as it sees fit. For instance there would not be much value in a wallet if you could not access it to retrieve the money that you put inside of it. That is to say that the wallet is a "part" of you and you should be able to manipulate it as you see fit. However, I should not be able to manipulate your wallet.Terza
@Simeon: Not really. Separation of concerns means an object is responsible for just one thing. Models should still be designed this way. Its just that 'anemic domain models' are responsible for nothing at all.Sasnett
Not at all, if the logic is concerned with the data itself.Scots
@kevin They are responsible for holding data and O/R mappings.Creepie
with anemic models it should be possible to move my data + ORM + DB to a different system and have completely different service logic operate over it.Creepie
Let me try to give another example. If I have a service that recommends user names when a user name is taken, in which domain object should this logic go given a 'Rich Domain' and a User domain object?Creepie
@Simeon: Obviously there's some amount of judgement and art involved. The point is ultimately to have code that is easy to read and maintain. To give you a counterpoint: I have a model object called "Job" that happens to be persisted to some store. When I get it out of the store it should be possible to run it. Should that be in a service object or should job simply expose a method run()? I think it's more natural to say job.run() than it is to say jobRunnerService.runJob(job).Sasnett
@Sasnett agreed completely. So would you always choose a Rich over an Anemic domain or some mixture of both or in some systems a mixture and in others only one type ?Creepie
@Creepie in your new example. Given that the User object should not be able to see and/or manipulate the fields of other User objects there would need to be some interaction happening with the database. Now in terms of what generates the "new" username that would fall on the code that determines if a username already exists; in my explanation lets say on the database code. The reason for that is that the database "knows" what is and is not availableTerza
@Terza by database code you mean DAOs maybe ? Or stored procedures. I think I'd disagree in both cases. Maybe because of my current mindset but the logic that generates new user names depends on nothing. The logic that finds out whether a username is taken depends on the DB. This is why my current 'feel' is that the logic that generates usernames should be in a service not in a domain object.Creepie
@Creepie sub "database" with "my type here" so yes you could have the DAO do it or whatever object in your application knows about usernames.Terza
@Terza IMHO a DAOs job is only to put/get code from/to a DB. Also see my previous comment. Doing anything else would voilate the Separation of Concerns.Creepie
@Creepie Ok I imagine this well help a bit. Person attempts to create a new username --> UI --> UserValidation --> DAO --> "Select count(username) from userNames table" --> return count to UserValidation --> if count > 0 --> Generate new names (in UserValidation) --> return message to UI "Username exists"Terza
Is that similar to the process?Terza
If UserValidation is a service yes.Creepie
@Creepie Another question comes to mind, does your User domain object allow both get and set operations?Terza
@Terza For the sake of the example lets say no, I'd want it to be immutable so only get operations. I gave a fictional example with the User object by the way.Creepie
@Creepie that is fine, just nice to have a frame of reference. So in the scenario that the User object cannot set it's name you would want a name generation service to create a list of suggested usernames. Which could be in your UserValidation service. In the event that you would allow the User object to set it's own properties the create suggested usernames would be a part of it, assuming that the User object can't persist itself.Terza
@Simeon: Yes I'd do a mixture, with a strong preference towards "Rich domain objects". To take your example, it makes sense to have a separate object for recommending alternate user names. By way of analogy: I go to the ticket counter and say I want first row seats A and B. Is it possible for me (User) to provide alternates if those seats are already taken? Not really, the person behind the counter (some recomendation service) will say, "those are taken but you can have row 3 seats F and G", or row 4 seats A and B". So the real world suggests those should be different objects. Just my take.Sasnett
@kevin exactly :) So if there are cases where an Anemic model is preferred this can not be an anti-pattern IMHO. I'm also leaning towards a mixture. I'm concerned however that a mixture might make setting concrete coding conventions in a project extremely hard, if not impossible.Creepie
@Creepie In big-O it isn't the best case scenario that counts its the worst. While it isn't the same as a pattern perse it does show how something that is good in one place is bad in another.Terza
@Simeon: I think (my opinion only of course) that what Fowler is railing against is when people follow the 'anemic domain model' pattern across the board, either because they don't know better or as a kind of excuse for not thinking about a good design.Sasnett
@kevin agreed, however to call something an anti-pattern would mean that doing it is always wrong, correct ? So if we agree that in some cases the anemic model is the right way to go then it can't be an anti-pattern IMHO.Creepie
M
71

Martin Fowler brings this industry many words and less understanding.

Majority of applications today (web/db) do need many objects that expose their properties.

Any authority (self claimed) frowning upon such practice should lead by example, and show us a successful real world application that's full of embodiments of his marvelous principles.

Or else shut up. It is sickening that there so many hot airs in our industry. This is engineering, not a drama club.

Macready answered 9/6, 2011 at 15:24 Comment(11)
I'd give this +1000 voteups if I could. This is exactly my view. If you criticize something give examples :) This is also exactly the reason why I'm asking the question.Creepie
This should probably be a commentTerza
@Terza an answer stating, there are no examples is a valid opinion IMO.Creepie
@Creepie I was purely commenting that the answer would have been better served as a comment because it adds nothing to the discussion beyond someone's opinion.Terza
@irreputable, if you treat it liek you do, then it is not engineering but mere craftmansship or even only work.Philipphilipa
Boom! Now that's an answer I love. I've spent a lot of time researching allegedly all-encompassing architectural concepts. The potential advantages are typically obvious and clear. What I'm really hoping to learn is "what are the disadvantages"? With that information I could weigh whether or not the solution is a good choice for my situation. In this case, "Anemic Domain Model" is simply a pejorative for a valid option with disadvantages that should be considered.Unger
I wish I could upvote this answer twiceBabettebabeuf
This should definitely be a comment and not an answer. It's just opinion.Logicize
@irreputable, I totally agree with your observation. Martin Fowler has confused me more than teach me something worthy. If I could I would give you +1000 upvotes for your answer.League
I wish I could vote this up 1000+ times. I tried to follow the DDD version of a Rich Domain model and it works for very simple cases. However, here's just one example of where having a Rich Domain Model vs a Anemic one breaks down. Say I have a Person Entity, I now need to establish obtain a Passport. Does a Person know how to obtain a Passport? No. It only knows what info (getters) it needs to expose and call upon a Domain Service that takes a Person Entity to obtain and establish a Passport. Domain Services are king not a entities that are only half-baked.Magnifico
Martin Fowler gave you an idea. It's your responsability to iterate on it and find the best way to implement it. Probably it doesn't fit for you or probably you were just trying to adopt it in a project that was too mature to be changed. A good DD design should be well planned since the first entities of your application and it's hard to just say: "from today Team we write DDD", it just doesn't work. I see benefits there but it required me some practice and discipline. And i agree this should be a comment and not an answer.Closegrained
M
65

For the complete answer take a look at my blog that also contains source code examples [blog]: https://www.link-intersystems.com/blog/2011/10/01/anemic-vs-rich-domain-models/

If you look at the anemic domain model from an object oriented perspective it is definitely an anti-pattern because it is pure procedural programming. The reason why it is called an anti-pattern is that the main object oriented principle is not covered by an anemic domain model:

Object oriented means that: an object manages its state and guarantees that it is in a legal state at any time. (data hiding, encapsulation)

Therefore an object encapsulates data and manages the access and interpretation of it. In contrast to this an anemic model does not gurantee that it is in a legal state at any time.

An example of an order with order items will help to show the difference. So let's take a look at an anemic model of an order.

An anemic model

 public class Order {
    private BigDecimal total = BigDecimal.ZERO;
    private List<OrderItem> items = new ArrayList<OrderItem>();

    public BigDecimal getTotal() {
        return total;
    }

    public void setTotal(BigDecimal total) {
        this.total = total;
    }

    public List<OrderItem> getItems() {
        return items;
    }

    public void setItems(List<OrderItem> items) {
        this.items = items;
    }
}

public class OrderItem {

    private BigDecimal price = BigDecimal.ZERO;
    private int quantity;
    private String name;
    
    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }
}

So where is the logic located that interprets the order and order items to calculate an order total? This logic is often placed in classes named *Helper, *Util, *Manager or simply *Service. An order service in an anemic model would look like this:

public class OrderService {
    public void calculateTotal(Order order) {
        if (order == null) {
             throw new IllegalArgumentException("order must not be null");
        }

        BigDecimal total = BigDecimal.ZERO;
        List<OrderItem> items = order.getItems();

        for (OrderItem orderItem : items) {
            int quantity = orderItem.getQuantity();
            BigDecimal price = orderItem.getPrice();
            BigDecimal itemTotal = price.multiply(new BigDecimal(quantity));
            total = total.add(itemTotal);
        }
        order.setTotal(total);
    }
}

In an anemic model you invoke a method and pass it the anemic model to bring the anemic model to a legal state. Therefore the anemic model's state management is placed outside the anemic model and this fact makes it an anti-pattern from an object oriented perspective.

Sometimes you will see a slightly different service implementation that does not modify the anemic model. Instead it returns the value it calculates. E.g.

public BigDecimal calculateTotal(Order order); 

In this case the Order doesn't have a property total. If you now make the Order immutable you are on the way to functional programming. But this is another topic that I can't discover here.

The problems with the anemic order model above are:

  • If someone adds an OrderItem to the Order the Order.getTotal() value is incorrect as long as it has not been recalculated by the OrderService. In a real world application it can be cumbersome to find out who added the order item and why the OrderService has not been called. As you might have recognized already the Order also breaks encapsulation of the order items list. Someone can call order.getItems().add(orderItem) to add an order item. That can make it difficult to find the code that really adds the item (order.getItems() reference can be passed through the whole application).
  • The OrderService's calculateTotal method is responsible for calculating the total for all Order objects. Therefore it must be stateless. But stateless also means that it can not cache the total value and only recalculate it if the Order object changed. So if the calculateTotal method takes a long time you also have a performance issue. Nevertheless you will have performance issues, because clients might not know if the Order is in a legal state or not and therefore preventatively call calculateTotal(..) even when it is not needed.

You will also see sometimes that services do not update the anemic model and instead just return the result. E.g.

public class OrderService {
    public BigDecimal calculateTotal(Order order) {
        if (order == null) {
             throw new IllegalArgumentException("order must not be null");
        }

        BigDecimal total = BigDecimal.ZERO;
        List<OrderItem> items = order.getItems();

        for (OrderItem orderItem : items) {
            int quantity = orderItem.getQuantity();
            BigDecimal price = orderItem.getPrice();
            BigDecimal itemTotal = price.multiply(new BigDecimal(quantity));
            total = total.add(itemTotal);
        }
       return total;
    }
}

In this cases the services interpret the state of the anemic model at some time and do not update the anemic model with the result. The only benefit of this approach is that the anemic model can not contain an invalid total state, because it won't have a total property. But this also means that the total must be calculated every time it is needed. By removing the total property you lead developers to use the service and to not to rely on the total's property state. But this will not guarantee that the developers cache the total value in some way and thus they might also use values that are outdated. This way of implementing a service can be done whenever a property is derived form another property. Or in other words... when you interpret basic data. E.g. int getAge(Date birthday).

Now take a look at the rich domain model to see the difference.

The rich domain approach

public class Order {

    private BigDecimal total;
    private List<OrderItem> items = new ArrayList<OrderItem>();

    /**
      * The total is defined as the sum of all {@link OrderItem#getTotal()}.
      *
      * @return the total of this {@link Order}.
      */
    public BigDecimal getTotal() {
        if (total == null) {
           /*
            * we have to calculate the total and remember the result
            */
           BigDecimal orderItemTotal = BigDecimal.ZERO;
           List<OrderItem> items = getItems();

           for (OrderItem orderItem : items) {
               BigDecimal itemTotal = orderItem.getTotal();
               /*
                * add the total of an OrderItem to our total.
                */
               orderItemTotal = orderItemTotal.add(itemTotal);
           }

           this.total = orderItemTotal;
           }
        return total;
        }

   /**
    * Adds the {@link OrderItem} to this {@link Order}.
    *
    * @param orderItem
    *            the {@link OrderItem} to add. Must not be null.
    */
    public void addItem(OrderItem orderItem) {
        if (orderItem == null) {
            throw new IllegalArgumentException("orderItem must not be null");
        }
        if (this.items.add(orderItem)) {
           /*
            * the list of order items changed so we reset the total field to
            * let getTotal re-calculate the total.
            */ 
            this.total = null;
        }
    }

    /**
      *
      * @return the {@link OrderItem} that belong to this {@link Order}. Clients
      *         may not modify the returned {@link List}. Use
      *         {@link #addItem(OrderItem)} instead.
      */
    public List<OrderItem> getItems() {
       /*
        * we wrap our items to prevent clients from manipulating our internal
        * state.
        */
        return Collections.unmodifiableList(items);
    }

}

public class OrderItem {

    private BigDecimal price;

    private int quantity;

    private String name = "no name";

    public OrderItem(BigDecimal price, int quantity, String name) {
     if (price == null) {
      throw new IllegalArgumentException("price must not be null");
     }
     if (name == null) {
      throw new IllegalArgumentException("name must not be null");
     }
     if (price.compareTo(BigDecimal.ZERO) < 0) {
      throw new IllegalArgumentException(
        "price must be a positive big decimal");
     }
     if (quantity < 1) {
      throw new IllegalArgumentException("quantity must be 1 or greater");
     }
     this.price = price;
     this.quantity = quantity;
     this.name = name;
    }

    public BigDecimal getPrice() {
     return price;
    }

    public int getQuantity() {
     return quantity;
    }

    public String getName() {
     return name;
    }

    /**
      * The total is defined as the {@link #getPrice()} multiplied with the
      * {@link #getQuantity()}.
      *
      * @return
      */
    public BigDecimal getTotal() {
     int quantity = getQuantity();
      BigDecimal price = getPrice();
      BigDecimal total = price.multiply(new BigDecimal(quantity));
     return total;
    }
}

The rich domain model respects the object oriented principles and gurantees that it is in a legal state at any time.

References

Metalinguistic answered 22/2, 2013 at 9:11 Comment(7)
In case of anemic model, can't you force somehow adding OrderItem's through OrderService?Noonday
@Noonday If you want to force it you first must make the Order's setItems method package scope. Thus you must place it in the same package than the OrderService so that the OrderService can still modify the Order. Nevertheless all other classes in the same package can also modify the Order. So you just reduced the problem area. In this case you try to enforce an object's legal state by convention: "only modify an Order through the OrderService". But how can you be sure that now other class in the same package accesses it and brings it into an illegal state?Lisabeth
You say anemic domain model is procedural, but it can also be done functionally no?Lazy
IMHO this should be marked as the correct answerDrinker
What you call anaemic domain model is procedural programming with data structures and what you call rich domain model is object oriented programming. Either of them can work according to Clean Code. Now if you want to use domain objects (and not domain data structures) then ofc. you need the rich domain model.Boots
I'm trying to understand this question. Can you please explain example from here docs.spring.io/docs/Spring-MVC-step-by-step/part3.html There is a service layer with the class and method to change the state of the model double newPrice = product.getPrice().doubleValue() * (100 + percentage)/100; product.setPrice(newPrice); Is it correct?Endowment
@RenéLink isn't the OrderItem 'Anemic Domain Model'?Hindi
S
19

Well. You're right that almost all java code is written this way. The reason it's an anti pattern is that one of the main principles of object oriented design is to combine data and the functions that operate on it into a single object. For example when I was writing old school c code, we would mimic object oriented design like this:

struct SomeStruct {
    int x;
    float y;
};

void some_op_i(SomeStruct* s, int x) {
    // do something
}
void some_op_f(SomeStruct* s, float y) {
    // something else
}

Which is to say that the language didn't allow us to combine the functions to operate on SomeStruct inside of the struct, so we created a group of free functions that by convention took SomeStruct as a first param.

When c++ came along, the struct became a class, and it allows you to put functions into the struct (class). Then the struct is implicitly passed as the this pointer, so instead of creating a struct and passing it to functions, you create the class and call methods against it. The code is more clear and easier to understand this way.

Then I moved to the java world, and everyone separates the model from the service, which is to say the model is a glorified struct, and the service, being stateless as it is, becomes a collection of functions that operates on a model. Which to me, sounds suspiciously like a c language idiom. It's pretty funny because in c it was done because the language didn't offer anything better, and in java it's done because the programmers don't know any better.

Sasnett answered 9/6, 2011 at 14:22 Comment(3)
interesting comparison :) I'll think about this a little bit.Creepie
I don't think this is an accurate answer. You can have data structures in an OO code too. There is nothing wrong with that, for example a DTO like domain event or command is a data structure as well. By definition domain objects like entities, value objects, etc... have behavior, so they should not be data structures... But I am still looking for that definition :DBoots
You're trying to conflate the OO principle. I can have a Domain Service object that is completely OO with inheritance, polymorphism, etc. The methods can take an Entity model and work on as a regular data object. For example, I have a Domain Service that obtains a Passport for a given Person Entity. All I need is information from the Person Entity (getters), Is a Person Entity responsible for knowing the administrative gov't processes of creating a Passport? No. Another gov't Service is, I mean another Domain Service is. There's a new Domain Service called Model Validators.Magnifico
T
16

Given the following two classes:

class CalculatorBean  
{  
    //getters and setters  
}  

class CalculatorBeanService  
{  
   Number calculate(Number first, Number second);  
    {  
       //do calculation  
    }  
} 

If I understand correctly, Fowler is stating that because your CalculatorBean is just a bunch of getters/setters you don't gain any real value from it and if you port that object to another system it will do nothing. The problem seems that your CalculatorBeanService contains everything that the CalculatorBean should be responsible for. Which is not the best as now the CalculatorBean delegates all of its responsibility to the CalculatorBeanService

Terza answered 9/6, 2011 at 14:10 Comment(25)
+1 that is exactly Martins argument, it gets even more valuabe when you write a Singel User Desktop Application (and not a DB driven enterprise application)Philipphilipa
yes this seems very understandable. However doesn't the idea of having logic and data in a single class violate the separation-of-concerns pricipal ? en.wikipedia.org/wiki/Separation_of_concernsCreepie
@Creepie not in this scenario. Because the object knows about its internal pieces it should be able to manipulate them as it sees fit. For instance there would not be much value in a wallet if you could not access it to retrieve the money that you put inside of it. That is to say that the wallet is a "part" of you and you should be able to manipulate it as you see fit. However, I should not be able to manipulate your wallet.Terza
@Simeon: Not really. Separation of concerns means an object is responsible for just one thing. Models should still be designed this way. Its just that 'anemic domain models' are responsible for nothing at all.Sasnett
Not at all, if the logic is concerned with the data itself.Scots
@kevin They are responsible for holding data and O/R mappings.Creepie
with anemic models it should be possible to move my data + ORM + DB to a different system and have completely different service logic operate over it.Creepie
Let me try to give another example. If I have a service that recommends user names when a user name is taken, in which domain object should this logic go given a 'Rich Domain' and a User domain object?Creepie
@Simeon: Obviously there's some amount of judgement and art involved. The point is ultimately to have code that is easy to read and maintain. To give you a counterpoint: I have a model object called "Job" that happens to be persisted to some store. When I get it out of the store it should be possible to run it. Should that be in a service object or should job simply expose a method run()? I think it's more natural to say job.run() than it is to say jobRunnerService.runJob(job).Sasnett
@Sasnett agreed completely. So would you always choose a Rich over an Anemic domain or some mixture of both or in some systems a mixture and in others only one type ?Creepie
@Creepie in your new example. Given that the User object should not be able to see and/or manipulate the fields of other User objects there would need to be some interaction happening with the database. Now in terms of what generates the "new" username that would fall on the code that determines if a username already exists; in my explanation lets say on the database code. The reason for that is that the database "knows" what is and is not availableTerza
@Terza by database code you mean DAOs maybe ? Or stored procedures. I think I'd disagree in both cases. Maybe because of my current mindset but the logic that generates new user names depends on nothing. The logic that finds out whether a username is taken depends on the DB. This is why my current 'feel' is that the logic that generates usernames should be in a service not in a domain object.Creepie
@Creepie sub "database" with "my type here" so yes you could have the DAO do it or whatever object in your application knows about usernames.Terza
@Terza IMHO a DAOs job is only to put/get code from/to a DB. Also see my previous comment. Doing anything else would voilate the Separation of Concerns.Creepie
@Creepie Ok I imagine this well help a bit. Person attempts to create a new username --> UI --> UserValidation --> DAO --> "Select count(username) from userNames table" --> return count to UserValidation --> if count > 0 --> Generate new names (in UserValidation) --> return message to UI "Username exists"Terza
Is that similar to the process?Terza
If UserValidation is a service yes.Creepie
@Creepie Another question comes to mind, does your User domain object allow both get and set operations?Terza
@Terza For the sake of the example lets say no, I'd want it to be immutable so only get operations. I gave a fictional example with the User object by the way.Creepie
@Creepie that is fine, just nice to have a frame of reference. So in the scenario that the User object cannot set it's name you would want a name generation service to create a list of suggested usernames. Which could be in your UserValidation service. In the event that you would allow the User object to set it's own properties the create suggested usernames would be a part of it, assuming that the User object can't persist itself.Terza
@Simeon: Yes I'd do a mixture, with a strong preference towards "Rich domain objects". To take your example, it makes sense to have a separate object for recommending alternate user names. By way of analogy: I go to the ticket counter and say I want first row seats A and B. Is it possible for me (User) to provide alternates if those seats are already taken? Not really, the person behind the counter (some recomendation service) will say, "those are taken but you can have row 3 seats F and G", or row 4 seats A and B". So the real world suggests those should be different objects. Just my take.Sasnett
@kevin exactly :) So if there are cases where an Anemic model is preferred this can not be an anti-pattern IMHO. I'm also leaning towards a mixture. I'm concerned however that a mixture might make setting concrete coding conventions in a project extremely hard, if not impossible.Creepie
@Creepie In big-O it isn't the best case scenario that counts its the worst. While it isn't the same as a pattern perse it does show how something that is good in one place is bad in another.Terza
@Simeon: I think (my opinion only of course) that what Fowler is railing against is when people follow the 'anemic domain model' pattern across the board, either because they don't know better or as a kind of excuse for not thinking about a good design.Sasnett
@kevin agreed, however to call something an anti-pattern would mean that doing it is always wrong, correct ? So if we agree that in some cases the anemic model is the right way to go then it can't be an anti-pattern IMHO.Creepie
I
8

It simply violates the “Tell, Don’t Ask” principle which states that objects should tell the client what they can or cannot do rather than exposing properties and leaving it up to the client to determine if an object is in a particular state for a given action to take place.

Idiopathy answered 16/1, 2017 at 22:57 Comment(0)
F
6

As with most things in the software development world there is not black and white. There are cases where an anemic domain model is the perfect fit.

BUT there are a lot of cases where developers try to build a domain model, aka do DDD, and end up with an anemic domain mode instead. I think in this case the anemic domain model is considered an anti-patern.

Just make sure you use the best tool for the job and if it works for you don't bother changing it.

Farr answered 10/6, 2011 at 7:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.