Hibernate and Domain Model Design

Ürgo Ringo
Jan 4, 2018 · 4 min read
Used image from Ridley Scott’s Alien

When I first learned about Hibernate about 12 years ago (I guess just about when it was at its peak) I found it to be one of the coolest libraries ever created. Hibernate made it possible to use OOD in practice! Build complex domain model graphs that were not only data structures but also contained some actual domain logic. And then you could just persist all of that.

Unfortunately my views on Hibernate/JPA have changed since then. Where I once saw it as an enabler I now see it more as a constraint — a source of accidental complexity. So much so that I would not consider Hibernate (or any other ORM tool) as the first choice for persistence anymore.

I feel that nowadays it is popular to criticize Hibernate and JPA in general. Unfortunately it seems that very often these opinions are based on some superficial experience and emotions. I do believe that emotional aspect and overall popularity is important when evaluating some technology but it is also important to try to understand the underlying rational reasoning.

I do not consider myself a Hibernate expert but I hope my experience is good enough to represent the opinion of someone who has given it a good try. I have been part of a dozen or so projects using JPA/Hibernate. From small 4 dev 4 month ones to big enterprise systems built by many different teams.

Aggregates

This all changed when thanks to Vaughn Vernon I finally understood what was the idea behind Aggregates in DDD. I realized that referencing objects outside of your aggregate by ID actually makes your design much better and helps to avoid this huge tangled mess where everything is somehow linked to everything. Also I understood that if you are relying on lazy-loading for something in your aggregate then you have probably modeled it incorrectly. Aggregate is all about keeping invariants — something that you don’t seem to need if you can do lazy-loading.

So in short Hibernate’s support for complex object graphs and lazy loading is not something we inevitably need for implementing rich domain models.

Design constraints

It gets hard to use Hibernate without making any sacrifices to the design as soon as we do not want to model our domain classes 1:1 based on our database schema. Once we start thinking how to make our domain entities smaller, introduce interfaces and multiple implementations for value objects, use unidirectional associations where it makes sense it will require a lot of effort to find the right blog post or stackoverflow answer. Yes, it is possible to use things like PrePersist, PostLoad hooks or force Hibernate to use AccessType.PROPERTY but these are all tricks that are only needed for the persistence framework and will not add any other value.

Violation of Single Responsibility Principle

Even though Hibernate takes care of all DB interaction we still need to verify that it understands our mapping the way we intended. If we have an entity MyEntity which uses annotations for mapping information we will need to write a test like MyEntityPersistenceSpec. However, we also need to test business logic so we will have something like MyEntityDomainLogicSpec as well. Now we have 2 specs for testing 2 different aspects of one production class. This kind of split in tests is often good sign that we should split the production code as well. One way how to achieve that is by moving all state into MyEntityState object as suggested by Vernon but this is again an example of particular tooling forcing design decisions on us.

Even if we go old-school and externalize mappings into XML we still have the problem that we cannot be fully sure if the way how we modify our entities does not somehow affect persistence. All the collections in MyEntity are not under our full control. They are implementations provided by Hibernate. For example, we must be careful when we create new collections vs when we use clear, addAll.

Learning curve

I still believe that Hibernate is an amazing technology. What better proof can there be when something is so widely used for more than a decade. It’s just that when I used to believe that Hibernate is the enabler for building good domain models then now I believe it is more suitable when you are OK to keep your domain model relatively close to your DB schema.

I think quite many systems are built using anemic domain model where full power of OOD is not utilized. Depending on the essential complexity of given problem domain this can have little or significant effect on the overall cost of maintenance. So in that sense I don’t believe that complex domain model is always a “must have” in which case using Hibernate might still be ok. At least when none of the reasons for not using Hibernate are applicable.

Originally published at tech.transferwise.com on January 4, 2018.

Wise Engineering

Posts from the @Wise Engineering Team

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store