Home > Architecture, Uncategorized > Anemic Domain Model Anti-Pattern

Anemic Domain Model Anti-Pattern

April 24th, 2009

We have built a brown field project, which is to say that it has some ugly architectural and coding attributes.

There are 2 mains issues with it.

  1. Low code coverage (unit tests)
  2. In many cases we implemented the Anemic Domain Model Anti-Pattern (see Fowler’s blog)

The issue, in part, is that LINQ psychologically pushed us in that direction (I think – or maybe I’m just making excuses).

This is an example, but we mapped out an architecture a few years ago that was along the lines of this simple tiered structure:

 image

** Note the use of “tbl” for tables & Entity objects is merely to reinforce their connection to the DB due to the 1 table –> 1 object mapping of LINQ. (I don’t use “tbl” in my tables)

All seemed well in the world. We happily went on our way creating partial classes for our tables.

First User Story: Determine the Order cost.

The solution seemed easy enough, loop through the order lines, add up each order line cost, and take the sum.

public Currency Cost( ){

    return this.tblOrderLines.Sum( ol => ol.Cost );

}

But then came the question of where to put this. Obviously OOP would push us to put .Cost() on the tblOrder object. But we faced two problems:

1) By naming the persistence layer as DAL Layer, psychologically we were averse to putting too much business logic there.

Perhaps the DAL layer *is* the Domain Object layer and by merely renaming the DAL Layer as Domain Layer, we would have felt better about it.

2) The second was that the tblOrder object didn’t have enough information to determine the total cost of the order. We had to add taxes, and the amount of taxes changed depending on the state and the type of the order_line.

The Order really needed access to DataContext in order to grab additional information from a tblTaxState table. This is were we made the mistake of putting the COST() into the OrderService

public Currency Cost(tblOrder ord, DBcontext db){

       tblTaxState st = db.tblTaxStates.Where(

                                 txSt => txSt.State == ord.Patient.State

                               ).First();

       Currency cost = new Currency();

        ForEach( var ol in ord.tblOrderLines ){

                    cost += ol.Cost + txSt.ApplyTax( ol );

        }

       return cost;

}

There are a number of ways we could have better solved this. We could have passed the tblTaxState to the tblOrder.Cost() method.

  tblTaxState st = db.tblTaxStates.Where(

                                 txSt => txSt.State == ord.Patient.State

                               ).First();

Currency cost = ord.Cost( st);

Or perhaps even better, would have been to merely pass the DBContext to the tblOrder.Cost() method

Currency cost = ord.Cost(db);

My question is whether that passing around of the DB Context to Domain / Entity objects *SMELLS*.

alan.huffman Architecture, Uncategorized , , ,

  1. No comments yet.
  1. No trackbacks yet.