Hi there,
Based on the ideas presented by Peter, Timothy and others, which I discovered only a few months ago, I have completely changed the way I build and implement my domain models. I still have a few paths to hack forward, but I can now see it quite clearly in my mind. I have adopted these concepts to the extreme, but am finding it very useful.
In this approach, the DTO is just a different representation of a domain object. It represents the essence of an object, and contains all the information necessary (but nothing more) to completely describe its state. The Java interface is the “structured” representation of the object, meaning it is (1) type safe and (2) can contain derived calculations. The two are different (public) representations of the same object, and each has a different purpose.
The interface:
* is read-only (but works very well if you adopt a more functional programming style)
* provides the “rich” representation of the object for better communication and comprehension
* provides “safe” access methods (again, mostly for better comprehension, since “safe” is
an illusion, so really only copies of objects are used)
The DTO:
* holds the minimally necessary state of the object
* can be serialised and sent across the wire
* can be persisted
* requires adherence to a few simple rules to permit the above (as per the spec)
Both the interface and the DTO are public, so should be in the exported package. Since they really are two representations of the same object, it is my practice to put them into the save .java file.
I have found that creating a value object out of both the interface and the DTO seems to work really well. The value object provides the implementation, based on the core data provided by the DTO and the public methods declared by the interface. There appear to be 3 types of methods:
1. Simple pass-through, when the method return type is the same as the data type
2. A type-safe representation of data
3. A derived calculation
Simplistic example:
public interface Person
{
String name();
String nickname();
Gender gender();
static class PersonDTO
extends DTO
{
public String first;
public String last;
public String nickname;
public String gender;
}
}
The implementation is in a private package, and would look something like this:
public class PersonVo
extends Person.PersonDTO
implements Person
{
/**
* Example of a derived calculation. A derived calculation could be anything,
* but depends on one or more "core" data fields in the DTO. Name could also
* have been an embedded object itself, but this is just an example.
*/
public String name()
{
return new StringBuilder()
.append( first )
.append( " " )
.append( last )
.toString();
}
/**
* Example of a core data field provided "as is" via the interface.
*/
@Override
public String nickname()
{
return nickname;
}
/**
* Example of a core data field provided bascially as is, but in a type-safe form.
*/
public Gender gender()
{
return Gender.valueOf( gender );
}
}
Now, using the OSGi Converter, it is trivial to persist any of my domain objects, as they are already in the form of a DTO. Since the DTO is actually a schema of the object, it can be converted to json, xml, yaml, or whatever, and back. I suppose that for JPA, you could consider annotating the value object. Or, instead of annotating the object, you could just write a generic converter that handles DTOs. Indeed, that has been my approach to persistence, and so far it is working very well.
To actually make this all work, I have been investing a lot of time in the Felix Converter project. It is still a mess err work in progress, but I am pleased with the results so far. If I have time, I will write more about this. It really seems to cut down on a lot of cruft in the implementation of the domain, especially with regards to persistence, which seems to be exactly what you are dealing with right now.
Cheers,
=David