0% found this document useful (0 votes)
19 views

Grooveshark Architecture

Uploaded by

kuper89
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
19 views

Grooveshark Architecture

Uploaded by

kuper89
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 5

Needs More Cowbell

An Overview of Grooveshark's Architecture


by Katy Richard

Introduction

Grooveshark (hereafter referred to as 'Lite') is a full-fledged web application written with


the Adobe Flex Framework. However, while Flex is an excellent application framework
for quickly developing data-driven applications in Flash, Lite is also made possible by its
architecture framework, developed in-house and codenamed Cowbell. This document
will provide an overview of the Cowbell framework, how it is used in Lite, and how all the
major pieces of Lite interact via Cowbell's central controller.

So What's Cowbell Anyway?

Cowbell is an MVCS (Model-View-Controller-Service) architecture framework, taking


inspiration from both Cairngorm and Joe Berkovitz's articles about MVCS. While its core
classes can be used in any ActionScript 3 project, it particularly benefits from including
at least the basic data binding classes from the Flex framework.

Cowbell itself has very few core classes. In a sense, Cowbell as a framework is more
conceptual than anything - a standard of application flow rather than a ton of code. The
classes it does have lay the foundation for this flow. Cowbell's core classes are
ApplicationController, ActionEvent, and an interface, ICommand.

The Controller

Every Cowbell-driven application has at its center an instance of ApplicationController.


ApplicationController has one main purpose - to catch and interpret ActionEvents.
ActionEvents bubble by default, so an application's controller need only listen on the
stage itself in order to capture any ActionEvent dispatched by any object on the display
list. In Lite, there is also an object stored in the model that the ApplicationController
listens for ActionEvents on. This object can be passed to non-View objects so that they
can use it as a proxy to dispatch ActionEvents and know they will reach the controller.

The ApplicationController maintains a list of registered actions, generally defined in its


constructor through the registerAction() method. When it receives an ActionEvent, the
ApplicationController checks its action property against the list of registered actions. If
the action is not registered, it does nothing (apart from tracing a debug statement that it
received an action it did not know how to handle). This makes it easy to stub out new
actions in the View without worrying about errors being thrown. If the action is
registered, it will execute the method assigned to that action. For some actions, the
ApplicationController may simply update the state of the application directly. For others,
it may delegate the action to a Command object, or to another class that is in charge of
that part of Lite's functionality. More specific examples of this delegation will be covered
later in this document.
ActionEvents

Each ActionEvent has an action property and an info property. The action property
defines what action the event represents, and is used by the ApplicationController to
determine how the event should be handled. The info property is simply an Object that
contains any additional information or context required for the action to be handled.
Since it is an Object, it is very flexible. Some actions need no additional information at
all (for instance, pausing the current song). Some actions require more (such as an
array of songs to be added to the current playlist, along with the index at which they
should be inserted, and whether or not to begin playing them immediately). All of this
can be handled via the info property.

The View and The Model

Both the View and the Model are very straightforward - the Model represents the current
state of the application, and the View displays this state to user. To do this, the Model
makes heavy use of Flex's data binding. Whenever a property on the Model changes, it
dispatches a PropertyChangeEvent. These PropertyChangeEvents are picked up by the
View in order to update what the user sees. Whenever the user interacts with the View
in such a way that would change the state of the application, the View dispatches the
appropriate ActionEvent to be handled by the Controller. The View does not change the
state of the application directly, but instead always dispatches an event to be handled
by the central ApplicationController.

Commands

In its most abstract definition, a Command is a unit of work to be performed. In practical


application, Commands are generally used when the application must interact with the
Service layer. A Command object must implement the ICommand interface. All this does
is ensure the Command object has an execute() method. Since interacting with the
Service is by definition an asynchronous action, using a Command object means the
Controller (or one of its delegates) does not have to worry about handling the return. It
can simply create a Command, execute it, and the Command will update the state of
the application based on its success or failure. Since there are some operations that
*should* be handled in a synchronous manner, the framework also contains a helper
class, called CommandQueue, into which Commands can queued so that each one will
not execute until the previous one has completed.
The Service

Being a web-based application, many of the actions performed by the user require
communicating with Grooveshark's backend. The Cowbell framework itself is agnostic
when it comes to the actual implementation of the Service layer, since the Controller or
its delegates generally create Command objects for this purpose.

In Lite, we use a customized implementation of JSON-RPC. From my understanding,


Jay has written about Lite's API separately, so I will not go into detail about the API itself
here. Within the client application, there is a single service class that handles the
making the actual requests. All a Command (or other) object must do in order to make a
service call is pass it the method name, a parameters object, and optionally an
ItemResponder with success and failure callbacks. The service will make the API call,
and pass the resultant JSONResult or JSONFault object to the appropriate callback
function.

Important Delegates

As mentioned before, the central ApplicationController delegates much of the actual


functionality in Lite to either Commands or other specific controllers. We will now take a
look at some the most important delegates and other classes in Lite, and how they
integrate into the whole.

PageController

The PageController is one of the most important controllers in Lite. Lite supports
deeplinking and browser back/forward compatibility through the swfaddress JavaScript
library. PageController both listens for and reacts to changes in the URL hash of the
browser, and takes delegated requests from the ApplicationController to change the
page (and update the URL hash). When the swfaddress library is unavailable, for
instance in the AIR version of the application or when the site is wrapped in an iframe
via something like the digg or facebook toolbar, the PageController also manages the
history stack itself. Up to 10 recently viewed Page objects are also kept in a local cache.

When the URL changes, or a LOAD_PAGE ActionEvent is delegated to the


PageController, it first determines the type of page to be loaded. If there is a page
matching the provided parameters already cached, it will reuse the existing Page object.
Otherwise, it will create a new Page object of the appropriate type and parameters.
Page objects handle loading their own data from the Service layer, via Command
objects.

Once the new page object has been determined, it will be set as the currentPage
property of the PageController, and the URL hash will be updated to the correct value, if
it is not already. The currentPage property is a bindable property that the View listens
on. Whenever this property changes, the View will automatically update to display the
provided page.
AuthController

The AuthController manages the list of recently logged in users, and their saved
(encrypted) passwords, if the user has chosen to save them. User authentication data is
stored in LSOs (flash cookies). When the application first starts, and the AuthController
is initialized, it checks both the user passed into the app via flashvars (from PHP) and
the LSOs to see what user should be logged in. The AuthController is also in charge of
updating and maintaining all the user-specific data in the application. When the logged
in user changes, AuthController creates commands to fetch all of that user's data and
preferences (apart from their library and favorites, which is further delegated to the
FavoritesLibraryManager).

FavoritesLibraryManager

The FavoritesLibraryManager, as can probably be guessed from its name, handles both
the initial loading and any changes to the user's library or favorites. When the logged in
user changes, the AuthController tells the FavoritesLibraryManager to reload the correct
data. Any ActionEvents received by the central ApplicationController to add or remove
items from a user's library or favorites is delegated to the FavoritesLibraryManager. Not
only does the FavoritesLibraryManager take care of creating the appropriate
Commands to load/modify a user's library/favorites, it also ensures that any Page
objects (be it the currentPage or one in cache) currently displaying that data are kept in
sync as well.

ObjectCache

The ObjectCache does not function as a delegate for any actions. Yet it is still very
important. It serves as a centralized weak-referenced cache of any Song, Album, Artist,
Playlist, and User objects in the currently running application. Any Command that
creates a new object must first check with the ObjectCache to see if an instance for a
given ID already exists. If it does, it must reuse the existing object. Otherwise, once the
new instance is created, it is added to the ObjectCache. This ensures that there is never
more than one instance of a given object throughout the application.

This makes it very easy to keep data consistent throughout the application. When a
user favorites a song, for instance, the FavoritesLibraryManager can set the isFavorite
property on the one song object it has, and know than anywhere in the application that
the song is reference will reflect the correct value.

Queue and QueueSong/PlayableSong

All playback functionality is delegated from the central ApplicationController to an


instance of Queue. There are generally only two Queue objects in the application at a
given time: the currentQueue and the previousQueue, both in the player model. When
the user clears their queue, the currentQueue is assigned to the previousQueue
property, and a new Queue instance is created and assigned to the currentQueue
property. This makes it easy for us to provide undo functionality for clearing the queue.
In order to undo, the previousQueue instance is simply assigned back to the
currentQueue property. As always, these properties are bindable, so whenever the
currentQueue or its content changes, the View automatically updates to reflect it.

ActionEvents to play an object are delegated to the Queue, which in turn delegates the
actual physical playback of any individual song to a PlayableSong object. QueueSong is
an extension of PlayableSong, which adds some properties and methods that only have
relevance when a song is part of a Queue (such as Radio smile/frown voting).

Queue objects manage their constituent QueueSong objects - automatically choosing a


new song to play when a song ends, based on shuffle and repeat properties;
maintaining the data that is necessary to request new Radio songs, and requesting
them (via a Command) when the end of the Queue is reached and Radio is on; and so
on.

Other Controllers

This document has only covered some of the most important classes in Lite. Some
others, which I will not go into detail about include:

AdController - handles passing parameters for ad rotation to DFP on a timer that decays
based on user idle time.

ThemeController - loads and maintains the list of currently available themes, and takes
delegated requests to change the current theme.

LocalController - loads and maintains the list of currently available languages, and takes
delegated requests to change the current locale.

GUTSController - Lite's interface to GUTS, our logging system.

JavascriptAPI - handles configuring and responding to calls from ExternalInterface.

LSOManager - provides convenience methods for accessing LSOs (flash cookies).

Hopefully this document has provided a basic understanding of how the Lite application
architecture works. For more detailed information or questions, please ask a Lite team
member, such as myself or Joe.

You might also like