Are persistence annotations in domain objects a bad practice?
Asked Answered
V

8

38

I realize that persistence frameworks such as Morphia and Hibernate rely on annotations on domain objects to do their magic. At some level, it seems to me that this is inserting persistence concerns into the domain layer which is something we're supposed to strive to avoid.

Is this something that I should try to dodge by perhaps using an external configuration file or maybe separate DTOs from the domain model? Or is this little leak between the persistence and domain layers generally regarded as acceptable?

Vanhorn answered 11/4, 2012 at 4:3 Comment(2)
See this presentation infoq.com/presentations/Clean-Model-Tim-McCarthyPidgin
Thanks, this looks to be a great presenation! Watching now...Vanhorn
S
17

In my latest iteration on an existing system using Spring and Hibernate, I have started to move in a similar matter. When first implementing Hibernate models, I strove to separate the application logic in service classes from the persistence logic via data access objects. When building a new system last year I allowed most of the persistence objects to serve as the domain objects because that was the expedient solution.

However, I am redesigning this same system in light of changing business requirements, and I'm again leaning towards separating those concerns. I'm only a few days into the new design, but already I find myself preferring to have one object that represents the in-memory concerns object while a separate persistence-based object is used to store its state changes to the database. For example, I have a Lead for persistence and a parallel ActiveLead that lives across transactions.

I'm not yet convinced this is the best method, but it makes sense on a gut level. I've longed to have a collection of persistence-agnostic--nay, persistence-ignorant--set of objects that remain memory-resident across database transactions without regard to the standard CRUD simplifications. Yet I understand that in the end all database operations are implemented as CRUD. The two worlds must collide, and the trick is in making them dance in harmony.

Hibernate annotations on domain objects? This is a fine compromise between ease of implementation vs. ease of maintenance in my view.

Supernova answered 11/4, 2012 at 4:37 Comment(1)
This also gives you the opportunity to be more strict/less strict as to the state of your persistence objects vs the in-memory ones. I try to be extremely cognizant of inputs and outputs from an interface and separating these concerns in this manner allows for that.Impressure
Y
12

I've recently worked on a reasonably complex system with had a separate persistence layer, and it was a huge pain in the ass and very bad for maintainability. You're basically looking at a conflict between the principles of YAGNI and Single Responsibility. In my opinion, YAGNI is the more important one (alas, also the more frequently ignored one).

I'd say in the vast majority of cases, it's much better to persist domain objects directly if you're using an ORM, unless you have concrete requirements that force the persistence entities to be structured differently (if they have exactly the same structure, there is no reason to separate them except ivory tower arguments).

To be sure: always do the actual persistence stuff (calling ORM functions) in a separate service/DAO layer! That way, it's easy to introduce a persistence layer later if you find that you need it.

Yoshieyoshiko answered 11/4, 2012 at 6:18 Comment(4)
One case where I run into trouble with using the same object is optimistic locking from multiple threads/servers. If you try to save a change to firstName for example, and find that someone else has changed the object, you need to reload the object to make the change. How do you insert the newly-loaded-and-modified object back into the tree of domain objects?Supernova
Voted down, I disagree. Too many annotations referring to, too many concerns which many times tend to force your class structure into different directions, plus behavior, make your class hard to read and maintain, and that is bad.Burkley
@dalvarezmartinez - What if the annotations are used not only for data persistence concerns, but for domain concerns? For example: Marking a Name property on a domain object as [Required] makes it pretty clear that the field is required, but also informs the Validation layer DB layer that they should not accept null values.Shyamal
@Shyamal It may be a valid case, I'm not saying you should never have any annotation, under no circumstances in the domain. It's just that in my little and poor experience, Single Responsibility is more important than, trivial YAGNI (trivial because the mapping is pretty easy in most cases). There are people, us, who force domain objects to have behavior (which requires certain data fields, structured in one way), also this is used as a DB Data structure (which requires data fields,structure in another way), see obj-rel impedance mismatch, and some XML annotation, just in case...way 2 much for meBurkley
V
9

Are persistence annotations in domain objects a bad practice?

Yes. With the rise of NoSQL, you can not rely on single persistence strategy.

For example, today I am persisting my domain objects (let's say using morphia) into MongoDB. What if tomorrow I want to persist domain objects to Neo4j ?

Or, one might want to persist domain objects to all three kinds of databases like relational (Postgres / MySQL), MongoDB(document store) and Neo4J(graph database) just for evaluating.

In all these cases, better to have separate persistence strategy rather than just relying on domain objects

Best Practice : Passing Persistent strategy as a strategy pattern might help. But have to be careful while designing your classes / objects.

Verdellverderer answered 4/1, 2014 at 18:27 Comment(2)
I think there are too many "what if..." questions. I haven´t seen any project which changes the database nor using three different db technologies in a single module which could sacrifice the cost of maintaining additional strategies/mapper etc. In my experience projects are much more difficult starting with this approach instead of simply annotating the domain layer.Detach
@Detach I think you make a fair point, practically speaking. Still, this leads me to conclude that it needs to be made easier to apply persistence frameworks in conjunction with the repository pattern - especially with respect to DDD and/or TDD.Agora
S
6

Short answer: I like persistent, rich, domain objects.

Long answer:

For nearly 10 years I worked on a fairly large system ~ 500k LOC using Spring and Hibernate. At the beginning, we started with a "transaction script" (see Fowler) approach, partly because we didn't quite trust Hibernate. However, in a short time, we came to trust Hibernate and due to my much earlier training in fairly purist OO, I became a big believer in transient persistence combined with a Domain Driven Design approach. We basically came to think of our system as being backed with an ODBMS (with plenty of small leaks :-)).

I called our architecture a "domain kernel" because the book DDD had not been written yet. This was in the early days of Hibernate, so the domain model was not polluted with annotations. The separate concern of persistence was kept separate in XML mappings.

Again, over time, we got better at pushing behavior down into the domain layer. We had a pretty traditional controller-->service-->dao-->domain layering scheme that was enforced with compile-time dependencies. What I observed over time is that this model worked very well for our system, which represented every facet of the fairly complex domain of 401(k) plan management, including plan setup, trading, accounting, compliance testing, sales, branding, etc. A rich domain model with (relatively) transparent "magical" persistence was key in our being able to build new features in terms of existing features in the domain model.

Our service layer only orchestrated the interactions between technical services (such as email, file I/O, queuing, etc.) and helped to span domain packages when necessary. The service layer also defined the transaction boundary (via Spring). Services only received or emitted DTOs or primitives. A lot of people hate that, as a break in DRY, but we found it kept us honest when defining the service interfaces and the code that used them. It also made it very easy to remote things later.

This approach allowed us to build high-quality software with a pretty small team (we were a Scrum team).

So, consider me a believer in persistent domain objects. Don't know if my story helps, but I wanted to share.

Silassilastic answered 7/10, 2013 at 14:35 Comment(0)
U
5

I believe I will use annotations on my domain if I am already decided with the persistence framework I am going to use, however, XML would be more convenient if you follow the Hexagonal architecture and TDD. If you annotate your domain with specific framework upfront, your will be coupled with the persistence integration and unable to test the core functionality with the aim of being technology/framework agnostic.

Urger answered 11/2, 2015 at 9:54 Comment(1)
The domain is not actually coupled with a specific technology, if annotations are used. Yes, the compiler needs the annotation interfaces, but the executed code is not affected, since annotations are adding only metadata. It is no problem to test the domain without a JPA implementation and it would even be possible to use a completely different persistence technology like MongoDB and to ignore annotations then.Reiner
O
4

In my opinion there's no need to duplicate the domain objects to be able to seperate them from your persistance layer. It works duplicate code in hand and it's perfectly doable by using those same objects as DTO's. If necessary you can always use seperate classes if it's needed, but I wouldn't make it a rule of thumb, it 'll cost you time and we all know time is valuable.

Okay answered 11/4, 2012 at 5:2 Comment(3)
I disagree. The domain objects are by their nature different from that persistence model objects, they have different purposes. If sometimes the domain is not really much of a domin and the persistence entities can be used directly, that a simple coincidence, an implementation detail. The idea is that you start modelling the domain, ignoring the persistence all together. Recently I blogged about exactly this situation sapiensworks.com/blog/post/2012/04/07/…Pidgin
@Pidgin In my experience, the domain objects and tables do not differ that much. The differences that do come up should be easy enough to translate through an ORM's customization options. In my opinion, a good ORM should help make this easy.Agora
Totally agree with @Pidgin and his blog post. For instance, I don't want to be forced to use ugly getters/setters in my domain model just to satisfy ORM needs. I want my domain model to shy without any constraint from the persistence's world.Snigger
D
2

I would prefer rich domain objects which have the annotations on it. Even Evans uses this approach in his sample app. He uses XMl instead of Annotations but he still persists the same objects.

Maybe it´s more clean to separate domain and persistence but don´t do just to be able to potentially choose a different db technology in the future. It´s the way down to complexity hell and Mister YAGNI will bite you.

Detach answered 10/2, 2015 at 10:6 Comment(0)
D
1

Something found in DDD community

Post by Chris Richardson * If you want to keep JPA out of the domain model then use XML instead of annotations (i've never been a fan of ORM annotations since IMHO it mixes concerns)

Personally I like a lot to use annotations, XML was always for me error-prone, one small change in a field name and you need to manually change XML too. If you want to refactor a single class of your domain, you could end up changing several files instead of it being handled automatically. But lately, I'ven been reconsidering this because I want to be able to use several persistent options in a project. I do not want anything related to persistence in my domain, so XML is an option. Still, several times I get to a point where there is no direct mapping, or I still want to use annotations because they are so easy to change and visible right into the code. Somethinig I've been doing lately is create my business domain class as and abstract one, and user another for persistence extending it. Something like this:

public abstract class Persona {
  private Set<State>states;
  public boolean inState(State state){
    return states.contains(state);
  }
}

if, for some reason there is a db where states are already defined as a single column, and no direct mapping is possible, I can extend business class and used it as a persistence entity.

@Entity
public class PersonaSql extends Persona {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  private String statesDefinition;

  @PrePersist
  void prePersist(){
    this.statesDefinition = mapStatesSetToString();
  }


  @PostPersist
  void postPersists(){
    this.states = mapStatesStringToSet();
  }
}

Of course, this is a trivial example. There are other ways to solve this issue, my point is: by using inheritance you can take the great advantages of working with annotations and have your business model ignorant of specific persistence code.

Another option without using inheritance is converting persistence entity to business model and viceversa, but I won't recommend going this route (even with things like automapper), unless your domain is simple and you are sure it is going to stay simple. For example, if you are creating micro-services, your domain should be simple enough and its expected to be simple.

Domicile answered 21/10, 2018 at 5:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.