Sunday, May 18, 2008

Value Objects in DDD - Part 2 - Creating VO's using Hibernate

This post is part of a series I am writing on Domain Driven Design (DDD).

In my previous post on Value Objects I had mentioned that Value Objects can contain references to other entities and value objects.

I will show how to use Hibernate's features to define and use Value Objects. For illustration purposes I have taken a simple VO. If you look at any price label on any product, there are 2 consituents to it - a numerical price part and a alphabetical currency part. When paying for product, you end up multiplying the numerical part with the conversion rate of currency you pay to cashier.

Building from this example, I have an Order entity which has a Price value object. The price value object contains a reference to the Currency entity.

Order contains Price; Price refers a Currency

Order.java looks like

public class Order {
    
    private long orderId;    
    private String name;    
    private long quantity;    
    private Price price; //This is a reference to a ValueObject
    
    public Order(){}
    
    public Order(String name, long quantity, Price price) {
        this.name = name;
        this.quantity = quantity;
        this.price = price;
    }
    
    public double getUSDValue() {
        return price.getUSDAmount();
    }
}


Definition of Price Value Object

Price is a value object. Notice the lack of any apparent identity columns (Primary keys).

public class Price {
    private long amount;
    private Currency amountCurrency; //This is a reference to an Entity

    public Price() {}
    public Price(long amount, Currency amtCurrency) {
        this.amount = amount;
        this.amountCurrency = amtCurrency;
    }

    public double getUSDAmount() {
        return this.amount * this.amountCurrency.getUsdConvRate();
    }
}


Mapping ValueObjects in hbm files

All details of how to make Hibernate treat price as a value object is added to the hbm mapping files.


<hibernate-mapping schema="shop" default-access="field">

    <class name="org.dddtest.entities.Order" table="ORDERS">
        <id name="orderId" column="order_id">
            <generator class="increment" />
        </id>

        <property name="name"><column name="NAME" /></property>
        <property name="quantity"><column name="QTY" /></property>


        <component name="price" class="org.dddtest.entities.Price">
            <property name="amount"><column name="amount" /></property>
            <many-to-one name="amountCurrency" column="curr_id"
                class="org.dddtest.entities.Currency" not-null="true" />
        </component>


    </class>
</hibernate-mapping>


From the above mapping file the two things of interest are
  1. default-access="field"
  2. Component tag
The field access alleviates the need to expose getters/setters on the entities thus ensuring the class is well encapsulated. This was dealt in the post on entities.

A component is an object that is treated as a value type and not as an entity. This means that the fields of the component/value class would be persisted as part of some entity. In our case, fields of Price were persisted in Order entity/table since the Order owns all values in Price. Hibernate when creating the Order, creates a Price object and maps the values to it. This price is set into the order.

A nice feature with Hibernate components is that the same Price VO can be used with any other entity as well, since the owning entity (Order, XYZ etc) can always override mappings for Price fields. Also one component can contain another component within itself !

Using Components, any combination like Entity -> VO -> Entity or Entity -> VO -> VO etc can be created. This gives us powerful abstraction abilities to keep grouping fields into powerful domain objects and not be bound/limited by data model.



Technorati Tags: , ,

Subscribe to comments for this post

3 comments:

Anonymous said...

Kaushik,
Great job on the post. Keep writing them. Looks like you have done too many hands-on on the whole thing. Way to go dude!

Anonymous said...

Hello!

Looks good. But when you want to use Inheritance and Polymorphism with value objects, hibernate does not support you.

When you read books like "Domain Driven Design" of Eric Evans or "Refactoring" of Martin Fowler I think you would agree that using this feature -- using inheritance with value objects -- is an important feature for programming with a rich object model.

But if one of the most known OR-Mappers like hibernate does not support that, I ask myself, what technologies do the previously named authors use to implement a rich object model?

Thank you.

Jörg

Anonymous said...

Ah, I found two workarounds for the inheritance problem.
One here: http://blog.xebia.com/2007/09/25/hibernate-component-value-object-inheritance-mapping/
and one that use Entities, that have the same public class-Interface like the Value Objects. It´s like this:

@Entity
public class Parent1 {

@Id
@GeneratedValue
private Long id;

@OneToOne(cascade = CascadeType.ALL)
private ValueObjectWO wo;

public ValueObjectWO getWo() {
return wo;
}

public void setWo(ValueObjectWO wo) {
this.wo = wo;
}

public Long getId() {
return id;
}
}

@Inheritance
@Entity
@AccessType("field")
public class ValueObjectWO {

@GeneratedValue
@Id
private Long id;

private final String someStringAttribute;

public ValueObjectWO(String attribute) {
this.someStringAttribute = attribute;
}

private ValueObjectWO() {
someStringAttribute = "xyz";
}

public String getSomeStringAttribute() {
return someStringAttribute;
}
}


@Entity
@AccessType("field")
public class ValueObjectSubWO extends ValueObjectWO{
private final String newField;

public ValueObjectSubWO (String inheritedParameter, String newField) {
super(inheritedParameter);
this.newField = newField;
}

private ValueObjectSubWO() {
super("");
newField = "newField";
}

public String getNewField() {
return newField;
}
}

 
Clicky Web Analytics