22 February 2008 - 3:16DTOs are not rich beans
Of all the problems of Data Transfer Objects (DTOs) the one that stands out is the anemic domain problem: DTOs are basically objects with a few variables and getters/setters for them, lacking any complex behavior. This way of developing applications has been decried times and times again so I will not waste my time exposing this problem.
However, I started realizing that maybe DTOs should not contain any complex behavior and that they should be as dumb as dirt, especially when being used for communication between multiple systems. Consider this example: we have a tax system which uses the object TaxInformation which various systems use for getting information about taxes. Let`s say that the commodity trades system, the equity trades system and the FX trades system are all using it. Let`s say that this object looks like this:
public class TaxInformation{
private Trade trade;
private TaxReceipt taxReceipt;
public TaxInformation(Trade trade){
this.trade = trade;
this.taxReceipt = new TaxReceipt();
}
public TaxReceipt getTaxReceipt(){
return taxReceipt;
}
}
As you can see a DTO dumber than dirt. Let`s create an endpoint creating and serving such an object:
public class TaxService{
public TaxInformation getTaxInformation(Trade trade){
TaxInformation taxInfo = new TaxInformation(trade);
return taxInfo;
}
}
All is fine, but a requirement comes in which says that if the trade is restricted then the tax receipt of the tax information object related to that trade should also be restricted. Here you are presented with a few choices:
1) Implement this requirement in the TaxInformation object probably in the constructor.
2) Implement this requirement in the TaxService object.
3) Implement this requirement in the TaxReceipt object, in its constructor. For the sake of argument, let`s assume that we do not have this possibility, so I turned it off.
OOP purists will immediatelly recommend choice #1, because this would turn the DTO into a rich bean and would do away with the anemic DTO. This is a mistake, because all the systems using this object for communicating with the tax system will start having class versioning errors unless they are updated with this DTO`s new class. The problem is versioning such a DTO. Putting complex behavior in a DTO raises the risks of change in the DTO, which raises the effort of propagating this change in the systems which use this DTO for communication. At one point it makes sense to drop DTOs and use some sort of protocol for communication, preferrably a protocol which deals with change pretty easily (XML documents with schemas which always get extended and with constraints which get changed very rarely are a pretty good fit).
DTOs should probably be used in systems whose lifecycles are kept in synch (i.e. they get upgraded in synch), in such systems you can propagate type changes pretty easily because you have a certain amount of control over their lifecycles (*). Expanding on this, I would say that typed languages are probably most effective locally because type changes cannot be propagated over large distances efficiently. Type-coupling (or API coupling) is a very hard coupling and its should be avoided and replaced with protocols. DTOs do create an anemic model and should be avoided if possible, but if you choose types for communication between systems it would make sense to keep these DTOs to their most basic function of passing data around or to have procedures for propagating type changes in all the systems using types for communication.
Sometimes weak DTOs are actually a good thing…
* BTW, whether a set of systems whose lifecycles are kept in synch is actually one big system with one particular lifecycle is a pretty good question. I would say that you can think about it as a big system.
No Comments | Tags: Development