mixins for Backbone
Views written for FactoryJS. It inherits all of its application structure, many behaviors, and is generally inspired by Chaplin.
Playing with Prototypes: A Cautionary Tale.
Oraculum emerged to support the development of our App Intel Console, a dynamic single page application that allows complex querying of our malware/security knowledge. We knew that the App Intel Console would one day serve as the codebase for our internal research/analysis toolchain, and would need to be embedded into other products/services. With this in mind we decided that we needed to adopt a library that supported workflows and processes for cross-product, cross-team code portability.
Initially evaluating several frameworks, we were all pretty smitten with Backbone and Chaplin. We really liked Chaplin’s introduction of a proper
Controller, as well as the many improvements to
Router it provided. What’s more, Chaplin introduced the
CollectionView, which beautifully solved the problem of rendering
Collections in an elegant and performant way. The tide only started to turn once we got into the nitty gritty of building an application of great complexity. Some of the implicit behaviors Chaplin provided suddenly weren’t what we wanted or needed. Modifying Chaplin’s underlying object prototypes to change its behaviors was a tedious and error-prone task, often leading to very tightly coupled behaviors and strange side effects. Foregoing this option and using CoffeeScript’s faux classes/extension provided some relief, but eventually led to deep inheritance, and, once again, tight coupling/strange side effects. Opting for a
mixin-based strategy helped to some extent, but the fact that we were still bound to overriding methods on a prototype continued to lead to the same outcome.
Frankly, the entire paradigm had to change.
Around the time we thought we had most of this stuff reasonably figured out, Gabriel Hernandez joined Lookout supporting our Research & Response team as a Senior Software Engineer. He brought with him a passion for the concepts of factories, flyweights and mixins, which quickly caught my ear as a brilliant solution to the structural challenges we were facing. After a few asks, the initial version of FactoryJS was born. We started wrapping Backbone and Chaplin in our fancy new factory container, and abstracted our behaviors out into
mixins. Immediately our codebase became more modular, more stable, and easier to test. Code sharing increased dramatically between the App Intel Console product and our internal research tools.
Simply using Backbone and Chaplin wrapped in the factory container took us a long way towards a functional product, but as the App Intel Console continued to increase in complexity, we once again ran into problems related to tight coupling. Chaplin’s underlying implicit behaviors were still forcing us to stub out methods we didn’t want, modify method implementations, and prohibited the use of things we had taken for granted in vanilla Backbone (like the
initialize() method). Eventually we decided that while we loved the additional behaviors Chaplin provided, we couldn’t continue to use them as they were written. We also needed to abstract these behaviors out into
mixins. Thus Oraculum was born.
So how does this thing work?
Oraculum is actually a library with three separate types of components and two utility functions. The official documentation goes into great detail about all of these components, but from a high-level perspective, these components are:
- A set of application/MVC components.
- A set of
definitions for rendering tabular data.
- A library of
- A set of utility functions that allow instance methods to emit events.
If you’re already familiar with Chaplin, Oraculum’s application components will look familiar to you. Aside from the implementation details in the underlying classes, and the named
definition resolution, structuring an application in Oraculum almost exactly the same as Chaplin 1.1.x. However, just like in Chaplin, these components are completely optional and aren’t required to use Oraculum.
The Oraculum application components are essentially nothing more than Chaplin 1.1.x’s
Dispatcher. These objects have been ported directly from Chaplin and wrapped in the FactoryJS container, having several of their implicit behaviors replaced by
mixins that perform the same (or similar) functions. As a result, these
mixins are available to any
definition that wants to use them. They include
mixins for publishing/subscribing to a global event bus, making objects disposable in a memory-safe way, freezing objects after construction, and several others.
Rendering tabular data
The tabular interface provided by Oraculum was introduced to solve the incredibly common problem of rendering
Collections in a tabular format with configurable columns, sortable attributes, etc. These components are actually an excellent candidate to be broken out into a separate Oraculum plugin in a separate repository, but we decided to keep them in core since it is such a ubiquitous use case. To this end, Oraculum provides several
definitions that allow a
Collection to be rendered in a tabular fashion with a simple interface comparable to Backgrid, but more configuration-oriented.
Oraculum comes with a large library of
mixins that aim to solve some of the most common use case problems when building a Backbone application. For example, if you need to bind an attribute of a
Model to a particular element in a
View, you may choose to use
DOMPropertyBinding.ViewMixin, which has both a configuration and data-attribute based interface for one-way model -> element binding. If you want a
Model to automatically invokes
fetch() after it’s been constructed, you could use
AutoFetch.ModelMixin, etc. At the time this blog was written, here is the exhaustive list of available mixins:
$ find src -type f | grep mixins
Instead of relying on faux classical inheritance for modifying an object’s prototype, Oraculum provides two utilities:
makeMiddlewareMethod. These utilities create a wrapped version of an instance’s target method that emit
:after events on a target event emitter. Oraculum also provides this functionality as a
MiddlewareMethod.Mixin, which allow methods to be wrapped immediately after an object is constructed.
makeMiddlewareMethod are how Oraculum provides shallow composition over deep inheritance, and they form the heart of Oraculum’s aspect-oriented programming strategy for logic decoupling.
By favoring composition over inheritance, and combining the concepts of factories, flyweights, mixins, and aspect-oriented programming in a dependency injection container, our code has become more stable, testable, dynamic, and portable, allowing insanely fast development of intensely complex browser-based applications.
Want to know more?
- Follow @OraculumJS on Twitter
- Check out the homepage
- Read the official documentation
- Fork us on github
- Or even better yet, Come work with us!