The GreenHopper Rapid Board was built to fulfill our most-voted customer requests, including the notorious support for multiple JIRA projects. This and other features, like swimlanes and ranking performance improvements, led to a change in the underlying technology stack as well. Old to new Since we had access to some new technologies, the previous combination of WebWork-actions, Velocity templates and PropertySet-based storage has been replaced by a slim REST service implementation, using ActiveObjects as storage solution and Google closure templates and JavaScript for client-side rendering. This means no more convoluted action class hierarchy, fragmented velocity templates and heavy server-side markup creation. This is what the new stack looks like: All rendering to the client Separating the responsibilities between client and server along the lines of a defined JSON structure has some really nice effects: All the UI logic is now in one place. Previously, we’ve always had to deal with translating data into markup or JavaScript on the server and then sending fragments back to the client. Having the data from the server around in JSON inside the browser makes a lot of previous workarounds obsolete, since the the scripts often have access to the data without another server roundtrip The server codebase is reduced to producing JSON data, instead of dealing with producing markup. This makes integration testing a whole lot easier (actually, it makes it feasible) since you’re not testing against a view (markup) but against a REST resource that provides raw data that can easily be deserialised – and is a lot less subject to change. No more dealing with webwork actions or sitemesh. The interaction flow logic, especially for highly integrated, AJAX-based pages, can be expressed in JS much more naturally. And lastly, decoupling. We could take the whole backend and replace it with a completely different technology, without touching the client code. Not that we expect this to be necessary, but it gives you that warm feeling of things being where they belong. ActiveObjects storage The capabilities of ActiveObjects to store and read large amounts of data made the new, non-reindexing ranking system possible, which was a prerequisite for a cross-project board. But ActiveObjects is also used to store Rapid Board related data: Columns, Swimlanes, Quick Filters, etc. Even though AO entities look a lot like domain objects, we’ve decided to hide the ActiveObjects integration behind our own layer of DAOs. Since AO follows the active record pattern, the proxies are quite heavyweight and contain references to lots of AO internals. We certainly didn’t want to cache them, and the whole business of contexts and transactions looked like a source of odd problems as well. So the GreenHopper solution uses DAOs to access AO entities and translates them into a fairly similar looking, immutable set of domain objects. These we cache, since they’re small and have lots of read access. The business logic works only on the domain objects. For example, a Quick Filter domain object looks like this: 12345678910public class QuickFilter { private final Long [...]