One of my biggest issues with MVP is the idea of a central application
Event Bus. I don't think the idea of an event bus is bad, but most
implementations I have seen rely on the GWT HandlerManager as an
implementation, and this causes a few problems (or at least concerns).
e.g. http://code.google.com/p/gwt-presenter/source/browse/trunk/src/main/java/net/customware/gwt/presenter/client/DefaultEventBus.java
or http://code.google.com/p/gwt-mvp/source/browse/trunk/src/org/enunes/gwt/mvp/client/DefaultEventBus.java
As an application grows, it invariably becomes more complex and will
both generate and consume more events. In a sufficiently large
application, this can result in a very chatty event bus. With larger
numbers of registered handlers, performance must eventually become a
concern as well. Handlers also cannot be registered with respect for
the generic types that some events support (e.g. OpenEvent<FooModel>
vs. OpenEvent<BarModel>) and this invariably leads to either
miscommunication or a large number of event Types. So how can you
avoid these problems?
I reason that any sufficiently large application should also be
divisible along some logical areas of function, thus creating smaller
"modules" upon which the larger application is built. It stands to
reason, then, that "module local" event buses would be the answer.
Local event buses would help minimize the effects of multiple
registered handlers for events with different generic signatures.
However, local event buses still do not solve the second problem I see
with MVP: event source.
The inherent problem with using a HandlerManager singleton as a
central event bus is that it destroys the concept of event sources. If
you follow through the code, you'd find that a HandlerManager is
constructed with a "source" object which is ultimately supposed to be
the source of any events handled by the HandlerManager. So by passing
all of your application's events through a central bus, there can only
be one source, or no source at all!
Although the following snippet is a bit contrived, it illustrates the
problem:
HasValueChangeHandlers<String> valueSource = Some.staticInitializer();
HandlerManager eventBus = new HandlerManager(null);
eventBus.addHandler(ValueChangeEvent.getType(),
new ValueChangeHandler<String>() {
public void onValueChange(ValueChangeEvent event) {
// event.getSource() will be null, as eventBus
// was constructed with null
GWT.log("event source is: " + event.getSource(), null);
}
});
...
ValueChangeEvent.fireEvent(valueSource, "foobar");
Unless the ValueChangeHandler<String> in this sample directly knows
about the source of the event (valueSource, in this case), there isn't
a way to discover who generated the event!
You could get around this problem by attaching the true "source" to
your custom events when you fire them, or simply not rely on
event.getSource() to tell you who fired the event, but neither option
makes good reuse of the constructs the GWT built-in events already
have and the second option makes it difficult to keep an application
"loosely coupled".
If an application is to be developed in a "loosely coupled" fashion
(http://en.wikipedia.org/wiki/Loose_coupling), then we must do
everything we can to prevent application "modules" from having direct
knowledge over one-another. For my application, this problem arises
when trying to tie several modules together to provide a seamless
transition from one area of the application to the next. Obviously
Module A must assume that a Module B exists, otherwise it would be
impractical to direct users to that area of the application
(regardless of how this is implemented). But without a central event
bus of some sort, there would be no way for separate modules to
communicate without direct knowledge of one another.
NOTE: similarly, an HTML page with a hyperlink can make a reasonable
assumption that the destination address is valid without having direct
knowledge of the destination page.
So clearly there must be a balance between overly chatty application-
level event buses and completely decentralized or tightly-coupled
code. Perhaps a careful mix of both approaches is best?
~~~~~~
FOR THE GWT TEAM:
If an application is to have any sort of event bus whatsoever, I do
not believe the current HandlerManager implementation is completely up
to the task. To address the event source issue, I would really like to
see the HandlerManager support the notion of specifying an event
source per event fired. I think the addition of this capability would
add tremendous flexibility in using HandlerManager for decoupled event
communication without having to wire up my own sort of custom event
plumbing mechanism.
Event Bus has nothing to do with MVP actually, it's about loose
coupling only. In Ray's talk, MVP is mostly (only?) there to allow
pure-Java unit tests, which run waaaaay faster than GWTTestCase tests.
> I don't think the idea of an event bus is bad, but most
> implementations I have seen rely on the GWT HandlerManager as an
> implementation, and this causes a few problems (or at least concerns).
> e.g.http://code.google.com/p/gwt-presenter/source/browse/trunk/src/main/j...
> orhttp://code.google.com/p/gwt-mvp/source/browse/trunk/src/org/enunes/g...
What's the problem? I too have similar code in my own framework.
EventBus is an interface that can easily be mocked in unit tests (I'm
using EasyMock), and I see absolutely no problem in using
HandlerManager as an implementation given that it fulfills all my
needs rather than making my own implementation. Moreover,
HandlerManager is used in all widgets so it has to be fully optimized;
I don't think my own implementation could be anymore optimized than
HandlerManager.
> As an application grows, it invariably becomes more complex and will
> both generate and consume more events. In a sufficiently large
> application, this can result in a very chatty event bus. With larger
> numbers of registered handlers, performance must eventually become a
> concern as well. Handlers also cannot be registered with respect for
> the generic types that some events support (e.g. OpenEvent<FooModel>
> vs. OpenEvent<BarModel>) and this invariably leads to either
> miscommunication or a large number of event Types. So how can you
> avoid these problems?
>
> I reason that any sufficiently large application should also be
> divisible along some logical areas of function, thus creating smaller
> "modules" upon which the larger application is built. It stands to
> reason, then, that "module local" event buses would be the answer.
> Local event buses would help minimize the effects of multiple
> registered handlers for events with different generic signatures.
> However, local event buses still do not solve the second problem I see
> with MVP: event source.
>
> The inherent problem with using a HandlerManager singleton as a
> central event bus is that it destroys the concept of event sources. If
> you follow through the code, you'd find that a HandlerManager is
> constructed with a "source" object which is ultimately supposed to be
> the source of any events handled by the HandlerManager. So by passing
> all of your application's events through a central bus, there can only
> be one source, or no source at all!
Having no source information actually helps in thinking about loose
coupling, IMO.
> Although the following snippet is a bit contrived, it illustrates the
> problem:
>
> HasValueChangeHandlers<String> valueSource = Some.staticInitializer();
> HandlerManager eventBus = new HandlerManager(null);
> eventBus.addHandler(ValueChangeEvent.getType(),
> new ValueChangeHandler<String>() {
> public void onValueChange(ValueChangeEvent event) {
> // event.getSource() will be null, as eventBus
> // was constructed with null
> GWT.log("event source is: " + event.getSource(), null);
> }});
>
> ...
> ValueChangeEvent.fireEvent(valueSource, "foobar");
>
> Unless the ValueChangeHandler<String> in this sample directly knows
> about the source of the event (valueSource, in this case), there isn't
> a way to discover who generated the event!
>
> You could get around this problem by attaching the true "source" to
> your custom events when you fire them, or simply not rely on
> event.getSource() to tell you who fired the event, but neither option
> makes good reuse of the constructs the GWT built-in events already
> have
com.google.gwt.event.shared.client.* events are meant to be used with
widgets or similar components.
> and the second option makes it difficult to keep an application
> "loosely coupled".
What's the least coupled?
a. a SelectionEvent whose you check getSource() against a known
source (e.g. a ContactList) to know what the "selected item" is about
(a contact to edited)
b. an EditContactEvent
> If an application is to be developed in a "loosely coupled" fashion
> (http://en.wikipedia.org/wiki/Loose_coupling), then we must do
> everything we can to prevent application "modules" from having direct
> knowledge over one-another.
You also have to be pragmatic. Design patterns are their to serve you
(in helping you design your app), not drive you.
> For my application, this problem arises
> when trying to tie several modules together to provide a seamless
> transition from one area of the application to the next. Obviously
> Module A must assume that a Module B exists, otherwise it would be
> impractical to direct users to that area of the application
> (regardless of how this is implemented). But without a central event
> bus of some sort, there would be no way for separate modules to
> communicate without direct knowledge of one another.
> NOTE: similarly, an HTML page with a hyperlink can make a reasonable
> assumption that the destination address is valid without having direct
> knowledge of the destination page.
That's what we usually call "interfaces", "protocols" or
"contracts" ;-)
There has to be a "contract" for two modules to communicate, and in
Ray's approach this is through an Event Bus and specific types of
events, rather than interfaces (list of methods) and method
invocations on instances you have a direct reference on.
This is how "loose coupling" is achieved in this case.
> So clearly there must be a balance between overly chatty application-
> level event buses and completely decentralized or tightly-coupled
> code. Perhaps a careful mix of both approaches is best?
As I implied above, choose what's best for you. I don't think there's
a "one size fits all" design (and that's probably why I don't use gwt-
presenter or gwt-mvp or any framework, but made my own for my app).
> ~~~~~~
>
> FOR THE GWT TEAM:
> If an application is to have any sort of event bus whatsoever, I do
> not believe the current HandlerManager implementation is completely up
> to the task. To address the event source issue, I would really like to
> see the HandlerManager support the notion of specifying an event
> source per event fired. I think the addition of this capability would
> add tremendous flexibility in using HandlerManager for decoupled event
> communication without having to wire up my own sort of custom event
> plumbing mechanism.
HandlerManager wasn't meant to be used as an event bus in the first
place, and GWT in itself isn't pushing into any application design
(you won't find anything related to MVP, EventBus, "Place service", or
dependincy injection in GWT, and I don't think it'll ever be the case)
GWT is a toolkit, not a framework, use it the way you want, not the
way people tell you you "should" use it.
Ray's talk is based on his own experience in AdWords v2, I don't think
he said this approach is being used as a "best practice" in all
Google's application (is Wave using MVP, DI, an Event Bus and a "place
service"? no one's ever said this AFAICT)
~~~
Yes, MVP can be independent of an event bus, and in fact, using GWT's
UiBinder makes that disconnection quite easy. If you're using UiBinder
at all, you are practically at an MVP stage (MyWidget.java being the
presenter and MyWidget.ui.xml being the view).
~~~
> In a sufficiently large application, this can result
> in a very chatty event bus.
To combat the "chatty" problem, I've decided to take a more pragmatic
approach by classifying events when triggering them. Presenters in my
application deal with two types of events: local events, like "the
user clicked on a button in my view" and global events, like "the user
is requesting navigation to an area of the application I do not
control".
Much like Widget provides an "internal" HandlerManager for just such
UI-based events, so then do my Presenters contain an "internal" bus as
well as an application-wide bus (if needed or injected via DI). When
firing an event, I just need to consider "is this an event which some
other part of the application might reasonably care about? Or is this
just mindless chatter?". Usually the answer is pretty clear.
~~~
I also found this discussion useful:
http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/8b0ae5eaf84d8bc2
What I gained from that thread applies here as well. When my
application makes a remote request, say for a CalendarAppointment, the
response is delegated to a service object like so:
public interface ICalendarService {
void getAppointment(int apptId);
}
When CalendarServiceImpl receives the CalendarAppointment, it is
dispatched down the application-wide event bus. This is good for two
reasons: 1) other parts of the application that need
CalendarAppointments now get "free" updates if they are interested in
this particular appointment (be sure to check!) and 2) the
ICalendarService interface doesn't imply anything about the underlying
implementation. CalendarServiceImpl might use RPC, but it could just
as easily use HTTP POST, XML or JSON mechanisms, and the
CalendarService consumer doesn't care! This abstraction makes
transitioning server technologies a lot easier...