views:

177

answers:

3

This is a design pattern question, so I'll illustrate it with a simple example of an address book app.

Firstly a few assumptions. 1. It appears to be acceptable to directly use DB domain objects as the backing store for Spring MVC forms.

Iteration 1 of my app I created a JPA mapped Person object with various attributes attached. Using the DAO pattern I created a persistence object which can getAll, store and delete people from the database. In addition I have a factory method, create, so I can get a person object. Using this DAO object I create a simple web front end. All is good.

In iteration 2 I need to support multiple storage types, so I create an interface for person, which has multiple implementations, and an interface for the DAO persistence, again with multiple implementations. Also, person was extended to be able to have multiple addresses.

interface IPerson {
    public String getName();
    public List<IAddress> getAddresses();
}

But, when it comes to updating the web interface to be able to deal with these multiple implementations I have an issue. The persistence implementation is injected by Spring. And, because that persistence object has a factory method I am all good for creating the IPerson implementation. But, if I want to do something fancy like allow multiple addresses be submitted as part of the one request then I have an issue. To allow this to work with Spring you seem to need to use an AutoPopulatingList, so spring can just .get(#) the record an copy the attributes in.

So, one solution to making this work is to require all persistence implementations use an autopopulating list, and create the correct implementation for all child classes. Is this appropriate, given that we'd need to apply this @PostLoad with JPA as the base lists get replaced by Hibernate.

The alternative is to not make any assumptions about the implementation passed into the persistence implementation and convert/copy the objects across to the appropriate type. This looks better, as then the Domain object are kept simple, and all the storage complexity is in the DAO. In this case we'd use a Default* implementation of the IPerson and IAddress interfaces.

Even though I like the second option better, I am not necessarily comfortable with this situation. Can anyone offer any insights or advice?

+1  A: 

I usually don't have interfaces on model objects like Person and Address. I wonder why you've decided to go that way?

I can't think of multiple implementations for each. If your IPerson has implementations like Mother, Sister, Friend, etc., I'd recommend going with a role-based design instead. Just because the phrase "Jean is a Mother" rolls off the tongue so nicely doesn't mean that you are wise to base your design on IS-A and inheritance.

Address can be a variable thing as you move between countries, so that might be more reasonable, but you'll have a very hard time coming up with a single interface that will suit for both the US and Japan.

In iteration 2 I need to support multiple storage types,

Can you clarify this? What does "multiple storage types" mean? DAOs that use relational datbaases, file system, object databases, etc.?

so I create an interface for person, which has multiple implementations,

I don't see how the previous requirement makes this necessary.

and an interface for the DAO persistence, again with multiple implementations.

This is the usual Spring idiom.

I'd say that the web and person tiers should not have to worry about the persistence complexity. That's what the persistence interface is supposed to be hiding. Anything that makes it leak out into the other layers is doing its clients a disservice. I think I'd prefer your second option.

But my real recommendation would be to eliminate the IPerson interface unless you can provide an air tight reason for keeping it.

duffymo
Hi duffymo, to clarify the multiple storage types, currently I have a JPA and JSON formatted file as implementation. The JPA implementation has all the JPA annotations on its beans, and requires a reference (@ManyToOne) from the address to the person. The JSON implementation is far more vanilla. If I had a JAXB implementation it would be annotated like the JPA example. Thus, I can't really just have one implementation of Person. At best I could eliminate the interface, but still require the storage level objects inside the DAO.
FYI DataNucleus supports persistence to RDBMS, XML and many other things (using JPA or JDO). It supports JPA annotations to define persistence to XML (or along with JAXB annotations) - using JAXB.
DataNucleus
+2  A: 

The alternative is to not make any assumptions about the implementation passed into the persistence implementation and convert/copy the objects across to the appropriate type. This looks better, as then the Domain object are kept simple, and all the storage complexity is in the DAO.he alternative is to not make any assumptions about the implementation passed into the persistence implementation and convert/copy the objects across to the appropriate type.

This is the pattern I've followed with Spring MVC

  • A package of domain objects, which have no references to services/DAO code (think of this as your model)
  • The controller layer, services layer, and DAO layer operate on Domain objects
  • To handle form controllers, use a separate layer of "command" or "form" objects which model the data that the user is filling out in the form, not your domain objects. Users submit to a controller which binds the request to a "command"/"form" object, and your controller maps or converts these beans to your domain beans.

For example, you might have a pretty rich User object, but when new Users sign up, they only need to supply 2 or 3 fields. I would model this as a UserSignupCommand and the UserSignupController uses this as it's command class (not the User domain object). The controller is then responsible for taking the UserSignupCommand bean and either converting the data to a User bean or whatever other type of input your services layer requires.

I would not recommend using domain objects as the form backing objects because in most cases there is not a true matchup between "the object in my domain I am modeling" and "the data supplied by the user in a form".

matt b
On reflection I think, pragmatically, you are correct. As explained in another comment I've had issues trying to make the same domain objects work for all layers. I also shouldn't put an interface on the domain objects. My conclusion is to create new objects for a given layer when required. Now that I have multiple DAO options I require different data (mapping) objects for each technology to deal with their idiosyncrasies. The DAO layer accepts the domain objects and converts then as required. The penalty is lots of object factories/converters to be maintained/tested. Have I understood you?
You understand me, but I wouldn't complicate things by having a suite of objects PER layer. Usually I would have all layers operate on the domain objects; the special form/command objects are just for representing user input. It might be harder to comment on your specific use case (such as when you say "I have multiple DAO options") without knowing the specifics better.
matt b
+3  A: 

It is really nasty to have multiple sets of classes that embody the same business data just with different tweakage. Really nasty as in if I had to choose between something like ditching one of your ways of persisting, like JAXB, and having the multiple implentations, I would rather ditch the technology and find a better way to do it, because having all that mindless code is a major pain.

Alan Perlis said, "It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures." You especially want to avoid data structures that largely duplicate each other. Maintaining a redundant code base is no fun.

I suggest you check out Domain-Driven Design. The idea is you have domain objects that do not have any knowledge of how they are stored (this is called Persistence Ignorance). That way your domain objects can be POJOs, you usually don't need to put interfaces on them because they have no dependencies on anything in your application except for other domain objects. (There might be edge cases where it becomes useful but it would not be the normal case.) You can put your business logic (information about how your domain object relates to other domain objects) in the domain object without worrying about how to test it because there's no tie to the database. Also the domain objects can be passed around freely throughout your application because they don't have any dependencies on a specific layer. In terms of dependencies the domain layer is responsible to all the other parts of the app, but independent of them.

The upshot of all this is that domain logic and technical details of how objects are stored become separate concerns so each can be tested separately. Also you have a central data structure that holds all the business data in only one place so you don't have multiple changes to make when something changes.

There is a free web book available on Domain-Driven Design from http://www.infoq.com.

Nathan Hughes
I think this is what I wanted to achieve, but there are interoperability issues when it comes to using tools like an ORM for the persistence, or Web MVC for the interface. Specifically, I have a POJO domain object which has "parameters" in the form of a Map. JPA does not support using Maps, so I can't just map the POJOs using XML, I need a set of persistence objects for storage. Similarly, Web MVC doesn't support Maps with user supplied keys, so again I need lists of Key-value pairs. But if I change the Maps to lists in the domain objects the service layer is a pain. Do you find this tradeoff?
Yes, I can see where you have some hard trade-offs to make, I don't want to come across as not having respect for the problem you're having. Maps are a particularly hard case, I know Hibernate doesn't give them the same level of support as sets and lists. Maybe you can have multiple methods to access the same information, where you have an accessor method that returns a map but accesses another property that is a set or list and builds the map on the fly?
Nathan Hughes
I agree, maps are a hard case in certain situations. But they are also very powerful. My current approach is to provide map-like methods to manipulate the lists, which is working well. In fact, thinking about this more, I think the adapter approach which Plone 3 uses could be a powerful tool in this situation. That is, you have an interface for UI level controllers, and provide an adapter from the domain objects to that interface. Not novel, but it's the only place I've seen it extensively used. Cheers.
One suggestion is to add some "non-persistent" properties to your domain objects. These properties would only be used for mapping request parameters to your model. Your domain object can handle the conversion in a setter or constructor. Provided you are not adding to many additional fields, I've found this to be a viable trade-off. If however, your form backing object greatly differs from your domain object then I would create a separate form object.
BacMan