Hexagonal Architectures: @Column(nullable = false) vs @NotNull in Java Applications

Home » Blog » Software » Enterprise Software » Hexagonal Architectures: @Column(nullable = false) vs @NotNull in Java Applications

Recently, I came across a small and common, but all the more interesting, hands-on example of why a hexagonal architecture can help with making both the architecture and the code of an application more understandable, approachable, and maintainable.

I won’t be going into any details about the concept of and ideas behind hexagonal architecture here. The gist is summarised quite nicely in this quote from Tom Homberg‘s article on Hexagonal Architecture with Java and Spring:

The main feature of “Hexagonal Architecture”, as opposed to the common layered architecture style, is that the dependencies between our components point “inward”, towards our domain objects.

If you’d like to know more about hexagonal architectures and how to use them, both that article and Tom’s excellent book Get Your Hands Dirty on Clean Architecture will have you covered.

Anyway, back to the example mentioned above. In Java applications, with JPA and Hibernate we often see model source code that looks somewhat like this:

Now, the actual business model and the use cases it’s applied to, isn’t really relevant here. Just as a quick refresher, the @Entity annotation tells JPA that we’d like to store a representation of instances of the type Entry in a database. The @Id, @GeneratedValue, and @Column JPA annotations denote an ID column, with an automatically generated value for each entry, and a generic column, respectively.

The really interesting part are those two annotations on the name property (or rather the nullable = false argument in the case of @Column):

Both nullable = false and @NotNull seem to imply the same meaning, i.e., “The value of this particular attribute shouldn’t be null.” (which usually is referred to as a not-null constraint), don’t they?

So, why do we have to repeat ourselves in that manner here? Isn’t that completely redundant?

From a cursory glance, it is. However, at a closer look, these two lines of code each apply to different aspects of nullability: While @Column(nullable = false) defines a column in the underlying database table to not be allowed to contain null values, @NotNull is part of the Bean Validation specification and simply requires the name attribute of any instance of Entry to not be null (see this article on @NotNull vs @Column(nullable = false) for more details).

Although using these definitions in conjunction is a common pattern – to the extent long-term Java developers like myself often aren’t even consciously aware of its implications anymore – there’s an important distinction between those two: One (@Column(nullable = false)) is responsible for a property related to persistence, while the other (@NotNull) is concerned with domain model behaviour, specifically domain model validation.

In terms of software architecture – and from a hexagonal architecture perspective, in particular – using these definitions together in the same class amounts to conflating the domain layer and the persistence layer. In many cases, that’s a perfectly adequate and justifiable shortcut to take. However, such a shortcut can also lead to confusion because of what looks like redundancy or due to a wrong interpretation as to which of these definitions does what exactly.

A better alternative that’s more in line with clean code principles, particularly the single-responsibility principle, would be to separate these responsibilities from one another by having two separate classes, one for the domain model and one for the persistence layer:

That way, we can keep those two layers separate and avoid confusion. Admittedly though, this can give rise to additional complexity further down line, e.g., when it comes to mapping between domain models and persistence layer entities, and the various strategies for doing so.

About the author: Bjoern
Independent IT consultant, entrepreneur

Leave a Comment