2

When using an Object Relational Mapper, how do I create new entities inside another entity and how can I persist them? I am using Doctrine 2 (PHP) but I assume this applies equally well to Hibernate (Java) and NHibernate (C#) as well.

For example, O have an Order entity that has a setCompleted() method. My business logic dictates that whenever an order is completed, a new Product entity is created. Note that Order and Product are currently not related (should they be?). To me, the most logical place to put that business logic is inside the setCompleted() method. But how do I tell the ORM that there's a new entity to persist? The entity manager isn't available inside the entity.

Or am I approaching this problem in the wrong way and should I implement it some other way?

Sander Marechal
  • 22,978
  • 13
  • 65
  • 96

3 Answers3

1

You Order class needs to have a reference to a Product, that could be null. When you call setCompleted you can create a new Product instance and assign it. Something like

public void setCompleted(){
 ... 
 this.product = new Product(...);
 ...
}

then depending on your mapping (check your ORM documentation for this) you will have to call the entiity manager save method on both order and product, or just order (if the relation owner is Order and cascade is enabled).

As this is business logic, I would not hide it in the model itself but rather have in the service layer.

mericano1
  • 2,914
  • 1
  • 18
  • 25
  • Could you give an example on how this would work when using the service layer? – Sander Marechal Dec 08 '11 at 10:29
  • business logic can(should) go on the domain objects, http://martinfowler.com/bliki/AnemicDomainModel.html – NimChimpsky Dec 08 '11 at 10:33
  • its not "hidden in the model itself" at all. Its the opposite, its available everywhere the domain object is and encapsulated so that no other knowledge is required. – NimChimpsky Dec 08 '11 at 10:43
  • I see your point, it's really a matter of preference. Some more reasing here http://stackoverflow.com/questions/110328/how-much-business-logic-should-value-objects-contain When using ORMs, I never put business logic in domain objects cause I want to have all the code creating/deleting objects/relations in the same place... – mericano1 Dec 08 '11 at 10:51
  • Your link points to DTO's/value objects, not domain objects. I think there is a generally accepted best practice, and that is sticking behaviour on domain objects keeping everything as object oriented as possible. – NimChimpsky Dec 08 '11 at 11:06
  • You are right, the link is only talking about value objects. Look at this one instead http://java.dzone.com/articles/business-logic-domain-objects. I think the main factor here is the interactions with other objects. If the logic only changes the internal state there is no doubt it should go in the bean itself. If the method is doing more than that ( from that link above, Book.sell() does not make any sense while BookServiceImpl.sell(book) does) then separation of concerns should be considered. – mericano1 Dec 08 '11 at 12:48
  • In case the creating of product depends on certain criteria 'this.product = new Product(...);' would not work and factories should be used in general anyway. So either we inject the factory into Entity (depends on context, but it can make the entity bloated) or into a dedicated OrderService (preferable). – Tomas Dermisek Dec 13 '11 at 05:04
1

I would have a new Product created as part of the setCompleted method. So that everytime Product is persisted any mapped entities also get persisted. Just let hibernate do the leg work ... do not explicitly persist it yrself using the service layer.

For example within Order (assuming product is declared properly as a entity):

@OneToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name="product_fk",nullable = false) 
private Product product;

public void setCompleted(){
 setProduct(new Product(...));
}

DDD with hibernate

NimChimpsky
  • 46,453
  • 60
  • 198
  • 311
1

You should create that new product from your service layer. E.g. you could have an OrderService that pulls in (talks to) your Order Repository and Product Repository. The when you call SetCompleted() you also create the product (via the Product Repository).

You could also look and the Pipes and Filters pattern and create an Order Pipeline. That way you can plugin various steps (filters) that execute one after the other.

You should not create a product from within an Order entity because your domain entities should be database agnostic. You need a repository to create a new product, so the service layer facilitates this by talking to the data access layer.

autonomatt
  • 4,393
  • 4
  • 27
  • 36
  • 2
    also you want to use factories for creating of new entities (ProductFactory) and factory can be injected into OrderService and does it job when setComplete() is called. That way it is also easy to mock and unit test. – Tomas Dermisek Dec 13 '11 at 04:59