I have a design question in the fight about domain driven design, the anemic domain model,
and the way EJB/JPA force us to do things.
Consider a web-mail application which is built using JPA for persistence and EJB for the
services layer. Let's say we have a service method in our EJB like this:
| public void incomingMail(String destination, Message message) {
| Mailbox mb = findMailBox(destination); // who cares how this works
| mb.addMessage(message);
| }
|
This is seemingly a reasonable business method. Presumably, the Mailbox object will still
be attached and it will seamlessly save the changes back to the database. After all, that
is the promise of transparent persistence.
The Mailbox object would have this method:
| public void addMessage(Message message) {
| messages.add(message);
| }
|
Here's where it gets complicated -- assume we want to have other mailbox types. Say we
have an AutoRespondingMailbox which automatically responds to the sender, and a
HelpDeskMailbox which automatically opens a helpdesk ticket with each email received.
The natural thing to do would be to extend Mailbox, where AutoRespondingMailbox has this
method:
| public void addMessage(Message message) {
| String response = getAutoResponse();
| // do something magic here to send the response automatically
| }
|
The problem is that our Maibox object and it's subclasses are "domain
objects" (and in this example, also JPA entities). The Hibernate guys (and many
others) preach a non-dependent domain model -- that is, a domain model that does not
depend on container/runtime provided services. The issue with such a model is that the
AutoRespndingMailbox.addMessage() method cannot send an email because it can't access,
for example, JavaMail.
The exact same issue would occur with HelpDeskMailbox, as it could not access WebServices
or JNDI injection to communicate with the HelpDesk system.
So you're forced to put this functionality in the service layer, like this:
| public void incomingMail(String destination, Message message) {
| Mailbox mb = findMailBox(destination); // who cares how this works
| if (mb instanceof AutoRespondingMailbox) {
| String response = ((AutoRespondingMailbox)mb).getAutoResponse();
| // now we can access the container services to send the mail
| } else if (mb instanceof HelpDeskMailbox) {
| // ...
| } else {
| mb.addMessage(message);
| }
| }
|
Having to use instanceof in that way is the first sign of a problem. Having to modify this
service class each time you want to subclass Mailbox is another sign of a problem.
Does anyone have best practices for how these situations are handled? Some would say that
the Mailbox object should have access to the container services, and this can be done with
some fudging, but it's definitely fighting the intended usage of JPA to do that, as
the container provides dependency injection everywhere except in Entities, clearly
indicating that this isn't an expected use case.
So, what are we expected to do instead? Liter-up our service methods and give-up
polymorphism? Our objects automatically become relegated to C-style structs and we lose
most of the benefit of OO.
The Hibernate team would say that we should split our business logic between the domain
layer and the service layer, putting all of the logic that's not dependent on the
container into the domain entities, and putting all the logic that is dependent on the
container into the services layer. I can accept that, if someone can give me an example of
how to do that without having to completely give-up polymorphism and resorting to
instanceof and other such nastiness.
View the original post :
http://www.jboss.org/index.html?module=bb&op=viewtopic&p=4257740#...
Reply to the post :
http://www.jboss.org/index.html?module=bb&op=posting&mode=reply&a...