Development teams collaborating on the same codebase are always faced with the challenge of keeping their source code maintainable as it evolves and grows over time. By choosing GWT to implement your web application you already gain the benefit of having IDE support for client-side code refactorings. This is a distinct advantage, but how should a GWT application be designed to allow for the collaboration of large teams and stay maintainable over the course of time?
The Model-View-Presenter (MVP) pattern provides an answer to this. Applying the MVP pattern in GWT applications is described in this two-part article: Large scale application development and MVP
In this post, I will demonstrate a modified version of the example code of this article series to show how Errai makes implementing a MVP application in GWT even easier and how Errai helps to further decouple the individual components. The source code of the modified example can be found on GitHub: https://github.com/csadilek/errai-mvp-demo
So, let's take a look at the individual components used in MVP and the modifications and improvements made to it using Errai.
The business objects used in this example (Contact and ContactDetail) remain more or less unchanged. The @Portable annotation was added to mark them as serializable by Errai. Errai marshalling was designed for maximum flexibility and to impose as few limitations to serializable classes as possible (see our documentation for details). The classes do not need to (but may of course) implement java.io.Serializable.
The views contain only UI layout code. They have no knowledge of the model, other views, transitions and contain no application logic. Errai can improve view implementations by providing the ability to inject UiBinders.
The presenters contain all the application logic: view transitions, RPCs, etc.
In the original example, the RPC proxies, the event bus and the view instances are passed to the presenters as constructor arguments. Using Errai, we can now inject all these components.
This might seem like a minor improvement at first, but once your application grows this will become more than handy. Let's say you come to a point where all your presenters need an additional RPC proxy. Without dependency injection, you would have to change all constructors and all call sites!
The GWT/RPC proxy was replaced with an Errai RPC proxy (the injected Caller). Errai RPC does not require an asynchronous interface nor a servlet based service implementation on the server. It can even be used to invoke methods on a server-side CDI bean (see our documentation for details).
The Application Controller
The application controller handles logic that is not specific to any presenter and also deals with history management and view transitions. It needs access to the event bus (which again we can inject using Errai IOC) to react on application events that trigger view transitions.
It also needs to be able to create new presenter instances when the browser's history stack changes. To create new managed instances of presenters, we use Errai's client-side bean manager that will make sure all the injection points of presenters are satisfied and the instance is ready to use.
Since Errai allows us to use dependency injection for all the required components, the main difference here is that we no longer have to pass the RPC proxies and the event bus instance to the app controller which in turn would have to forward it to the presenters and views.
The @Produces method provides the event bus instance to all the event bus injection points in all the presenters. The application controller itself is also just injected and started as soon as the app is finished initializing (@AfterInitialization).
So to sum it up, Errai's IOC container helps implementing the MVP pattern by further decoupling the individual components and by alleviating boilerplate code for UiBinders and RPC proxies.
An extension that we are currently working on is the @MockFor annotation that can be used to replace an injected type with a mocked type for unit testing. During tests, a presenter could then get the mocked view injected in place for the actual view for example. The presenters can then be tested using a plain JUnit test instead of an integration test.
As always, feedback is welcome and appreciated!