As many other developers, I’ve been used to the fat service layer and the anemic domain model of the transaction script pattern. In that programming model, immutability is pretty much as rare as a Dodo. However, I have been investigating the rich domain model pattern lately (as you can read in my previous post) and more importantly, a good migration path for “transaction script” developers to get acquainted with the rich Domain Model, a design pattern that has been heavily underrated (and misunderstood) by many.

In this post, I will explain some of the advantages that immutable domain objects bring us, while showing that some of the seemingly problematic side effects aren’t really that problematic.

A practical example is often the easiest way to explain these kind of things. Since the banking world has received enough discredit in these times, I will use another example than the infamous “withdrawal” example. Instead, we will use the almost-as-infamous product and order example.

Imagine an application where you can browse trough products and place an order for one or more of those products. Our simple domain will contain three entities: product, order and order line. A UML class diagram for this domain is shown below.

power_of_immutability_class_diagram_1

The anemic approach

A commonly seen approach to implement this domain model is the following. The Order is implemented containing some delivery details and a list of order lines. The Order exposes getters and setters to modify each of those properties, including a getter that provides access to the list of order lines. The implementation of the OrderLine class is similar; it might contain a reference to a Product instance as well as a property to indicate the quantity ordered, resulting in the following code:

public class OrderLine {
    private Product product;
    private int quantity; 

    public Product getProduct() {
        return product;
    } 

    public void setProduct(final Product product) {
        this.product = product;
    } 

    public int getQuantity() {
        return quantity;
    } 

    public void setQuantity(final int quantity) {
        this.quantity = quantity;
    }
}

This code has a few problems, some functional and some technical

  • What happens if the price of the product changes? Invoices will probably show the new price (which is always higher, for some reason).
  • It’s no longer possible to delete a product, since you won’t be able to see what somebody ordered
  • You allow every other class in your application to modify the OrderLine at will. What if you have a discount for customers that order for a certain amount? Each time an order line was modified, your code would have to call a calculateDiscount in your order
  • If you want to calculate the total price of an order, you would have to iterate through all OrderLine and Product instances.

Of course, each of those problems can be solved some way or another, but whatever you do to solve them, it only adds to the complexity already there.

The immutable approach

Making the OrderLine immutable will solve some of the problems outlined above. The state of immutable objects is exclusively set in the constructor, meaning that both the Product and quantity will have to be passed as constructor parameters.

To solve the problem of the Product changing state, we can copy some of the important fields into the OrderLine. Good candidates for copying are the product identifier, the product name and its price. In fact, we don’t need any reference to the original Product instance at all after the OrderLine has been constructed.

The implementation of the immutable OrderLine is then changed into the following:

public class OrderLine {

    private final String productIndentifier;
    private final String productName;
    private final Price itemPrice;
    private final Price totalPrice;
    private final int amount;

    public OrderLine(Product product, int amount) {
        this.productIndentifier = product.getIdentifier();
        this.productName = product.getName();
        this.itemPrice = product.getPrice();
        this.totalPrice = itemPrice.multiply(amount);
        this.amount = amount;
    }

    /* getters not shown for brevity */
}

All problems solved? Well, no, not really. For starters, the last two problems enumerated above are still not solved. And then there is persistence. I use hibernate for nearly every project that requires persistence. Hibernate and immutable objects are not the best friends possible. Hibernate requires a non-private default constructor. Doesn’t seem like a problem, just add a no-argument constructor to you class. But then the final keyword ruins the game. You’ve got no other choice than to remove it. This removed the guaranteed visibility when your class is accessed by different threads at the same time. Solving that is a totally different ballgame and would carry me way off topic.

Using an aggregate root

We still have the problem that our discount is not updated when OrderLine instances are added or removed from an order. Making an order immutable in the same fashion would solve the problems. But let’s assume that a user is allowed to update and modify his orders as long they haven’t been processed by the system.

Exposing the list of OrderLine entries inside an order is not a good way to go, since your order is not in charge of its own lifecycle. If you take a close look at the UML diagram above, you will notice there is a composite relationship between the Order and OrderLine classes. This means that the Order class is responsible for the lifecycle of the OrderLine items that belong to it. In Domain Model terms, this means that Order is the Aggregate Root of these components.

The order would then be implemented as follows:

public class Order {

    private final List<orderline> orderLines = new ArrayList<orderline>();
    private final Customer customer;
    private Address shippingAddress;
    private Price totalAmount;

    public Order(Customer customer) {
        this.customer = customer;
    }

    public List<orderline> getOrderLines() {
        return Collections.unmodifiableList(orderLines);
    }

    public void addToOrder(Product product, int amount) {
        removeFromOrder(product);
        final OrderLine orderLine = new OrderLine(product, amount);
        orderLines.add(orderLine);
        totalAmount = totalAmount.add(orderLine.getTotalPrice());
        calculateDiscount();
    }

    public void removeFromOrder(Product product) {
        // implementation left to your imagination
    }

    /* rest of implementation left out for brevity */
}

As you can see, a high level of encapsulation has been applied. It is now impossible to change any state of the Order or OrderLine without the Order knowing it. The list of OrderLine items that the Order exposes is made immutable using the utility method in the Collections class.

With this implementation, it the latter two of the problems enumerated above have been solved. Since the order is in charge of making the changes to its own state, it is rather easy to discover state changes that might impact applicable discounts or change the total amount of the order. Now, getting access to the amount of the order is as easy as returning the totalAmount property from the Order.

Conclusion

In this post, I’ve introduced a very powerful concept to manage the state of complex domain models: immutability. By preventing the application to change state directly, you gain more control over the the actions that need to be taken when the state changes. Especially in aggregates, immutability reduces the complexity of state management.

However, when using Hibernate, it is impossible to make an entity completely immutable using the final keywords. In that case it is sufficient to create a default constructor with package visibility and removing the final keywords.

The power of immutability in a Rich Domain Model
Tagged on: