I like Cairngorm. I know some folks see it and say "whoa...that's a lot of classes for a single gesture. Events, Commands, Controllers, Delegates, Locators...aieee." However, "everything in its place" mentality and separation of concerns it enforces really appeal to me.

However, one thing I absolutely can't stand is the ViewLocator/ViewHelper bit. The design forces causing it (separation of controller from knowledge of currently existing views and their implementation) are natural, and require a solution. I'm just not sure ViewHelper/ViewLocator are the right answers. They leave me writing code that still knows about the ID strings with which ViewHelpers have registered themselves.

Let's wrap a use case around this: I have a UserLoginEvent mapped to UserLoginCommand. When the UserLoginCommand gets its response from the backend server, I've got some view behavior that I'd like to take place that goes beyond what binding to the ModelLocator can provide. In other words, I need to call a function named loginFailed() on the view. Using ViewHelper/ViewLocator, I can add a ViewHelper to the view that exposes a loginFailed() function that the command will call: its purpose is to call the actual loginFailed() function on the view, disconnected the view's implementation from the command/controller layer. Complicated.

I started looking through the core Cairngorm code last night, and I noticed that the CairngormEventDispatcher's addEventListener() message just wants two parameters: the name of the event listened for and a reference to a listener function.

In Model-Glue terms:

<message-listener name="someMessage" function="someFunction" />

In Cairngorm, as in Model-Glue, the function referenced is normally on a controller.

However, unlike Model-Glue (and HTML apps in general), Flex/Flash gives us the ability to have views respond and change their display without reloading the page. This means there's no technical reason why the listener function couldn't be on a view.

By adding functions acting as intialized() and removed() handlers, you can add code to your view that listens for events, just like the controller:

private function addViewListeners():void {
    CairngormEventDispatcher.getInstance().addEventListener(AuthenticationController.USER_LOGIN_RESULT, loginResults);
}

private function removeViewListeners():void {
    CairngormEventDispatcher.getInstance().removeEventListener(AuthenticationController.USER_LOGIN_RESULT, loginResults);
}

Now, through a sequence of events, I can have the UserLoginEvent's handling code fire a UserLoginResult event. When it's dispatched, my view will "hear" it and fire its loginResults function. Simple.

Conclusion

The CairngormEventDispatcher allows for functions that aren't just methods on the controller to be added as event listeners. By taking advantage of this, we can allow views to respond to architectural events as well as controllers.

It's not without problems: our view is suddenly bound to Cairngorm's event architecture. I'm also not sure how much this stretches MVC...the model's still completely separate, which is my largest concern. It's just that the views are taking on some knowledge of the controller tier (names of events, existence of certain controllers used to get those names, the dispatcher itself) that I'm not sure they should have.

Thoughts?