34

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.

Behrang
  • 46,888
  • 25
  • 118
  • 160

3 Answers3

24

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

Kwadz
  • 2,206
  • 2
  • 24
  • 45
Arthur Ronald
  • 33,349
  • 20
  • 110
  • 136
  • 6
    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. – Stéphane Apr 08 '10 at 07:41
  • 1
    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 Apr 08 '10 at 13:43
  • 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. – Behrang Apr 10 '10 at 11:47
  • 2
    @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 Jul 18 '10 at 18:30
  • 1
    @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 :) – Stéphane Jul 18 '10 at 21:25
  • Business logic that is specific to an entity (or aggregate root) belongs in that entity, and it's perfectly fine if it's a JPA mapped `@Entity`. But if the business logic spans multiple entities (or aggregates), then it should be inside a domain model element called Domain Service (in DDD terms). – Paulo Merson Feb 17 '19 at 01:09
14

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
  • 8,542
  • 8
  • 45
  • 69
  • do you like triggers on your tables? as i would say this comparable, im not a fan of triggers, rather the process which is responsible for the interaction in the first place should determine how and when the data changes. He talks about anti pattern, this is fantastic, i feel this is just talk without actual examples. I'm not talking about entity validation! In Sql we dont define function on the table, this is implying that would be a gd idea. Examples needed. – Seabizkit Apr 24 '19 at 09:49
0

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
  • 4,437
  • 6
  • 32
  • 52
  • 3
    So you don't like OOP, but procedural code. Hope you are not writing your business logic on Singleton components… – lnrdo Apr 06 '15 at 12:34
  • 2
    Don't forget about functional programming and immutable data... Yes, it's not OOP, but not everybody HAS to like OOP and not everybody who don't like OOP HAS to like procedural programming. – Dalibor Filus May 31 '18 at 11:42