I handled a lot of the design-related material in _Bitter EJB_. One of the things that always bugged me about entity beans is the fact that they inhibit true OO domain models (at least I thought they did). Other than that, I think they're a perfectly viable persistence framework. An OO domain model is a must for an application with almost any degree of business logic.
My last project was 80% complete when I joined and they were already using entity beans. It wouldn't have been prudent to start using something else, so I made them work, and I was quite happy with the results.
I started out using the design proposed in the TSS article on the two layer domain model (
http://www.theserverside.com/resources/article.jsp?l=TwoLevelDomainModel). They suggest having a pure implementation class with your business logic and abstract getters/setters and then extending that implementation class with your entity bean implementation. This is to enable more testability, better abstraction, etc.
I ran into a couple of problems. One, I still couldn't easily swap out my persistence implementation for something else (i.e. Hibernate, a transient implementation, etc.). The "this" references posed a problem. In the case of a Hibernate implementation, I'd want to use the actual "this" reference. In the case of the entity bean implementation, I'd need to look up the client reference and use that. I could get around this by implementing a getThis() method in each of my business object, but that just seemed kludgy.
Second, I still couldn't have polymorphism. As as I said, this is a must for maintainability. Third, there's the problem with reentrance, but this only becomes an issue if you're running multiple threads within the same transaction (I don't even know if this is possible in a pure J2EE context).
Long story short, I opted for a 3 level domain model where I wrapped the entity beans. I still had the first layer, the implementation class. Next, I had entity wrappers that extended my implementation class and delegated to entity beans. This solved the "this" reference problems and allowed for polymorphism, but how do we handled the wrapping/unwrapping code? We needed to wrap the entity beans on the way out (i.e. from finders in our DAO, and from relationships in our entity wrappers). We needed to unwrap them on the way in (i.e. before we set an entity bean relationship in our entity wrappers).
Unwrapping was easy. Each wrapper simply implemented an Unwrappable interface that returned the wrapped entity bean. Wrapping would be the hard part. Did I need to strewn wrapping with if/else blocks throughout my wrapper code, writing custom Collections wrappers for each multiple relationship? Nope. Using the Visitor pattern, I isolated all of the wrapping code into a simple WrappingVisitor class and I also removed the cyclic dependency between my entity wrapper and entity bean layers at the same time.
Basically, each entity bean was visitable. To wrap it, you simply needed to visit it with the WrappingVisitor. The WrappingVisitor simply implemented a visit method for each entity bean type that would return the wrapped version. All of the polymorphic code went here. For example, a visitor method could return a different subclass based on a field value in the entity bean. As an added bonus, I only needed one Collection wrapper for the entire application. The generic wrapper would simply wrap the collections returned by CMR relationships, wrap entity beans on the way out (from the get() method for example), and unwrap them on the way in.
As an added bonus, I employed code generation in my domain object layer. Using qdox, I wrote a utility that would autogenerate the interfaces and plain bean implementations of each domain type. You determined what methods to include in the public interface using an @interface tag in the implementation class.
I used the bean implementations in a transient DAO implementation that could be swapped out for the entity bean DAO implementation. The transient, or in-memory DAO was useful for preliminary functional testing and deploying the application faster (i.e. you didn't have to worry about cleaning up the database or deploying the entity beans). I actually developed almost the entire application against this in-memory DAO, not having to worry about entity beans once. A coworker rolled the entity bean layer in a day and we had no problems. It was truly awesome.
Now, I had a truly OO domain layer for which the persistence layer was 100% abstracted away. Rolling a Hibernate DAO would have been a snap as I was already autogenerating the plain bean implementation classes. I can also say that this work paid off in maintainability and predictability as I brought the application in on time. What do you think?