views:

242

answers:

3

I am reading Hibernate in Action and the author suggests to move business logic into our domain models (p. 306). For instance, in the example presented by the book, we have three entities named Item, Bid, and User and the author suggests to add a placeBid(User bidder, BigDecimal amount) method to the Item class.

Considering that usually we have a distinct layer for business logic (e.g. Manager or Service classes in Spring) that among other things control transactions, etc. is this really a good advice? Isn't it better not to add business logic methods to our entities?

Thanks in advance.

+4  A: 

As said

We have a distinct layer for business logic (usually called Service layer)

Domain-Driven-Design (DDD) states you should put business logic inside your domain model. And, believe me, it is really good. As said by POJO in Action book about Service layer

  • It is Use Case driven
  • It can define Transaction boundaries

Before

@Service
public class BidServiceImpl implements BidService {

    @Autowired
    private ItemRepository itemRepository;

    public void placeBid(Integer itemId, User bidder, BigDecimal amount) {

        Item item = itemRepository.getById(itemId);

        if(amount.compareTo(new BigDecimal("0.00")) <= 0)
            throw new IllegalStateException("Amount must be greater than zero");

        if(!bidder.isEnabled())
            throw new IllegalStateException("Disabled bidder");

        item.getBidList().add(new Bid(bidder, amount));
    }

}

After

@Service
public class BidServiceImpl implements BidService {

    @Autowired
    private ItemRepository itemRepository;

    public void placeBid(Integer itemId, User bidder, BigDecimal amount) {
        // itemRepository will retrieve a managed Item instance
        Item item = itemRepository.getById(itemId);

        item.placeBid(bidder, amount);
    }

}

Your domain logic is show as follows

@Entity
public class Item implements Serializable {

    private List<Bid> bidList = new ArrayList<Bid>();

    @OneToMany(cascade=CascadeType.ALL)
    public List<Bid> getBidList() {
        return this.bidList;
    }

    public void placeBid(User bidder, BigDecimal amount) {

        if(amount.compareTo(new BigDecimal("0.00")) <= 0)
            throw new IllegalStateException("Amount must be greater than zero");

        if(!bidder.isEnabled())
            throw new IllegalStateException("Disabled bidder");

        /** 
          * By using Automatic Dirty Checking
          * 
          * Hibernate will save our Bid
          */
        item.getBidList().add(new Bid(bidder, amount));
     }

}

When using Domain-Driven-Design, your business logic lives in the right place. But, sometimes, it could be a good idea to define your business logic inside your Service layer. See here why

Arthur Ronald F D Garcia
Why would the Item know about the rules to place bids on it? I would put that in a different module that handle the rules, and abstract it from the process of placing a bid. I would'nt like to change the Item class each time we evolve the rules. But I would still call those rule inside the Item.placeBid(...) method.
Stephane
(+1) indeed. :)
Bozho
Interesting. When I look at that code, it still seems kind of backwards to me. I'll have to read up on DDD and see what I think. I feel like that last link provided about the reasons to "sometimes" use a service layer hits on a lot of the points that I like using them, though, so maybe I'm not completely crazy.
Jon Quarfoth
Just by looking at the above Before/After example and my experience that most non trivial projects reside in the "sometimes" category, I still feel that it's better to leave Entity objects clean of biz logic code. Haven't practiced DDD so far though.
Bytecode Ninja
@Stephane: an item knows about how bits are made on it because *that is what an item is for*! It's a fundamental part of the nature of an item under the object-oriented worldview.
Tom Anderson
@Tom Anderson I think the Item can know that there is a validation to be done, but I would extract the details of that validation somewhere else. I just don't like to list all the rules right there. But maybe that's ok. I don't know much about DDD, it's just my sense of writing code speaking here. I'm open to learn more :)
Stephane
+2  A: 

One of the most quoted articles on this is:

"The Anemic Domain Model" by Martin Fowler. Well worth reading: http://martinfowler.com/bliki/AnemicDomainModel.html

The general gist is if your domain model is purely data with no behaviour then you have lost many of the benefits of OO design.

or to quote:

"In general, the more behavior you find in the services, the more likely you are to be robbing yourself of the benefits of a domain model. If all your logic is in services, you've robbed yourself blind."

Pablojim
A: 

Personally I love the anemic model -- data is data, code is code; but there are exceptions.

It comes down to 'density': If you have a great number of services which interact with a few domain objects; it makes sense to put some of the common business logic in your domain model, it thus becomes part of the service. If you have a few services which interact with lots of domain objects then favor the anemic model over the rich domain objects.

I have found that if I use my domain objects in multiple contexts (e.g. I use the same domain objects on the client side and on the service side) that business logic often gets in the way -- since it must be applicable in all contexts.

Justin