Friday, April 26, 2002 10:22 PM
A lot of effort has been spent trying to design systems with elegant architectures that pass data effortlessly from client to server to database and back up through to the client again, with designers and framework providers frequently referring to the MVC pattern and how helpful it is in this endeavor. As it turns out, transaction semantics severely complicate use of the "Model View Controller" (MVC) pattern and the current Enterprise Java Beans (EJB) application framework conspires to make use of MVC even more difficult for thick clients. This may suprise the average EJB designer, but I believe this is due to a misunderstanding of what the MVC pattern actually is, especially when applied to non-web clients. Much of the discussion of the
A lot of effort has been spent trying to design systems with elegant architectures that pass data effortlessly from client to server to database and back up through to the client again, with designers and framework providers frequently referring to the MVC pattern and how helpful it is in this endeavor. As it turns out, transaction semantics severely complicate use of the "Model View Controller" (MVC) pattern and the current Enterprise Java Beans (EJB) application framework conspires to make use of MVC even more difficult for thick clients.
This may suprise the average EJB designer, but I believe this is due to a misunderstanding of what the MVC pattern actually is, especially when applied to non-web clients. Much of the discussion of the pattern on the internet and in Java communities appears to have centered on the structural aspects of the pattern, completely avoiding the behavioral aspects of the pattern. For this reason, I'll provide a brief refresher of the MVC pattern and then proceed to explain why this crucial pattern is made nearly impossible to implement in EJB.
A discussion of a pattern usually begins with a discussion of a problem that needs solving. In this case, the problem being solved is that of maintaining consistency between objects that have similar dependencies. Or: several clients talk to the same server with the same database behind it. When one client (view) changes some of the data on the server (model) other clients will need to know about the changes so that they can avoid displaying stale data. By mediating client (view) change requests to the system (model) through a controller, other clients looking at the same data can be made aware of the changes without needing for them to periodically poll the system (model). The controller accepts the change request, alters the system (model), and then sends event messages to the other clients so that they have the opportunity to alter their presentation accordingly.
Neat solution to a common problem. Hallmarks of a good pattern. In the world of thin clients and web browsers, these behavioral aspects of the pattern are often ignored, as an interface in a web browser can be thought of as taking snapshots of the system. Users don't expect changes to be updated to them unless they click the "reload" button or go to another page. That simplification of the problem is great for web developers, but let's not forget that there are lots of "thick client" applications where snapshots of the model aren't good enough and where the MVC pattern provides much larger dividends.
Now let's add transactions and we'll see usage of the pattern begin to get messy. The first problem is that you can't notify other clients of the changes until after the transaction has successfully completed. You can't tell a client, "I'm changing value 'count' to 2." until after the change has been signed, sealed and delivered to the server; and until the transaction has completed,
the change hasn't happened yet.
In addition, it will probably be difficult to determine precisely what was changed after the transaction completes, so the controller isn't nearly as clean to implement. There has to be some way of either setting aside all of the change information during the transaction managed code for the controller to send to the other views after the transaction or finding it by examining some framework resource. These two issues imply that having consistent thick clients within a transaction managed environment will likely come at a cost.
Now for the problems with EJB and MVC. The first blow that EJB delivers to MVC is that transaction visibility is 1) tied to the bean and 2) limited to one bean type: the Stateful Session Bean (SFSB). Entity beans (local and otherwise) and stateless session beans (including message driven beans) do not provide any equivalent to the SessionSynchronized interface of SFSB's so that you can run code on all transaction boundaries. All requests to an EJB based application that wishes to maintain consistent clients must come through SFSB's. Since a message accepted through a message driven bean interface can certainly start a transaction, change data on the server, and complete the transaction, this limitation makes no sense and prevents access to much of the EJB spec for thick client applications.
The second issue, keeping track of what has changed during the transaction, is even worse in the EJB spec and API's. The types of events that you need to notify other clients of are Create, Update, and Delete of data or persisted objects in the system. However, there is no interface to allow callbacks from these requests that would permit user code to localize this code for all entity beans in the system. At best, you have to be aware of all of the places where your objects can be created, deleted, or altered, and insert code to register those changes with your controller. A messy and fragile set of widely spread out code to maintain.
Other object persistence systems have similar problems. No consideration appears to have been given in JDO, for instance, to the importance of being able to find the set of new, changed, and deleted objects within the current transaction. Surely, all transaction managed persisted object frameworks maintain this information, but not one provides an interface to access it.
The changes that would have to be made to correct this problem appear to be fairly straightforward. First, add transaction interaction to the transaction API. Application code should be able to register a transaction helper and expect callbacks to hit that object's methods when appropriate. Second, expand the API of object persistence frameworks (Entity Beans, JDO, etc.) to allow detailed examination of the activity within the transaction by application code. Either allow the application to examine the transaction object itself or allow the insertion of application code around object lifecycle events, which will allow the set of those events to be accumulated during the transaction for later propagation.
My question (there really is one) is: have I missed something important, or is this thesis right, and if it's right, what are some workarounds that you could suggest for the second problem when using JDO objects accessed via an EJB interface?