It’s been more than 3 years since Ray Ryan gave that seminal talk on how to use GWT in large applications. In the intervening years, the advice he gave has been underpinned with libraries and GWT infrastructure. GWT now offers built-in support for MVP, weaving in History management elegantly though the use of Places. GWT-dispatch provides a clean implementation of the command pattern. And GIN makes Dependency Injection a natural extension of the GWT paradigm and binds all the other approaches together into one seamless, sensible architecture.
This article isn’t going to make the case for doing things Ray Ryan’s way. I don’t think there’s any need to convince anyone. But I would like to point out some choices that emerge from developing complex applications using these patterns and make some suggestions about what these choices might mean.
But firstly, I’ll like to touch on one thing that Ray Ryan did not talk about much, and which is not covered in many of the GWT MVP articles I’ve read: the ‘M’ in MVP – the Model. The suggestion seems to be, from a reading of the standard literature, that the Model is just whatever DTO the Presenter happens to be holding onto at any given moment. It’s true that a model like this will serve you well in a great many scenarios. For example, if your MVP Triad is displaying a list of Users, then a List<UserDTO>, returned from a service call, is all you probably need to act on when coding the behaviour of this Triad. But there are other circumstances, when we will need to maintain some client-side state, and in this case, the DTO is just not enough. This shouldn’t surprise us. The DTO has its own specific role to play in the overall architecture. A good DTO should contain just enough data to allow the client to communicate efficiently with the server, while still displaying everything that the current MVP’s View wants to show. The DTO is typically a reduced blend of domain objects, assembled on the server side for use by that MVP Triad in particular, and passed forward and back in a way that reduces chattiness. It should never contain any information that represents client-side state.
Once we accept the need for a Model, even if a simple POJO, we are faced with the question about where to create and maintain this Model. If you’ve been using Ray Ryan’s architecture for even a short while, the answer is obvious: GIN. The central class in an MVP Triad is the Presenter. It should mediate between the Model and the View. The Presenter is also the Activity in the standard literature on Place management, and we use GIN to instantiate the Presenter/Activity when its corresponding Place is reached. By extension, the View and Model, which are linked by the Presenter, are also created through GIN, as they are injected into the Presenter’s constructor. All it takes is to add one little @Singleton annotation to the definition of a Model class, and suddenly we have a Model instance that can be created, maintained and presented automatically whenever required by its Presenter/Activity – even if that Presenter/Activity is instantiated afresh each time its Place is reached. This Model can hold any number of DTOs as well as the MVP’s own presentation state (e.g. a boolean to represent visibility of a collapsible part of the screen).
Now we have a singleton class that holds our presentation state without infecting our DTOs. But we quickly realise that we can take this singleton Model one step futher: we can share it with other MVP Triads.
The mechanism to share isn’t hard to understand – we simply add our Model class to the constructor of another Presenter/Activity. Harder to understand is why we would do this in the first place. Well sometimes this can be a very efficient way of sharing information between two cooperating Triads. Let’s go back to our example of a Triad to show a list of Users. Let’s say we want to be able to click in the list and move to an EditUser Place, with its own MVP Triad for editing the details of a particular User. Once the edit is finished, we’d like to go back to the list of Users and see the updated User in that list, preferably without performing an entirely new service request to retrieve the list. A shared Model instance could store the list of UserDTOs and the selected UserDTO together, allowing each Triad to work from common data, while still respecting the Place management pattern of GWT.
This is a convenient, readable and efficient way of allowing two Triads to communicate, but it comes at the price of increased coupling. Triads which share a common Model are coupled not just by the fact that they depend on a common data structure, but also because their internal implementations are based on common assumptions about how each Triad reads and updates the Model. This is not a problem as long as we are aware of the coupling, and restrict this pattern of communication to Triads which are designed to always work in tandem. We can even begin to use this knowledge to define the elusive meaning of the word Component in the context of large-scale GWT architectures: Components are groups of cooperating MVP Triads that may share Model instances. A component, in this sense, might be the group of MVP Triads that performs CRUD operations (even sophisticated and varying CRUD operations) on a particular subgroup of domain objects. In our example, we might have the User component, which is composed of Edit User, Add User, View User and List Users MVP Triads.
Taking this as the highest form of coupling between Triads, let’s work backwards and ask ourselves in what other, less-coupled ways can our Triads communicate. In my experience of the patterns that emerge from this GWT architecture, the next most tightly coupling mode of communication is where one Presenter/Activity issues the instruction to go to a new Place, thus passing control to another Presenter/Activity – quite probably to a Triad outside the component as we’ve just defined it. Places offer a fig-leaf of decoupling in this context. In principle, the invoking Triad does not know what – if any – other Triad may respond to the instruction to move to a new Place. In practise, our Places are deeply associated with particular Activities, and therefore particular MVP Triads. We could of course change the behaviour associated with a Place simply by mapping it to a different Activity, but this rarely happens. And in fact, the name of our Place – if we have any sense – gives a strong indication of what other component it belongs to. For example, our View User activity may, in response a button clicked on its View, instruct GWT to switch to a Place that lists all Operations performed on the system by the particular User. Control would switch to the Operations component of the application (in particular, the MVP Triad that knows how to list Operations). This is coupling across components, and I think should be allowed, not just because of the fig-leaf of Places, but also for readability. The code in the View User Presenter/Activity makes it quite clear to the glance of a developer, where control is going. This is a reasonable trade off if all you want to do is move from one Activity to the next. It is a better alternative to the least coupled form of communication, which can be harder to follow: Application Events.
There are entries on this blog, going back years, that talk about a loosely-coupled way of communicating between Triads. You can consider this article to be an evolution of those ideas. They describe an Application Model View Control (AMVC) architecture in which Triads communicated by passing Events exclusively via an intermediate layer called the Application Layer. The Application Layer is responsible for interpreting those events and invoking application level logic to coordinate the behaviour of multiple other Triads. Transplanting this idea to the Ray Ryan architecture, we introduce the idea of an Application Activity/Presenter which listens on GWT’s event bus for events that have an application-wide meaning (for example, LogOutCurrentUser). Such an event might come from any component in the application, and its effects might be spread widely across the application – a much more complex operation that merely switching to a new Place. This is a pattern that we have been following in DSI to good effect. But it can lead to difficult-to-read code at times. As such, it is probably best kept for complex patterns of activity across component barriers – not just switching Place.
To summarise, the Ray Ryan approach to large scale GWT application development allows choices to emerge when it comes to communication between Triads. We can organize these choices into a kind of hierarchy and try to apply a consistent pattern to their use, creating as we do so, a new definition for what we mean by Component:
Most coupled: Shared Model instances. Only sparingly and ONLY within Triads of the same component.
Medium coupling: Invocation of one Triad’s Place from another Triad. Can be trans-component. Use if simply switching Places.
Least coupled: Event Applications passed through a mediating layer. User for all other (more complex) interactions.