[转载]
In my previous blog post I found several reasons for using DTOs instead of using the domain model classes as transfer objects. I received some feedback and several people were saying that DTOs are a thing of the past and should be now considered an anti-pattern.
My main argument in favor of using DTOs was that they provide protection for the domain model and facilitate easier refactoring. The domain model needs to be protected from getting polluted with presentation related concerns. Refactoring would be easier if you could change the domain model classes without the client code being affected. This would be possible if the client was not dependent on the domain model classes directly. When using DTOs the dependency is not direct, the DTO classes provide some isolation. Effective refactoring is necessary in order to achieve a working domain model and not end up with an anemic one.
In this post I will discuss some other ways to protect the domain model and to facilitate effective refactoring. This time I am assuming that domain model classes are exposed to clients and DTOs are not used.
Avoid Anemic Setters
I learned the term "anemic setter" from the Spring forum's architecture discussions. An anemic setter is a setter that uses a primitive (or a String or some other JDK type) as it's parameter type and assigns the parameter value value to the domain object's field. In some cases anemic setters should be avoided. For example if your Customer object stores an address it probably should not expose setters for individually assigning the street address, the zip code and the country. Instead the address should be modeled as a separate Address object and the Customer should have a setter that takes the Address as a parameter. Or alternatively, the Address could be specified when the Customer is created. The latter is probably better if every Customer must have a valid Address. The Address class must also enforce that all it's component values are provided when instances of it are created.
This arrangement guarantees that the Customer state is never corrupted. A buggy or malicious client is not able to assign a partial address consisting of, for example, only the zip code. The anemic setters design relies that all clients, in all situations, use all three different setters when storing or modifying addresses.
The lack of anemic setters places some challenges for the data binding solution. A primitive binding framework might require that all fields coming from the UI (HTTP request or a Swing form) must be stored using anemic setters.
Domain Logic Only
The domain model should contain only logic from the core domain. Presentation logic should never find it's way to the domain model classes. So what is the proper place for presentation related functionality then? Here are two options:
- Create separate model classes (MVC models, form objects)
Unusable Methods with a Distributed Environment
I discussed unusable methods in my previous blog entry. I haven't figured out how to avoid them. AOP magic comes to my mind again here... Does anyone have suggestions?
Evolving the Architecture
My recipe for success follows here.
Start simple and use the domain model throughout the the system, including the presentation and other types of clients. This way you will be enjoying the benefits of the domain model everywhere. Your client code will appear to be speaking the domain specific language.
After this you need to keep your eyes open all the time. Look at your domain model:
- Is it alive and rich with domain logic?
- Has the domain logic leaked out of the domain model?
- Has non-core functionality leaked into the domain model?
- Is refactoring painful and hurting domain model evolution because you are worrying about breaking hard-to-change client code?
If these kind of signs are visible, it is time to refactor. Maybe some additional UI specific helper classes or model classes are necessary to host the presentation logic that was leaked into the domain classes. Or maybe you even
need to introduce command classes (or DTOs) to isolate the domain model in some troubled area. You need to be alert all the time to be able to react quickly. Even better, you can refactor the architecture before adding anything that would lead to a problematic design.、
我认为也不必过于死板的非得隔离domain model曾。model仅仅是一个数据载体,一般人不会在其中加入logic。但是有些时候persistent层的model要的数据和domain层要得数据不一致,我看到有些人在domain层的model中加入一些东西,这样这个model即可以用在persistent层也可以用在domain层。这样是很方便,但是破坏了结构。在这种情况下,不如在建立一个persistent层的model用户装载数据。当然如果domain层的model在不做改变的情况下就可以被persisitent层使用,那也不一定非得建立一个一模一样的persistent层的model(当然如果你愿意,我也不拦你)。