Grooveshark Architecture
Grooveshark Architecture
Introduction
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
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.
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
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.
Important Delegates
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.
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.
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).
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.
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.