Saturday, May 12, 2012

Value Objects + microinterfaces + generics

I guess readers are already aware of reasons why separation of application layers has positive impact on project's architecture. For more-than-simple application we usually want to ensure that any change in entity will not broke our webservices or web interfaces. And we want to be warned in compile time if there is any mismatch between what is behind other layer's boundary. That is why we introduce Value Objects, DTOs etc. - not going into details simply beans. This pattern has been recently used in my project. Let's assume we have an entity:
@Entity
class Product { 
 private Integer id; 
 private String name;
 private String description; 
// and some other stuff...

// and all setters and getters
}
and we have also corresponding bean, related to some limited functionality:
class ProductBean {
 private Integer id;
 private String name;
 private String description;
// basically same what is in entity
// but in future we expect that this will no longer be true
}
I have created some procedures that work on our beans:
class Operation { 
 private ProductBean product;
 
 public SomeResult operate() {
  //this can be some operation with lots of invocations of bean's properties
 }

}
Now my architect comes and says: 'No, it is logic layer, you should use actual entities, not beans for this operation'. But in other functions I will still be using beans.
What to do?
There are some options.
  • Use copy and paste, have same code for every combination of entity and DTO. Your manager is happy until first change is made to the functionality. You know it today.
  • Use reflection based stuff. Like BeanUtils from Apache Commons. Or use some dynamic language. But then you loose static type checking. You better have good test coverage or you may like surprises. Compile time error detection is valuable.
  • Go agile. Use entity everywhere. Result as in previous point. And perhaps your customer has not paid for changing 30 vies where you use Product?
  • Introduce common interface for entity and beans. This will asure static type checking. But then your entities and beans are coupled. If your logic depends on interface you will end up changing both kind of objects. Or worse: you will have interfaces like Product_V_8_6_1.
After taking above stuff into consideration, I decided to use some other approach.
Let's introduce specialized small interfaces like:
interface HasId {
 Integer getId();
 void setId(Integer id);
}

interface HasName {
 String getName();
 void setName(String name);
}
and so on... used per property.
My operation changes then to:
class Operation <PROD extends HasId & HasName & HasDescription > {
 private PROD product;
 
 public SomeResult operate() {
  //with no changes - I use entities, beans with no distinction now
 }
Now I have type safe generic operations not depending on specific type. If I introduce compatible change to my entity I may or may not touch my bean and I have previous application logic unchanged. With zero effort.
I depend only on properties not on owning them classes. And thanks to generics I still operate just like on types.

No comments:

Post a Comment