The previous way
There are some relative complex sequences that happen on lonelyplanet.com and if we try to program them in a procedural way they can quickly become spaghetti code. When a user searches for a hotel, for example, their sequence would be something like:
- User clicks "Search"
- Disable the Search Form
- Disable the filters
- Call the server
- Replace the content with the returned result
- Add pagination to go to the next page if necessary
- Enable the Search Form
- Enable the filters
- Show a summary of their search
- Update window.history
- Track the search with analytics
What we ended up with is a series of function calls, passing data around. This becomes quite hard to test and difficult to comprehend as a whole system.
Components and Events
Instead we chose to build a system by composing small components that operate independently, are stateless, and handle input/output via events. A well written component can be understood and worked on in isolation and has no interleaving with other components. This makes them easy to test because we can just test input -> output.
The above scenario would change 10+ function calls into two events:
// When the user searches we publish this event, passing the search parameters this.trigger(":cards/request", data: searchParams); // When the data is returned from the server this.trigger(":cards/received", data: dataFromServer);
We then have distinct components which can handle the events independently. For example we might have a PushState component which updates the url on
:cards/received, and a Filters component which disables and enables the filter respective to the events.
Components should not be able to speak to one another directly, their only API is the event system. If future components adhere to these rules it makes the architecture simpler and easier to maintain.