2009-10-23

GWT App Architecture Best Practices (slides+text) Part 1

This is material of conference, that I have learned and for everyone, who are interesting in GWT programming.

The source is


Ryan:
Hello. My name is Ray. And I'll be talking to you today about GWT application architecture best practices. We tried to find a drier title, but this is the one that won. I am stunned, amazed, and flattered that you all are here when you could be eating lunch or learning about Wave. So, thank you very much for sowing up. I was going to go get lunch myself if you hadn't. So, I'll warn you also that all my rehearsals of this have run long.  And I've responded to that by adding more slides to the presentation, so I'm probably going to do a lot of mumbling and a lot of talking fast. And we'll see where it goes.




So what are we talking about today?  This is about how to put together a nontrivial GWT application. Something that you actually want to ship. Something that you're going to have more than two or three people at a time working on without stomping on each other's toads.Toes.
We're going to look, in particular, at the client side because that's where I tend to pay attention. And what we're really exploring here are lessons that were learned over the past year building the new AdWords user interface. 
AdWords is a small product at Google. You may have heard of it. And it's recently been--the user interface of that has been revamped from the ground up as a GWT application.



But first, let's go to the last slide of the presentation. If you take nothing else away today, there are three things that you really want to remember. First thing, which I'm not going to talk about much at all, but still, you want to remember it, get your browser history right, and get it right early.
One of the first things that you should nail down on your app if you want it to work is what the back button and the forth button are going to do to your users, how they're going to be bookmarking things. GWT gives you a very nice history library to take care of that, and if you apply it early, it's easy. If you apply it after the fact, and try to retrofit it in, not so easy.
The next takeaway point is you're going to use an Event Bus as the backbone of your application to let yourself-- let you make sure that the left hand of your app has no more than a nodding acquaintance with the right hand of your app. Preferably trough conversations brokered by a neutral third party.
And then finally, you're going to start saying to yourself, "DI + MVP FTW." Which is to say, dependency injection and the Model/View/Presenter pattern for the win.
And I promise that that'll all make a lot more sense in about 50 minutes or so.


So let us start with a demonstration of the app that I mentioned, the AdWords Ul. Which I, of course, forgot to cue up. So give me a moment to rectify that situation. Yeah. We might just blow off this part of it if I can't get my act together here. There it is. And it has decided to reload. Good.
So this is live right now. I'm looking at the production site. If I run into glitches here, there are people in ten countries around the world, a number of users-- which number I have been cautioned not to disclose-- but they'd notice. You can see that this is a pretty traditional kind of table full of data sort of app. But we've got nice little widgets like the GWT Incubator tree view, fast tree view, helping us guide our way through things here. Our users are going to do things using the paging scrolling table from the GWT Incubator to make lots of edits of their data.





Maybe several fields at once in that kind of a thing. Or I might want to type around and change my matching and change my CPC. I forget what that means, actually, although this is my live account. So I'm not going to actually commit any of these change that I'm making. They might take advantage of the GWT visualization stuff that you might have learned about yesterday to show graphs and such. There's my pretty graph. I'm not doing so well,really. Maybe we're going to compare my click-through rate to the number of clicks that I get and see if there's a correlation there.
Yep, there is. So this is the app that we've built with a team of more than a few engineers. I really forget the handful, but the sizeable handful has grown lately. And we're all able to commit changes to this and test portions of this without necessarily talking to each other every day and destroying each other's code.



So let's get back to the presentation.
So we've got three major themes that we're going to talk about here on your way to making no bad apps the AdWords way. You're going to embrace the asynchronous nature of you app. You are an AJAX developer. And the first word in AJAX is "asynchronous." - Let's just not kid ourselves. You want to always be decoupling your app. Like I said, the bit over here
and the bit over here just shouldn't really know much about each other. And finally, you're going to strive to achieve statelessness, so as various bits of your infrastructure fall over, the other bits aren't bothered by it.
So, embracing synchrony. These Keynote builds, man. I can't get enough of 'em. Yes. The "A" in AJAX is for asynchronous. There is nothing interesting that you're going to do in your application that isn't going to, at some point or another, require some kind of an asynchronous callback. So you need to just come to terms with that fact. You don't want to spend a lot of energy trying to create little synchronous bits that you can call once everything is swapped in, because you've got better things to do with your time.
And if you do it well, it's really not such a bad thing. 



So here's an example of what you might start out trying to do. Through the talk, I'm going to assume that we're writing an address book application.A contact manager. Could you all turn you cell phone ringers on, please? The first thing you're going to do as a good developer is you're going to create your data objects, your model objects at the heart of your app. And at the heart of an address book we have contacts.
And my contacts have things I like a name field, and they have ContactDetails. ContactDetails are an interface to cover phone numbers and street addresses and mother-in-law's birth date and that sort of a thing. And the mistake that we made here is that we are downloading the kilobytes and kilobytes of ContactDetail information at the same time that we're downloading our contact objects.
At least, that's what I see implied here. That's a mistake. The odds are that you want to get your hands on that list of contacts
with as little information as you need as possible to display them to your users, and then you want to move on from there to fetch just the details that the user cares about.



So probably you want a structure that looks more like this. My contact comes down with a list of Detaillds. Some kind of opaque Id object. It's probably a string under the hood. But we're all Java developers here. We like our strong types.
And that way we don't accidentally ask for a contact with a phone number Id. Another small change I made here, which is kind of sneaky, is that instead of using a list of ContactDetails, doing the Josh Bloch thing and correctly providing an interface for my field name rather than the implementation class name.
I'm actually being very straightforward about the fact that this is an ArrayList that I'm looking at. I picked up this superstition
from a talk that I may have misunderstood on optimizing GWT RPC. Where if you know that all you're ever going to send across the wire are array list instances, you don't want to the GWT RPC-- the magic generator-- a chance to try to pick up every other possible implementation of the list and accidently compile that into your app. Small point.




So here we are, writing everything asynchronously. And thinking to ourselves, "This is really going to suck."






It doesn't have to. If we wrap our asynchronous calls and asynchronous operations in the command pattern, life can be pretty good. A command pattern is a kind of a thing you do where instead of having call for this-- you know, a "do this" method and a "do that" method and an "update the other" method-- we have objects that embody such calls. So when it's time for me
to get ContactDetails, I'll make an instance of getContacts command, and I'll hand that off to a command processor and receive back in return a "got the contacts" response. Every action I want to take with my app, I encapsulate into an object like that.

And because I have all of these nice little command objects, 
I've got a nice unit of currency for implementing caching policies. May be whenever I see the same GET request twice, I'll cache away the response I got last time and just return that to myself immediately. Not bother with a server-side trip. 
I've got a place where I can do batching mechanisms. If I see five getCalls in succession that aren't interlayered with updateCalls, I'll have a timer going where I can collect all of those guys and put them into one do this group of operations command, and put them into one do this group of operations command, send that over to the wire, over wire in a single HTTP request, rather than sending them off as bits of the app ask for them.
I got a place that I can centralize my failure handling. So that every time I'm making one of these async calls, I can inherit the "oops, something went wrong" behavior rather than having to call it out in line there. This also, if I want to get a little bit forward-looking about this, since I'm being honest with myself about the asynchronous nature of what's going on here. I've done all of the hard parts of chopping up my app into bits that can be code split at GWT.runAsync points. 
I won't go into detail on GWT.runAsync, because I'm sure you've been hearing about it for two days, but do it this way and you're halfway there. If you ever read a book on "how I'm going to implement undo and redo on my application," the odds are the term "command pattern" is going to come up. And you're already working that way. Because you're so clever, you're halfway there. And then finally, the work that you've done to isolate yourself  from all of this RPC. You've got a nice funnel point at which you could slip in your HTML5 persistence type of code when you decide you want to get into offline work.



So. let's look at some actual code here, how we might implement something like this. Are there any AdWords engineers in the audience? I can say anything. Okay. They won't actually recognize this interface. It's not exactly what they do. But there are tools


that they created specifically for the project, which when you pull back the hood, this is what's going on there.
So I assume most of you are GWT developers or you would be off looking at Wave. You probably recognize the bottom half of this slide where we are defining a GWT RPC service. When we're working in this style, we have a single service, and that service provides a single call, which is basically that "execute" word there. Do something for me, and then return to me the response.
What happened?
And then the various somethings that we want our app to do are going to implement this Action interface, which we couldn't call Command because that word is taken by another GWT class.

So an example of an Action object that we might write would be this GetDetails thing. Remember that we are working on our address book, because the world doesn't have enough address books. The command will be something as simple as I'll instantiate the thing. I'll give it a list of the Ids of the details that I want. On the server, I'll do something clever to actually fulfill that request and



send back across the wire a GetDetailsResponse object, which has this nice ArrayList of the details that I can then go and render.
Now, I'm not trying to say what the granularity of your operation should be when you write your app. I'm implying a pretty verbose world here where I've got 500 different flavors of Get command, based on my 500 different model objects. Maybe you want to do something a little bit more generic and just have a single Get something command that's parameterized based on the type that you're fetching. Maybe it's going to make sense for your app to be more coarse-grained. Maybe you want to write actions like, "Get me all the stuff I need to render the first page of the Ul." The level at which you break stuff down is up to you. I'm just trying to pound home the idea to pound home the idea that you've got objects that embody what it is you're trying to ask for and the ways you ask for it. And I took that digression one slide too early. We've had our...whoops. We have our action class.
We have our response class. To make the code a little bit more pleasant to deal with.




we're going to write probably a third class which is a custom subclass of the asynchronous callback that happens--is handed to each GWT RPC call. This gives us a spot  to write failure handling that the application code can inherit and not have to worry
its pretty little head about. Maybe we'll pre-chew the response a little bit to keep everybody from having to write the Get, the Get, the Get, the code to actually get the meat of the response.


So what I actually will end up writing in my app when it's time to render a contact or something like that is a method like this showContact thing. Where I somehow magically -- we'll discuss it-- got my hands on the service. I've called this execute method.




I've given it an instance of the Get Details command that I want it to run. And I've instantiated my callback GotDetails so that once I've got all my information, I can call renderContact, I can call renderDetails,  and go mess with widgets and stuff.
As Java goes, that's fairly concise. We don't have anonymous.. we don't have closures and probably never will. But this is about as short as it's going to get in a Java sort of world. 















3 comments:

  1. Hi,
    I have an understanding problem in the "plus convinience callback" slide. It doesn't appear in the video and it seems strange for me to present the GotDetails class as an abstract class as you are instantiating it in the example of the last slide.
    Does anyboy can explain me that?
    thanks!

    ReplyDelete
  2. He is not instantiating the abstract class but a derived one that implements the "got" method.

    ReplyDelete
  3. Thank you for this useful article :)
    But i would like to understand a thing: With the command pattern, if we would like to add a cache management, could we configure it in the client side to avoid the RPC calls (and not in the service implementation layer) ?

    ReplyDelete