Domain Primitives

As mentioned in my previous blog post, the idea of domain primitives seems interesting. Since the focus was more on validation I did not go into much detail. In this blog post, I will be discussing Domain Primitives in much more detail, their advantages and disadvantages, and even their potential future evolutions.

The idea of domain primitives is rather simple, instead of using generic types to represent the objects you use real class instances for them. Sounds rather straightforward, yet almost nobody does it and I believe the major reason for this is that we have generic types that are suitable for almost all data representations. Why would you create an object to represent an e-mail address, first name, last name, street, and things like that when a String can do all of this and you don’t have to write any code for it yourself? The same applies to any quantity, size, or amount which can all be represented with either an int or a long.

You could even argue that creating a class as a wrapper around the primitive types goes against the DRY principle but this is not completely true as we need to distinguish between different types of duplication. In a way, you could call them syntactical duplication and semantical duplication. Where the first is duplication because they share the same syntax, we have the same code but only on a syntactical level such as checks on the size of a string or checking whether an integer is positive. It is the semantical duplication that is the problem as you have a piece of code that represents the same thing, it is code that has the same meaning like duplicating some sorting rules.

Why would you want to use domain primitives in the first place? As already mentioned in the previous blog post they are handy to have all of your validation in one place and they offer guarantees that the data is valid thus you no longer need to check all the data in every method you call. It also abstracts the implementation of the entity such that it avoids too many code changes if your implementation would change. Even though I don’t think this will happen often, if you ever need to change int to a long to allow bigger numbers, having this detail in a domain primitive will make it much easier. Note that this is only valid if you also provide methods on your domain primitives and don’t just use them as data objects.

public class Quantity {

    private final int value;

    public Quantity(int value) {
        this.value = value;
    }

    public Quantity add(final Quantity other) {
      return new Quantity(this.value + other.value);
    }

    public int getValue() {
        return value;
    }
}


final Quantity a = new Quantity(35);
final Quantity b = new Quantity(79);

final Quantity c = new Quantity(a.getValue() + b.getValue());
final Quantity d = a.add(b);

By having the add method, you restrict the implementation details to the constructor and the simple get which you will likely need to construct DTO or DAO objects. Which will drastically decrease the number of locations where you have to change the code. Another advantage of using domain primitives is that they allow you to make your APIs clearer, restrict them a bit, and prevent bugs caused by switching parameters.

public void createOrder(final ProductId id, final Size size, final Quantity quantity) {
    ....
}

public void createOrder(final int id, final int size, final int quantity) {
    ....
}

The first implementation will not even compile if you by accident switch parameters, the second however will just let you go with it. Your tests should protect you from this, but it is always possible that you miss situations like this and I am always in favor of an API that does not allow using it in the wrong way compared to one that asks you to use it properly.

With these two very clear and very strong reasons to use domain primitives, why have I not done so? Well, I guess I didn’t really think much about it. But even then I have seen them being used for the second reason in the past and I couldn’t imagine it being something valuable enough to actually start using them, because domain primitives also come at a cost and they can make your code much more verbose than what it should be.

final ProductId id = new ProductId(dto.getProductId());
final Size size = new Size(dto.getSize());
final Quantity quantity = new Quantity(dto.getQuantity());
createOrder(id, size, quantity);

createOrder(dto.getProductId(), dto.getSize(), dto.getQuantity());

In the code snippet above we had to introduce an extra ‘layer’ to convert the DTO (which is a simple JSON object) to our domain primitives. I don’t think you can argue that in this case, the code gets much clearer, instead you seem to be doing some rather stupid conversion and who is to say you don’t make a mistake there?

Another reason I haven’t used them in the past is the concern regarding the number of objects you are creating, which eventually just become garbage that needs to be cleaned up. However, with the many improvements being done in the garbage collection I have done a quick test and from this, I concluded that there seems to be no overhead in creating all of these objects. Yes, your memory footprint will go up, but the garbage collector can handle this and get rid of all of the garbage without increasing the runtime of the application. On this topic, there was also a recent post by Oracle itself regarding Project Valhalla (https://inside.java/2022/12/06/jepcafe15/), and they did a test that does show that in some scenarios (when using it in an array), having objects is slower then primitives. I believe this is because of optimizations the CPU does by pre-fetching memory blocks where with primitives it will load more useful data compared to objects, hence also the result that a split loop is faster than a joined loop over two arrays.

However with value record classes still being implemented even this drawback will be removed in the future, which does open the door to using domain Primitives even more. In the meantime, I have been looking to see whether something like a ‘primitive alias’ exists in any language. By this, I mean that you can write a class that is nothing more than just an alias for a primitive plus some extra logic. The class would only exist at compile time and at runtime, everything would be inlined. I haven’t found such a feature yet, and implementing something like this seems impossible with the Java Annotation Framework.

What about the sheer amount of classes you are creating and how to organize all of these such that you can still easily find them. Perhaps something I have grown fond of could help here as well, you could group them together by using nested inner classes, this can also help with naming issues since you have the outer class as some type of scoping mechanism.

I have decided that domain primitives are certainly something I will be experimenting with in the near future, but for now, I have not yet done so. This means you can expect a follow-up blog post regarding this topic on how it went and which approach I took.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.