DTO complex types and extra annotations (javax.validation, javax.persistence, ...)

154 views
Skip to first unread message

Wai Keung Yiu Man Lung

unread,
Jan 19, 2017, 5:28:28 AM1/19/17
to bndtools-users
I have been reading the enroute articles on the case for DTO instead of JavaBeans.

1. I am wondering how should complex types such as java.time.*, or java.math.* (BigDecimal, BigInteger), be handled.
Since these are obviously not primitives, I know that time can sort of be represented as long instead but how about unbound values such as BigDecimal?

2. Also, I am planning to write the DTO in the API bundle, how should I handle cases where I want to make sure of validation or persistence annotations.
Will I need to include the possible annotations in the API itself or what over ways can I handle this requirement?

3. Furthermore, sometimes, complex types makes more sense in certain scenarios, such as validatino for time @Past, @Future.
How can I make use of these? Should I after all, be creating a class implementation in the provider bundle in order to handle this?
For JPA, DTO -> Entity class in provider bundle using JPA.
From osgi R6, I saw for example BundleDTO, and an interface Bundle that will expose some get methods.
It is then later, implemented as a class. Maybe, it is this one that requires to have the validation and persistence annotation as well.

4. Finally, should my API return the DTO as the service result or maybe an interface representing the DTO?

Sorry for this confusing post but I am trying to get a grasp to understand how DTO will change the typical Provider (Java) -> API -> Consumer (Java) design.
I normally would have just used an interface in the API to represent my transfer object and call it a day, I am not sure where the DTO comes into this chain.

-Wai Keung

Peter Kriens

unread,
Jan 19, 2017, 8:21:50 AM1/19/17
to bndtool...@googlegroups.com
On 19 Jan 2017, at 11:28, Wai Keung Yiu Man Lung <wai.keung....@gmail.com> wrote:
I have been reading the enroute articles on the case for DTO instead of JavaBeans.

1. I am wondering how should complex types such as java.time.*, or java.math.* (BigDecimal, BigInteger), be handled.
Since these are obviously not primitives, I know that time can sort of be represented as long instead but how about unbound values such as BigDecimal?
You would handle them as byte[] or String. The litmus test is Javascript. If Javascript can work with it then you’re fine, things are easily to serialize. Special types will need conversion on the receiver/sender site. Sounds primitive, and I guess it is, but it saves a tremendous amount of misery once you have to leave your process. And it is a long time ago I wrote something that did not leave my process.

2. Also, I am planning to write the DTO in the API bundle, how should I handle cases where I want to make sure of validation or persistence annotations.
Will I need to include the possible annotations in the API itself or what over ways can I handle this requirement?
Depends. It is a painful case. You want to leave the DTOs in an API as unencumbered as possible but that implies redundancy. I am not aware of a good solution for this. Personally I have my own internal types that might seriously overlap with the API types.

3. Furthermore, sometimes, complex types makes more sense in certain scenarios, such as validatino for time @Past, @Future.
How can I make use of these? Should I after all, be creating a class implementation in the provider bundle in order to handle this?
For JPA, DTO -> Entity class in provider bundle using JPA.
From osgi R6, I saw for example BundleDTO, and an interface Bundle that will expose some get methods.
It is then later, implemented as a class. Maybe, it is this one that requires to have the validation and persistence annotation as well.
The primary purpose of a DTO is to communicate state between processes. JPA is a perfect example of a, imho failed, attempt to hide this aspect of communication. (Persistence is communication to a future process.) Custom specific types do not work very well in that use case because they tend to evolve in hard to reconcile ways. 

4. Finally, should my API return the DTO as the service result or maybe an interface representing the DTO?
DTOs should be ‘value’ objects and not shared between different receivers. So when you return them from a service you should return copies unique for that invocation. Creating an interface just to represent that DTO creates a lot of extra work for very little value. A copy is better imho.

Sorry for this confusing post but I am trying to get a grasp to understand how DTO will change the typical Provider (Java) -> API -> Consumer (Java) design.
I normally would have just used an interface in the API to represent my transfer object and call it a day, I am not sure where the DTO comes into this chain.
As said, prepare for interprocess communication (like persistence). Since the data you send over the wire MUST be standardised and carefully evolved it is often better to use that standard internally. The interprocess communication aspect has killed many of the advantages of object oriented design. Internal variables become exposed when you communicate an object over a wire. Therefore the benefit of data hiding turns out often not to happen.

Kind regards,

Peter Kriens


-Wai Keung

--
You received this message because you are subscribed to the Google Groups "bndtools-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bndtools-user...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Timothy Ward

unread,
Jan 19, 2017, 11:07:11 AM1/19/17
to bndtool...@googlegroups.com
Hi,

Peter has made some excellent points, and I believe can give a little more context/background.

On 19 Jan 2017, at 13:21, Peter Kriens <peter....@aqute.biz> wrote:

On 19 Jan 2017, at 11:28, Wai Keung Yiu Man Lung <wai.keung....@gmail.com> wrote:
I have been reading the enroute articles on the case for DTO instead of JavaBeans.

1. I am wondering how should complex types such as java.time.*, or java.math.* (BigDecimal, BigInteger), be handled.
Since these are obviously not primitives, I know that time can sort of be represented as long instead but how about unbound values such as BigDecimal?
You would handle them as byte[] or String. The litmus test is Javascript. If Javascript can work with it then you’re fine, things are easily to serialize. Special types will need conversion on the receiver/sender site. Sounds primitive, and I guess it is, but it saves a tremendous amount of misery once you have to leave your process. And it is a long time ago I wrote something that did not leave my process.

Peter is absolutely correct about this. The point of a DTO is to keep the data model 100% portable between remote machines, and to make it easy to interact with things like the OSGi Converter service. Rich types get in the way of this, and even technologies like JAX-RS need to have special handlers registered to serialise/deserialize them. The overall model is much simpler if you accept the limitations of a DTO, rather than having to add special handling logic in every client and server.


2. Also, I am planning to write the DTO in the API bundle, how should I handle cases where I want to make sure of validation or persistence annotations.
Will I need to include the possible annotations in the API itself or what over ways can I handle this requirement?
Depends. It is a painful case. You want to leave the DTOs in an API as unencumbered as possible but that implies redundancy. I am not aware of a good solution for this. Personally I have my own internal types that might seriously overlap with the API types.

The moment that you start adding persistence annotations and/or validation annotations to the API types you are leaking implementation details. Is it really the case that your API must be used with JPA, or could someone write an implementation using JDBC/myBatis/NoSQL? By adding the JPA annotations into the API you are forcing an implementation choice everywhere, right down to the way the types are mapped to database tables. The same is true with the validation annotations. Whilst the restrictions on values should be documented, is it really a requirement that everyone use JPA and javax.validation? In my view a good API should be clean of these sorts of annotations.


3. Furthermore, sometimes, complex types makes more sense in certain scenarios, such as validatino for time @Past, @Future.
How can I make use of these? Should I after all, be creating a class implementation in the provider bundle in order to handle this?
For JPA, DTO -> Entity class in provider bundle using JPA.
From osgi R6, I saw for example BundleDTO, and an interface Bundle that will expose some get methods.
It is then later, implemented as a class. Maybe, it is this one that requires to have the validation and persistence annotation as well.
The primary purpose of a DTO is to communicate state between processes. JPA is a perfect example of a, imho failed, attempt to hide this aspect of communication. (Persistence is communication to a future process.) Custom specific types do not work very well in that use case because they tend to evolve in hard to reconcile ways.

Peter is correct here in that the JPA entity types are a different API, they are an API that you have with the database, and they should not, in general, be the same types that you share out into the world. If you make the DTO types your entities then you are totally unable to separately evolve your database and your API. This sort of coupling is exactly what modularity is supposed to avoid.

 

4. Finally, should my API return the DTO as the service result or maybe an interface representing the DTO?
DTOs should be ‘value’ objects and not shared between different receivers. So when you return them from a service you should return copies unique for that invocation. Creating an interface just to represent that DTO creates a lot of extra work for very little value. A copy is better imho.

Returning a DTO is definitely preferable if you ever intend to call the service remotely. Using an interface as the return type prevents the value from being easily serialized/deserialized.

David Leangen

unread,
Jan 19, 2017, 1:08:40 PM1/19/17
to bndtool...@googlegroups.com

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.

If you are interested, please come over to d...@felix.apache.org.

Cheers,
=David

Wai Keung Yiu Man Lung

unread,
Jan 20, 2017, 5:34:02 AM1/20/17
to bndtools-users
Hi all,

Thank you very much for the very helpful feedback on the usage of DTO and API.
I also saw the Object Conversion RFC (Converter) and will keep a look out on the progress.

I agree indeed that the API should not be cluttered with implementation details.
Indeed, implementations can have private representations of the DTO with the appropriate additions as applicable.

I will try more experimentation and explore further David's idea which seems reasonable if it works out.

Wai Keung
To unsubscribe from this group and stop receiving emails from it, send an email to bndtools-users+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups "bndtools-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bndtools-users+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups "bndtools-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bndtools-users+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages