When creating large web applications with GWT, it's not always clear how to get started. The most important (but most difficult!) question, I've found, is exactly how to structure the project - i.e. what justifies creating multiple web applications, multiple GWT modules, multiple EntryPoints, or everything all in one. Although there is plenty of discussion out there (here, here, here, and here), the general consensus is still a little muddy. Like anything in our business, it seems the "right" answer is "it depends", and so in this post I hope to highlight a few of the factors in this decision.
Example: Web apps, GWT Modules, and EntryPoints
It's probably best to start with a simple example. Imagine you already have some ecommerce site (you can decide what it is that you sell!), and now you need to build an internal application to manage your business. There are two "sections" to this site: (1) the Inventory UI through which Customer Service Reps can see what's in stock, and (2) the Orders UI through which they can track orders and through which the Shipping Manager can fulfill orders and send them out.
Note that there is some code that both the inventory and orders sections will need to share ("common" above) - for example, base classes, domain classes, helpful utilities, etc.
The question is how do you structure this application within GWT? Here are a few choices:
- Two WARs: have a separate web application for the inventory and orders sections, each sharing the common.jar file.
- Two EntryPoint modules: have one web application, but create separate GWT modules for common, inventory, and orders, where inventory and orders both have EntryPoints defined (see picture below).
- One EntryPoint module, three dependent modules: have one web application, but split each of the three pieces (common, inventory, and orders) into their own GWT modules, and then create one main GWT Module with an entry point which imports the others (see picture below).
- One big GWT module: have one web application and just one GWT module and one entry point.
Just to be clear, here's a picture of the GWT module organization in options 2 and 3:
This decision is pretty fundamental, and so it's best to get it right up-front (or at least to try!). Here are some factors to consider:
Download Time
GWT applications are not like more traditional web applications. When the user first navigates to the site, he downloads the entire application (as a Javascript bundle), rather than downloading the HTML for each page incrementally as he navigates through. This means, all things being equal, the larger the application, the larger the Javascript bundle, and so the longer the initial load time of the application. The problem is, however, that if a user never accesses a whole section of the site (e.g. Shipping Managers never use Inventory), he still needs to wait for that portion of the Javascript to load. And if there are many unused sections in the site for a given user, this could mean a lot of wasted time.
At first blush, this may seem like a legitimate argument for either options 1 or 2 above (separate web applications or separate modules with entry points), especially if the different sections are sufficiently large. And while taking this approach and breaking things out could definitely solve your download latency issue, before you do this there are few caveats to first consider.
First, GWT 2.x has essentially solved this problem with code-splitting, a mechanism that allows you to partition your code, and only download portions as the user needs them, not all at once. (Note: structuring the dependencies to allow for effective code splitting can be tricky, so it is imperative that you think this through up-front). Second, Google is pretty clever in how it can boil down even a relatively big application into a small amount of Javascript, so unless the application is very large, this is usually not an issue. And lastly, the Javascript for the application is cached on the client, so only the first visit to the app could potentially be slow.
Looking at option 3, the different modules provide good possible division lines along which to code split, however code splitting is equally possible with option 4 (one GWT module) as well. Google has some helpful patterns defined for code splitting, and there are a number of other good sources as well (like here).
Essentially, application download time, in and of itself, is probably not a solid reason for breaking an application into multiple applications (either WAR or entry point), however if navigation between sections is rare, then it may be justified, which leads me to my next point...
Navigation and URL
With GWT, one application is just one URL (e.g. http://www.foo.com/index.html), and different "pages" are just inner links (e.g. "index.html#my-orders"). It may be important (for usability, SEO, security, etc.), however, for different "sections" of the site to have different URLs (e.g. "/orders/index.html" and "/inventory/index.html") rather than just one with inner links (e.g. "index.html#section=orders" and"index.html#section=inventory").
A most obvious solution would be to split it into different web applications (option 1), but in many cases (including the example above), this won't work - e.g. the two sections may need to share an HTTP session, etc.
Creating different GWT entry point modules (option 2) could solve this issue as well - there would be only one web application this time, but each module would be accessible via a different URL. The downside here, however, is navigating between these modules (e.g. inventory-to-orders or orders-to-inventory). First, the normal mechanism for navigating in GWT 2.1 (i.e. placeController.goTo()) only works within modules, and so it'll be necessary to "hard-code" the module that you are navigating to in the navigation code (e.g. Window.Location.assign()). Second, client-side state cannot be shared between GWT modules with different entry points, so if a module needs to know about something from another module, it'll have to go to the server.
These disadvantages may be acceptable, especially if the different sections are accessed by mutually exclusive sets of users, and so cross-section navigation is rare. Otherwise, keeping things together, either in one entry point but multiple GWT modules (option 3) or one module altogether (option 4), will be necessary, and you'll just have to live with the new, AJAXy-style URLs.
Compile Time
As you know, in GWT applications, Java code gets compiled into Javascript. This compilation can take a fair amount of time for larger applications (even when compiling for just one browser), and slow builds make developers less productive. Not good. Of course GWT is smart enough to only compile what's changed, and most times developers are working within the hosted mode which doesn't necessitate a manual build, but even still, in a team environment "clean" builds are sometimes justified, and if these are slow it can significantly disrupt a developer's flow.
Surprisingly, segmenting applications into multiple entry point GWT modules (option 2) can often do more harm than good, especially in the case where the shared code is large. For each entry point module, GWT re-compiles everything it needs, so if there are two modules, "orders" and "inventory", and they both share "common", then "common" will be compiled twice (note: this is true even if common is a separate project and has already been compiled into a common.jar - i.e. the JAR contains no Javascript!). In this case, segmenting into multiple entry point modules would therefore significantly hurt build time, not help.
If the application is significantly large, then finding a way to speed build times, outside of the well established configurations/optimizations, might still be necessary. Segmenting into separate GWT modules with one entry point (option 3), allows for a possible work-around. If developers typically work within only one functional module at a time (e.g. orders or inventory), it's possible for them to comment out (in their local environment only!) the references (e.g. imports in gwt.xml or code references, etc.) to the other un-used functional modules, thereby bypassing the GWT compile for these unnecessary modules in their local build. Basically, the idea is, using the example above, don't make the developer of the Orders section compile the Inventory section code when he'll only ever be working within Orders.
Of course developers have to remember not to check these files in, and of course the main build (e.g. Hudson, etc.) will build everything (e.g. Orders plus Inventory) and so will take the normal amount of time, but at least locally things would be faster. (Note: this is a trick we've used on projects with success, but I haven't heard others mention it - I'd love to hear about your tricks...or if you see a flaw in this approach).
Maintainability and Reusability
From a maintainability and reusability perspective, creating one monolithic application is a well-known path to a big ball of mud. If different teams of developers work on different sections, or if different pieces of the application may be re-used in the future by other applications, then modularizing in some way will be helpful (options 1, 2 or 3). In some cases this may mean creating one share-able set of client code (e.g. common), or possibly many (infrastructure, domain model, etc.) - depending on the expectation for re-use. Again, in general, for an enterprise-scale GWT application, it's important to put some thought into dependency organization, using either projects, web applications, GWT modules, or packages as a unit of structure.
Conclusion
As I mentioned at the start, the "right" approach to structuring a large-scale GWT application completely depends on the requirements of the project. I hope this post has at least shed some light on a few of the factors that are important to consider, but I know I've only scratched the surface...so please let me know about your experiences, or what I might be missing!