Choosing Technologies: How Zapproved Made It's Front-End Architecture Decision

In this post, we discuss the front-end architectural choices made in developing one of our newer products, DDP (Digital Discovery Pro). Along the way we hope to give insight as to why we went the way we did. Before starting, let us mention that in the last few years there has been an explosion of choices of front-end libraries, frameworks, and design patterns for building dynamic web sites. This post covers only the particular set of choices we made. We try to give some justification along the way for these choices, but other combinations are certainly reasonable and may have worked equally well.

Web API

The fundamental architecture of DDP is that of a static website being served directly out of AWS (Amazon Web Services) S3, delivered by AWS CloudFront. A ‘static’ website simply means that the JavaScript and HTML files are fixed and delivered to the browser (client) efficiently; the website still has dynamic behavior given by the JavaScript. The client code gets its data by making (RESTful) HTTP calls into Web APIs hosted on our AWS servers. In addition to the static website, we use AWS to host various sets of services that take request data, transform it, and finally, serve it up to the client via the Web APIs.

This type of system is in contrast to designs where the server is more intimately involved in the construction of the web pages of the product. Popular examples of such systems are MVC frameworks which exist for all of the popular server-side languages. We chose the static webpage / RESTful API pattern [1] because it gives us a very clean break between client and server. This allows engineers to make independent and rapid progress on both sides of the APIs. In addition, specifically to AWS, the ability to ‘front’ the static website with CloudFront offers many advantages [2].

Single Page App

Our web application, DDP, is designed as a Single Page Application (SPA) [3]. In a nutshell, the goal of a SPA design is to provide a user experience similar to that of a UI in conventional software. In a SPA, as the user switches from one view to another, the data is called up via HTTP requests and the on-screen view is seamlessly changed. One is not navigating to new web pages.

Front-End Framework — AngularJS

We could have built our dynamic, API-based, SPA system entirely in native JavaScript but this wouldn’t be realistic. Nowadays we have many powerful libraries and frameworks available that make constructing a rich SPA experience easy, reliable and maintainable. The jQuery library gives some nice abstractions above straight JavaScript. Beyond jQuery, libraries such as Ember or Knockout provide a model where HTML is annotated with constructs to specify dynamic, data-dependent behavior. The annotations link to JavaScript code.

AngularJS [4] is a very popular framework for building dynamic SPAs and we chose it for DDP. We are currently experimenting with upgrading to Angular 2 (now confusingly called Angular). One big difference between a library such as jQuery and a ‘framework’ such as AngularJS boils down to Inversion of Control (IOC). jQuery provides one with a useful set of functions and the programmer decides when and where to call them. In AngularJS or other frameworks, the framework decides when and how to execute various operations to achieve the dynamic effects that are specified at a high-level in the HTML. One still writes lots of JavaScript of course, but these pieces of code are embedded inside of AngularJS components and AngularJS provides the calling context.

AngularJS provides an important piece of SPA functionality known as routing [5]. This allows us to navigate from ‘screen’ to ‘screen’ without the request-response latency and rendering of reloading the entire page. As mentioned before, this gives the user a seamless, ‘like regular software’ experience.

Another important feature of AngularJS is the ability to write custom directives [6]. In DDP, we use these to build custom HTML tags that help to organize and modularize our complex UI. So, instead of just writing our HTML pieces using standard components such as <div></div>, we can create a tag like <draw-pie-chart></draw-pie-chart>, which allows us to encapsulate the complex task of drawing a pie chart within that component. The HTML for the pie chart is done within the component itself. The higher-level HTML becomes simpler and easier to read and the JavaScript is also separated and encapsulated. In the end, we achieve a nice separation of concerns that gives a much more maintainable code base.

Constructing and using components is doable in AngularJS. As one moves to Angular 2 [7], it is strongly encouraged by the framework. We are currently in a good position to upgrade to Angular 2 and have started the process.

Other Libraries

In addition to AngularJS, we employ other useful libraries. One of these is jQuery, which provides us with some valuable plugins. If one mixes jQuery with AngularJS in this way, one must be mindful that AngularJS will not know what jQuery is doing. Remember, AngularJS is a framework and as such, it likes to be in control! In some circumstances one may have to use $watch from AngularJS to synchronize the two systems. Overall, it really isn’t good practice to mix the two libraries very much and we do it only in a limited way.

Other useful libraries we use are Underscore (nice JavaScript functions), Angular-ui-bootstrap (awesome Bootstrap components ported over to AngularJS), D3 (drawing graphics on a web page), and the AWS JavaScript SDK.

TypeScript

We made the decision early on to make our front-end system more rigorous by employing TypeScript [8] in addition to plain JavaScript. TypeScript allows one to add strong typing and intellisense to JavaScript code so that one can program effectively in an object-oriented style. Combined with a powerful IDE such as Visual Studio or WebStorm, one constructs well-engineered objects and methods from the start. Don’t worry, though — the powerful functional aspects of JavaScript are still there. TypeScript does not take anything away from JavaScript. The beauty of TypeScript is that one can incrementally add typing information to a code that started out as pure JavaScript. At first, this is an investment but over time it really begins to pay off.

A nice example of this occurred when we did a major refactoring of DDP data models about six months ago. Had we been only in JavaScript, this would have been a minor disaster — since our basic data models had changed significantly, we would have had to tediously re-test and debug all of the business workflows in the product. As it turned out, however, we simply changed the base class definitions in our TypeScript, looked in our IDE where our code was ‘turning red’ (not correct according to TypeScript), and quickly and reliably adjusted the code to the new object definitions. The adjustments were made and, by and large, everything worked again. This amounted to a savings of several weeks of work and this alone made our TypeScript investment pay off.

Folks have written TypeScript type definition files for all of the major libraries such as AngularJS, Underscore, jQuery, and the JavaScript core itself. So in addition to making one’s own code more rigorous and coherent through the addition of typing information, one obtains intellisense and the same rigor against all such library calls and calls to JavaScript. Once you have all this working properly in your development environment you will never go back!

Another benefit of our TypeScript investment is that our ongoing upgrade from AngularJS to Angular 2 has been made much easier. The folks at Google have adopted TypeScript for Angular 2 and so it is a good idea to use it. Unfortunately this has helped lead to resistance in the community against Angular 2.

typeLITE

A final plug for TypeScript is the availability of the typeLITE [9] tool which allows us to annotate our objects in our API code so that we can establish a rigorous type connection between our back-end object JSON stream and our front-end objects. In our case, the back-end objects are written in C#. One adds annotations to the model C# objects involved in the API, runs the typeLITE tool, and then a TypeScript file is created with type definitions that is included in the front-end code. This helps significantly in keeping the two worlds of front-end and back-end code in sync with one another.

Wrapping up

In this post, we hope we have given you a flavor of what we have found important in building a web application with a rich interface and yet at the same time, keeping it correct and rigorous.

 

[1] https://en.wikipedia.org/wiki/Representational_state_transfer

[2] https://medium.com/@willmorgan/moving-a-static-website-to-aws-s3-cloudfront-with-https-1fdd95563106#.ffg4lufj3

[3] https://en.wikipedia.org/wiki/Single-page_application

[4] https://angularjs.org

[5] http://www.w3schools.com/angular/angular_routing.asp

[6] https://weblogs.asp.net/dwahlin/creating-custom-angularjs-directives-part-i-the-fundamentals

[7] https://www.ng-book.com/2/

[8] https://en.wikipedia.org/wiki/TypeScript

[9] http://type.litesolutions.net/

 

 

 

AUTHOR: Steve Otto, Lead Software Engineer, Zapproved

 

Steve is a former scientist engaged in software engineering for the past 20 years with experience ranging from parallel computing languages to optimization algorithms. His current interests include web application languages for effective presentation and correctness.

Related Posts