Posted by srikanth
on July 1, 2010 at 12:08 PM PDT
Effective JavaFX architecture is not an easy task. Designing testable JavaFX applications that are beautiful inside-out requires serious thought and some experience. Or you could read this blog series :-)
In spite of the cool animation and glamour power of JavaFX, the largest usage of JavaFX will be for building “boring” enterprise software combined with some visualization. There is a dearth of resources exploring architecture options for building serious and large applications using JavaFX. This blog post is an attempt towards addressing that gap.
An enterprise JavaFX project can be a complex beast. No two projects are alike. You might have to build a brand new system, integrate with an existing system (partially or completely) or deal with project specific or company specific constraints. Hence there is no single architecture that satisfies all the constraints. However, some of the common criteria for architecture and design of JavaFX application are as follows:
- Simplicity of architecture
- Usage of proven design patterns
- Less coding, Less maintenance
- Domain Driven Design (where possible)
- Ease of Unit Testing
Some patterns lend very well to the above criteria and I will use those patterns and principles for the design and end-to-end architecture useful in fairly large JavaFX application.
Front End architectural principles
The best place to start is obviously the actual front end itself. Pure Java UIs have the following option in this area.
- Obviously there is the Swing Application Framework (JSR 296).
- There are other options such as Netbeans RCP, Spring RCP, Eclipse RCP frameworks and can be combined well with a dose of OSGi for extensions.
If I were to implement a moderately large JavaFX project none of these models suit my need well.
- The former (Swing Application Framework) is too high level. A high level application framework is a good start, but JavaFX in RIA mode has its lifecycle cut out. There is always the creation of Stage, Scene and then displaying it. There isn’t much scope for enhancing this. So this is not a feasible solution.
- On the other extreme, we have the low level RCP style frameworks. Developing a JavaFX app in RCP style today is not possible because such a framework does not exist. In addition, it forces you to implement the UI in a certain manner which may not be suitable for every scenario.
Typical JavaFX Node Lifecycle
Given this, a middle ground is my preferred solution i.e. to capture commonalities in the behavior across pages (Node in JavaFX parlance. From here on, I will refer to these individual JavaFX pages as JavaFX node) into a framework. The boilerplate code that goes into every JavaFX node could thus be captured in one place that controls the lifecycle of the JavaFX node. Any good JavaFX architecture does so by capturing these fairly generic lifecycle phases:
- User enters some data and submits
- UI level Validation occurs
- Domain objects are constructed and validated again if necessary
- A request is built. Data in appropriate from the UI is converted to a form appropriate for transmission
- Request is sent and wait for response synchronously or asynchronously (based on our choice of communication mechanism)
- In both cases, we might get actual response, or exception in some format (500 error, 203 error, authorization error etc.). If the request was asynchronous, then the data from response received has to be set into the UI (same page or different one) in an appropriate manner. If the data has to be set into another UI, some kind of transition to another page should also occur.
- Server requests generally should be asynchronous and the user should be provided with a mechanism to cancel if it takes too long
- In addition, if the user signals aborting a particular action mid stream, there should be option to veto (dirty checking) by other components (For instance, “You have made changes. Do you really want to move away from this page?” etc.)
Now that we know what the general lifecycle should be, the question arises – Where should this be captured – View or Controller (if there is such a thing as Controller). I will address that next.
Model-View-Controller or Model-View-Presenter
MVC and its variants are the defacto-kings of web frontend implementations. However it is the Model-View-Presenter that lends itself well to rich UI development. The nice thing about it is that the MVP style UI is that it separates all the aspects of event handling from the UI into the Presenter class itself. This makes it easier to unit test the UI in isolation (in combination with mock objects if needed).
There are two variants of MVP – Supervising Controller and Passive View (Check these links to read them from Martin Fowler’s essays - martinfowler.com/eaaDev/SupervisingPresenter.html and http://martinfowler.com/eaaDev/PassiveScreen.html ).
If the data binding worked well with JavaFX, perhaps Supervising-Controller style would have been a good fit. But since the opposite is true (as noted in my earlier blog - http://weblogs.java.net/blog/srikanth/archive/2010/06/21/javafx-bind-%E2%80%93-too-much-hype ), it is the Passive-View MVP that best fits JavaFX. A typical division of responsibilities among participants in a Passive-View MVP implementation in JavaFX is provided below (for the JavaFX node lifecycle mentioned earlier):
- Every JavaFX node is implemented as a subclass of CustomNode.
- Every such subclass will hold reference to a NodePresenter. (A question arises how will a node get reference to a node presenter. The answer is Dependency injection of course. I have personally used Spring for wiring JavaFX objects on the client side itself. Guice could perhaps be also used - http://weblogs.java.net/blog/srikanth/archive/2010/06/12/wiring-javafx-objects-spring-tread-caution )
- It is the NodePresenter that is notified on every event in the UI.
- NodePresenter then starts the lifecycle on the JavaFX node. It connects to the server, using other helper classes and waits for the response.
- After the response is received, it passes control to another NodePresenter (and not to another JavaFX Node), also passes any data and asks the next NodePresenter to take over. (Indeed the view is passive ?)
Why is MVP style JavaFX UI test friendly?
You can use JUnit to test JavaFX. If you have been previously been disappointed at how JUnit cannot test a JavaFX UI, then adopt this MVP approach and soon you will find that the JavaFX UI can be unit testable. (JFxtras also comes with a JUnit extended framework for unit testing. Take a look at it - jfxtras.org )
- Notice that the UI need not be completed to start testing it. All it needs is appropriate fields. The unit test could just instantiate the node, set the data, and then could call doOnSave by itself and let the NodePresenter do the rest of the stuff. At the end of it, the test case can assert that the data is set into the current node or some other node.
- Similarly the code to connect to the back end need not be present to unit test this piece. You could either stub or provide mock expectations for the back end code and the mock expectations work against the presenter, once again letting the developer to assert the data in the node widgets
A typical Passive View style implementation of in JavaFX is slightly different than one might see in GWT or elsewhere. For instance, in GWT, the UI contract is captured in Java interface and the UI widgets also provide an interface making it easy to stub the entire UI. Such a facility does not exist in JavaFX.
A JavaFX Ui is never going to be plain vanilla. It might be even a composite of multiple nodes. A change in one node of a UI might need to trigger changes in another node in another part of the UI. The JavaFX bind syntax is not a good candidate as it tightly ties the source and the target. Hence there is a need to fire up Application Events so that other nodes in a composite UI can listen for changes in the underlying model and react accordingly. Note that in standard Java, this is implemented using PropertyChangeListeners etc. However one never has control over the backend might be designed and the equivalent could be achieved by firing Application Events.
Not all events to be fired are Application Events. An Application Event is something has a significant business meaning. Smaller changes could be fired as Command Event and State Event. A command event indicates that something has been triggered in the application. A state event indicates something has changed in the application. Use these events accordingly. Event LIsteners could be implemented the standard Java interface way or you could take advantage of JavaFX mixins to achieve this.
I am not sure if this term controller chain has been defined in industry literature, but I sure feel the need for it. In a small application, one could have few JavaFX nodes and a few Node Presenters. As the application grows, there is a need to group the Nodes into modules and modules into application. In such a scenario, we need to have controllers for module and the entire application itself. Notice that I referred to these as controllers and not presenters. That is because presenters are micro managers and controllers are hands-off managers. At the Node level there is need for micro management. But at the module or application level, such a need ceases to exist. But nevertheless, there needs to be a hierarchy in the chain of controllers (hence the term controller chain) with some ownership relation between them.
Advantages of having controller chain
- When JavaFX nodes need to transfer control to another node, ultimately the change in scene has to be effected at the Stage level. With a single level hierarchy of presenters/controllers, soon every node presenter starts “talking” to every other node presenter and ultimately get linked to the Stage somehow. This is definitely not desirable. Using bound functions to trigger the change of node from one node presenter to its module controller and then a lookup at the application controller level will be a better design
- Modules can be cohesively designed and the application can consist of loosely coupled modules
- The top level Application Controller can communicate with Stage and decide who should be the default node/module during startup
- ACLs can be assigned at the module controller level (In a classic Java EE application, the model is role based. When the user clicks on a link, a decision is made whether or not to allow access to the link. However it is better to design rich UI on a ACL based authorization model)
- Allows the application to grow bigger with the evolving pains. Modules are transparent to each other
NOTE: It is easy to confuse whether OSGi could also be used as an alternative for controller chain, but it is actually complementary to the controller chain.
An event bus is not an absolute necessity, but if a JavaFX app really grows so big that using listeners across modules results in tight coupling, then event bus should come into the picture. Otherwise it is just a over engineering.
This blog post covered a lot of basics of UI design and architecture as it relates to JavaFX. JavaFX comes with its own quirks and the architecture and design has to adapt to that. I have not gotten into a lot of dirty details. But I will soon provide a reference implementation covering all of the above points I discussed, so stay tuned. In addition, I will also cover asynchronous communication and data exchange mechanisms, the EDT in JavaFX, security considerations and testing (including testing async calls) in more detail in the coming installments. Until then, follow these rules to churn out elegant and testable JavaFX applications !!