Chapter 1 - Introduction and Overview

By John Lenz. 2016.

This book describes the design of a website which uses Haskell both on the client and server. The server uses servant to implement the backend routes and uses GHCJS (the Haskell to JavaScript compiler) to implement the client. The design is focused on websites which generate the DOM entirely in the client (so called single page applications or SPA); no HTML is generated on the server. In my opinion, if you use a mix of HTML generated on the server and a few sections of the page automated via angular, react, or friends, you are better off using a framework such as Yesod. Chapter 0 contains a more detailed discussion of the tradeoffs between these design approaches.

The full stack of client, server, testing, and deployment code all lives in a single repository. The following is a list of the pieces that make up this repository; each piece will be described in a future chapter. The overall design is influnced by the 12 factor app.

domain-data subdirectory

The domain-data subdirectory contains a cabal library which is shared between the client and server. It contains the type definitions for the domain and business data, various pure data transformation code, and the Servant API. It is compiled both by GHC for the server and GHCJS for the client, so has minimal dependencies. It also has a small unit test suite written using hspec. This is described in Chapter 2.

datastorage subdirectory

The datastorage subdirectory contains a cabal library for database operations and authorization. It is used only by the server and contains all the queries and database code for the server, as well as the checks that a user has permission to access the resources (authentication is handled elseware, the datastorage library just does authorization). The reason it is split out into its own library rather than as part of the server is that it has an extensive unit test suite written using hspec. The database and authorization is the one part of the server which requires extensive testing to make sure the queries work and permissions are properly granted and denied. Separating this into a library allows focused unit testing. This library is described in Chapter 2.

server subdirectory

The server subdirectory contains a cabal library and executable for the server. It implements the servant server, authentication, settings, and logging. The server has a small unit test built using hspec and hspec-wai. The server is described in Chapter 3.

client subdirectory

The client subdirectory contains a cabal library and executable compiled by GHCJS to JavaScript for execution in the browser. It depends on the domain-data library to share data types with the server. This library does not have a unit test; most complex business logic is moved to the domain-data library and tested there, while DOM manipulation is better tested as part of the acceptance tests. The client is described as part of Chapter 4.

metalsmith subdirectory

The metalsmith subdirectory contains a Metalsmith static site. This static site contains the HTML, markdown, CSS files, images, and JavaScript dependencies for the the site. The metalsmith build script also references the JavaScript output of the client build, including it as part of the static site. The result of the metalsmith build is a collection of static files which are served to the client at the main URL for the web application. The metalsmith site is described in Chapter 5.

acceptance-test subdirectory

A typical continuous delivery/deployment pipeline for web applications is

build -> unit test -> integrate -> acceptance test -> deploy

Acceptance testing is black-box testing where the tests interact with the full program/web site exactly as a user does. Acceptance testing will run the production build of the server and load the metalsmith static site into a real web browser such as Firefox and Chrome. The acceptance tests then click elements on the page, send keys, and make sure the DOM is as expected. Importantly, the only interaction the acceptance test has is via the browser.

Using the great tools webdriver and selenium, this is suprisingly easy. In only a few lines of code, you can open a page in real browsers, find elements on the page, click on them, and check their properties. In Haskell, this is via the webdriver and hspec-webdriver packages.

The acceptance-test subdirectory contains a cabal executable project using hspec-webdriver. Importantly, it does not depend on any other libraries so that it treats the website as a black box. The acceptance testing is described in Chapter 6.

DevOps files

A key principle of DevOps is that "Infrastructre is Code"; everything needed to build, unit test, integrate, acceptance test, and deploy is part of the source repository. The DevOps files include the stack.yaml file, package.json file for JavaScript depenencies, metalsmith build files, continuous integration (Jenkins/TravisCI/another CI) control files, and deployment scripts (I am using AWS so this includes CloudFormation templates and other scripts). All this lives in the source repository and is described in Chapter 7.