Skip to content

Instantly share code, notes, and snippets.

@soareschen
Last active August 29, 2015 14:11
Show Gist options
  • Save soareschen/777a33d9f015b476698c to your computer and use it in GitHub Desktop.
Save soareschen/777a33d9f015b476698c to your computer and use it in GitHub Desktop.
Quiver Architecture For The Front End

Quiver Architecture For The Front End

Despite my lack of experience in HTML front end development, I am already itchy to step a foot in and try to create new front end architecture. Is my non-expertise a benefit or red flag? I really don't know.

I find the higher order constructs in Quiver - builder, filter, middleware, component - to be a very robust and general architecture that can be applied to wide range of applications, including GUI apps and perhaps even rendering engines. So let's see how a HTML app can can be defined based on Quiver:

Template

function template(model) => VirtualDOM

At the root, a front end application is a template function, a.k.a. view handler. It is a function that accepts a model plain object and returns a Virtual DOM.

Unlike React, the template is stateless and all states are stored in the Model domain. But model in Quiver is just a plain object. If a UI state changes, the model object changes and the template function is called to re-render the virtual DOM. I believe even states such as form data and scroll position can be handled as models, and template functions can be pure.

Template Composition

Right now both Web Components and React compose on Elements. This is similar to the current back end pattern where composition is done on handler functions. This way of composition is often fragile and makes the code harder to understand.

Just as the back end Quiver design, my better take on this is to make the composition on the template builder level. To see what it means let's take a simple example of a page with a side bar and main content area. The Web Components approach would be constructing side bar and main elements something like:

<app>
  <sidebar> ... sidebar content ... </sidebar>
  <main> ... main content ... </main>
</app>

Now the content inside these elements can be arbitrarily complex. The sidebar and element have no idea of what is inside and just render the content. However in practice the content inside elements are very well defined. So they can be just bounded once and save a lot of computation for the fragile relationship with the children.

In the meta sense, we want to compose base on templates, not elements. We want to create new templates by passing them child template functions. Something like:

var appTemplate = makeAppTemplate(sideBarTemplate, maniTemplate)

Template Builder

The template builder function is a generalization of template-based composition. It has the following signature:

function builder(config) => { template, observables }

The template builder function accepts a config that can contain subtemplates, among other things, and return a result object containing the constructed template function as well as a map of observables.

The observables returned by the builder function represents exported events that are relevant to the rendered elements. It is separated from the template function, so that once the template relationship is established, we can subscribe to events just once regardless of the change in virtual DOM elements in each re-render by templates. This design also enforce privacy in events, in the sense that a builder function can choose to expose only certain events that are relevant to the UI functionality.

A top level application template builder can be something like follow:

var appTemplateBuilder = function(config) {
  var { sidebarTemplate, mainTemplate } = config
  var sidebarCollapseObservable = ...
  
  var appTemplate = function(model) {
    var sidebar = sidebarTemplate(model)
    var main = mainTemplate(model)

    return (
      <div class="app">
        <div class="sidebar">sidebar</div>
        <div class="main">main</div>
      </div>
    )
  }

  return { 
    template,
    observables: {
      sidebarCollapse: sidebarCollapseObservable
    }
  }
}

Middleware

In Quiver front end, there will be no need for model system or any complex wiring between different domains, because everything else fall nicely to the Quiver middleware architecture:

function middleware(config, builder) =>  { template, observables }

A middleware is simply a function that accepts a config and a template builder, and return the same result as the builder. This design allows us construct complex control flow that affects how the template is constructed and used. Things such as push notification and form submission suddenly become trivial with middleware. Furthermore, middleware can have dependency to other middlewares to inject services such as URL and offline storage, so that each functions are cleanly separated but easily composed.

For example, a push notification middleware can be something like follow:

var newTweetMiddleware = function(config, builder) {
  var { newTweetObservable } = config

  var { template, observables } = builder(config)
  
  var tweets = []
  var newTemplate = function(model) {
    model.tweets = tweets
    return template(model)
  }

  newTweetObservable.forEach(function(tweet) {
    tweets.push(tweet)
    var vdom = template({tweets})

    // TODO: still need to figure how to re-render vdom
    // without going all the way from root.
    vdom.render() 
  }
  
  observables.newTweet = newTweetObservable
  return {
    template: newTemplate
    observables
  }
}

And a form submitting middleware can be something like follow:

var submitTweetMiddleware = function(config, builder) {
  var { postTweet } = config
  var { template, observables } = builder(config)
  var { submitButton: submitObservable } = observables

  submitButton.throttleFirst().forEach(function(tweet) {
    template({ submitting: true }).render()
    postTweet(tweet)
  })

  return { template, observables }
}

Summary

All these are currently just wild ideas I have for applying the Quiver architecture to the front end. I am still very new to front end architectures, so I may be wrong with many of my assumptions. Nevertheless, the basic idea can be summarized as follow:

  • Compose on templates, not elements
  • Template builder function to inject dependencies and bind observables
  • Middleware function for everything else, including model and state management.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment