Skip to content

CodeGenerationPlatform_DTO

Ladislav Gazo edited this page Aug 29, 2015 · 1 revision

Introduction

Maybe you know Gilead - Generic Light Entity Adapter framework that allows you to use persistent entities in the GWT application - and maybe you know that there are few fundamental problems behind this good looking idea. First of all, you have to introduce pretty hard dependency to your domain/dto objects on the gilead LightEntity class or ILightEntity interface - this can be serious problem if you have complex domain model reused over many application (not only in GWT ones). Or you can use dynamic proxy mode, that is pretty risky and not recommended by the owner of the gilead framework itself. "Warning ! The Dynamic Proxy mode is probably the trickiest one and should be considered as experimental ! if possible, use stateless mode, you will escape some class loader and generators possible issues !" http://noon.gilead.free.fr/gilead/index.php?page=dynamic-proxy

In spite of this drawback we've been "successfully" using Gilead - in many variations - stateless mode or dynamic proxy mode for few years. In that time, size of our application became grows dramatically - RPC layer had hundreds of KBs - so we started investigate the problem and we learned following things: **It's veery bad practice use generic Serializable interface in the objects that are tranfered over the RPC layer, like we had:

public class ClientSession {

	private Map<String, Serializable> session;
	
	...
	//getters and setters
}
```**

and we were using this session object also in the async services - BAD IDEA #1

Now, here is why:
GWT RPC generator computes that there can be a lot of another objects stored in this map (every object that implements Serializable interface) and creates serializer/deserializer for these objects. That produces a big code that will part of your final JS and the size of your application will grows dramatically.

  * It's not good practice to reuse domain objects in the client side code for many reasons:
    * RPC layer becomes too massive and complex and generated RPC code will become bigger and bigger until your application won't be usable
    * you will transfer objects over the wire that is not really used in the UI (fields are not displayed to the user, etc.)
    * your domain is many times optimized for the database, not for the UI (=user) itself
    * gilead does not helps you and it have very ineffective implementation, let's see why

public abstract class LightEntity implements ILightEntity, Serializable { /** * Map of persistence proxy informations. * The key is the property name, the value is a map with * persistence informations filled by the persistence util * implementation */ protected Map<String, IGwtSerializableParameter> _proxyInformations; ...


In each LightEntity (=your domain/dto object) is map of the proxy informations, that is Map holding IGwtSerializableParameters. This is the root of the problems, because ILightEntity interface extends IGwtSerializableParameter interface

public interface ILightEntity extends IGwtSerializableParameter { ... }

> -> therefore there can be stored any of your domain object in proxy info hash map,
> ---> therefore GWT RPC generator have to create serializer/deserializer for all domain objects for every async service!

> That means you use gilead based domain object/dto in your async service, it is the same as you use there all of them (so the only reasonable solutions is to have only one complex async service, but it is not reasonable anyway)

  * Based on this facts we understod that the best practice is to use DTOs in the RPC layers, but we didn't want to use gilead anymore (it took us a lot of time and effort and it didn't offers us a proper solutions for our problems we had)

So, we decided to develop own annotation processors that will:
  * create DTO's from domain objects easily in a few seconds - DTOs should be related to your domain entities that's why it's good way to start here. We would like to say which fields are required in the UI and which should be skipped, also we would like to define different transformations from/to domain objects (ideally using the annotations)
  * create converter from DTO to domain object instance and back - from domain object into the DTO instance (same configuration from previous step should be reuse)
  * create "Local" service interface pair to the "Remote" service interface - Remote service interface uses DTOs in the method definitions, Local service interface uses domain object pair (So you will just write remote interface, and local version of the interface will be automatically generated by processor and stays in sync with remote version - this can avoid many stupid mistakes and typos)
  * create transparent layer that will handle RPC method calls using DTO's to the service implementation methods used domain objects and back (this should be done using converters defined in the step 2)
  * export local service, wrapped into the transparent conversion layer to the specific public URL that will be mapped for the remote service and will handle requests
  * create async version for the remote service interface in the automatic way (this is nice to have feature :-) )
  * and what is the most important - customization! - if someone want to use of converter, it should be achieved in easy way, if someone want to use own DTO (not generated one), it should be also achieved easily, etc - in the nutshell: there should be a way how to easily replace everything that is generated by a custom implementation.
  * if should NOT be a golden hammer, it should be strictly tight to the GWT world and nothing more.

# The DTO's #

The TransferObjectProcessor is responsible for the DTO's generation from the domain entity, based on the configuration.

@TransferObjectMapping(domainClass = Customer.class) @Mapping(MappingType.EXPLICIT) public interface CustomerDtoConfiguration {

void id();

void ico();
void icDph();
void contact();
void person();
void address();
void country();
void company();
void accountNumber();

}


TransferObjectMapping is the starter annotation for the TransferObjectProcessor and defines that DTO will be created from Customer domain entity `@TransferObjectMapping(domainClass = Customer.class)`
The next you have to do is to define mapping type

# Summary #

| **Processor** | **Project** | **Activation** | **Purpose** |
|:--------------|:------------|:---------------|:------------|
| TransferObjectProcessor 	| sesam-model-domain-processor | by sk.seges.sesam.pap.model.annotation.TransferObjectMapping annotation | Generate DTO from domain object based on the configuration |
|               |             |
Clone this wiki locally