functional-frontend-architecture
This repository is meant to document and explore the implementation of what is
known as "the Elm architecture". A simple functional architecture for building
frontend applications.
High level overview
The entire state is contained in a single data structure. Things can happen
and the state should change accordingly. The number of things that can happen
is described as a set of actions. Actions flow unidirectionally down the
application. Actions are handled by pure update functions. Such a function
takes an action and a state and returns a new state. The state is handed to a
view function that returns a virtual DOM representation. A module is an
encapsulated set of actions, an update function and a view function. Modules
can be nested inside other modules and modules can contain other modules. This
makes the architecture nestable and modular.
Features/goals/ideas
- As few mutations in application code as possible. The vast majority of your
application can be completely pure.
- Time changing values and interactions with the world is introduced in a
controlled manner through FRP.
- Testing should be easy! And nothing is easier to test than pure side-effect
free functions.
- State should be easily inspectable for debugging and serialization. Also,
time travel.
- Minimalism and simplicity are center pieces in every used library.
- Actions should be expressed as union types.
- Everything should be modular and nestable.
-
Simple live code editing thanks to hot
module replacement.
Documentation
Examples
-
Counters example without FRP This is several
versions of a counters application. It starts out very simple and then
gradualy increases in complexity. This is implemented with Snabbdom, union-type and Ramda.
-
Counters example with FRP This is similair to the above example but
the architecture is bootstraped with Flyd as and FRP library.
-
Who to follow A small who to follow box using
GitHubs API. It can be compared with a plain FRP version using
Flyd
and one using Rx.
-
Zip codes This is a translation of Elm's zip-codes
example, designed to show how to use asyncronous tasks, specifically http
requests. ES6 Promises are used here as stand-ins for Elm's Tasks.
-
TodoMVC A TodoMVC implementation built with Snabbdom,
union-type, Ramda and Flyd.
-
File Uploader Implements a file-upload component,
i.e. it renders a list of chosen files, shows their upload progress, and
allows users to cancel uploads. It demonstrates asynchronous side effects
and a realistic stand-alone reuseable component or 'widget'.
-
Hot module reloading Demonstrates how
simple it is to use webpack's hot module replacement to achieve live code
editing without browser refreshes.
-
Modal Demonstrates a technique for implementing modals.
-
Nesting A simple application that demonstrates three
level nesting of components. This is intended to show how the action-routing
scales.
Libraries
The architecture is implementation independent. It can be implemented with
varied combinations of libraries. It only requires a virtual DOM library and a
way to update JavaScript data structures without mutations. Having a nice
representation of actions is also useful.
Virtual DOM libraries
The view layer in the architecture consists of pure functions that takes part
of the applications state and returns a description of it's view. Such a description
will typically be a virtual DOM representation that will later be rendered with a virtual
DOM library. A number of options exists.
-
Snabbdom A small modular and
extensible virtual DOM library with splendid performance.
-
virtual-dom A popular virtual
DOM library.
-
React Mature and widely used. It
supports JSX which many people like. It is however a bulky library. It
supports stateful components which should not be used together with the
architecture.
Updating data structures
When handling actions inside update
functions it is necessary to update ones
state without mutating it. Libraries can provide a help with regards to this.
-
Ramda Ramda provides a huge amount of functions for
working with native JavaScript data structures in a purely functional way.
Representing actions