The Architecture of Morrisons OrderPad

Morrisons OrderPad is a tablet web-application that helps staff in supermarkets place orders for new stock as they walk around the store. The resulting application makes a good expositional architecture for a tablet web application backed by a lightweight java server application. We highlight the separation of application control and DOM interaction on the client, using small, focused frameworks on the server, the broad-stack testing environment, and the use of a pilot project to understand what features were needed.

27 October 2014

This page is a fallback page for the proper infodeck.

There are couple of reasons why you are seeing this page

The following is dump of the text in the deck to help search engines perform indexing

Description of the architecture for Morrisons OrderPad - A tablet web-app to allow staff to make orders to restock supermarket shelves as they walk around the store.
by Rob Miles and Martin FowlerThe Architecture of the Morrison's OrderPad

Morrisons OrderPad is a tablet web-application that helps staff in supermarkets place orders for new stock as they walk around the store. The resulting application makes a good expositional architecture for a tablet web application backed by a lightweight java server application. We highlight the separation of application control and DOM interaction on the client, using small, focused frameworks on the server, the broad-stack testing environment, and the use of a pilot project to understand what features were needed.

2014-10-27

Martin Fowler is an author and speaker who enjoyed understanding and writing about the OrderPad project from a safe distance.

Rob Miles was the tech lead on the OrderPad project for its first eight months. He continues to provide technical leadership and architectural consulting to Thoughtworks's clients in Europe

Hints for using this deck

Our thanks to Ankit Bansal, Paul Gibbons, David Jones, Richard Walters and Andrew Maddison for their information and review comments about this deck.


Supermarket staff restock while walking around the store

Central systems can generate restocking orders based on available data, but local supermarket staff have better understanding of local needs. So central systems provide a report with suggested restocking orders and supporting data such as recent sales and current promotions.

Currently this information is in a large printed report. The staffer makes notes on paper and returns to the office to place reorder requests.

Orders need to take into account delivery schedules which vary by store and product and week (e.g. meaty crisps might be orderable on Tuesdays and Thursdays in Bognor Regis but only on Sundays in Halifax. Next week Bognor may change to only ordering meaty crisps on Wednesday.)

Operational Volumes OrderPad is a tablet application that provides information and captures restock orders

OrderPad receives data suggested restock orders together with recent sales and delivery schedules. These come in form of flat files several times a day. A data input module converts this data into OrderPad's database structures.

Users look at this data on the tablet while examining the shelves. They can override the restock orders directly on the tablet .

OrderPad sends restock orders to the mainframe as MQ messages.


Should the app be native or HTML5?

HTML5 both allows us to delay our tablet choice and make use of new tablet options later in the lifecycle of the software. This hardware flexibility helps the client use more cost-effective tablets and insulates them better from technology shifts.

Native applications offer better user experience choices, but we felt that we could provide an effective user-experience within the constraints of HTML5.

There is better tooling for HTML5 than in native application environments

We didn't need to support any of the device's hardware features

We needed to support working offline should the user go out of range of wi-fi. This can be handled with either choice (but is more familiar for native applications).

We chose HTML 5

This was the decisive reason


Our javascript segregates application control from DOM access and server communications

Controllers handle all the application behavior but do so without any direct access with the DOM. We use the Segregated DOM pattern. DOM access is encapsulated by DOM wrappers which are the only objects to use JQuery. We find this allows us to write control logic that's easier to follow since it focuses on the intention of control and not DOM access.

Furthermore this also allows application control to be tested with Jasmine tests that don't need a browser (or other DOM implementation). Jasmine tests are run using Rhino as part of the unit test suite.

Server gateways turn logical commands (updateOrder) into http requests.

The client retrieves data from the server using a Resource API


To illustrate the division of responsibilities, let's look at the case where a user clicks on an up-arrow button to increase the size of a restock order.

During load, the controller tells the DOM object to attach the controller's orderUpClicked function to the click event on the up-arrow control.

the controller asks the DOM object for data about the order, since all the data is stored inside the DOM as field values or HTML data attributes.

The DOM object uses JQuery to pluck data about the order from the DOM populating a simple javascript object to return to the controller.

The controller carries out the business logic, updating the order object

The controller tells the DOM wrapper to update the DOM with the updated quantity

The controller sends updated order data to the gateway to send to server


We decided against using a javascript MVC framework

We considered using an MVC framework (such as Knockout, Backbone, or Angular) but felt that there weren't any compelling advantages over using server-side rendering.

We've been happy with this decision, the code is well structured and easy to modify. The separation between controllers and DOM Wrappers has worked out particularly well and we were good at keeping the discipline to maintain that separation.

…but regretted not using a CSS framework

For similar reasons, we decided against trying a CSS framework (such as SCSS or LESS). Over time the CSS code did get unwieldy and we now think we'd have been better off using one.


Sometimes users walk out of wifi range…… so we use a queue to store updates

Once an order is ready to be sent to the server the controller sends the update to the gateway.

The gateway takes the order data and packages it up into a generic message object, which it passes it to a message processor to send. The message object uses the same properties as the JQuery AJAX object, meaning the call to $.ajax() can be substituted directly for the call to messageProcessor.send()

The message processor adds the message to the queue, and then processes all the messages in the queue.

The message queue is kept in local storage.

If the ajax post fails because the network is offline, then the queue processing stops with unsent messages in the queue. The processor will retry after a short time-out.

When offline, any actions that require a GET to the server results in an error message to the user.


Server Architecture

The server runs a servlet as an embedded HTTP sever.

We built our own simple routing framework to dispatch based on the request URL

Controllers gather data and generate an HTML response using string template.

Services don't return HTML. Some services handle POST requests - such as dispatching orders via messaging to the mainframe. Some services return non-HTML data to the client.

Controllers and services access data via row data gateways which are populated from the database with Hibernate. The database is an application database with a schema supporting the display needs.

Data import is done directly to the database using stored procedures that read input files from the mainframe.

This server-side architecture follows our practice on several java projects we've done since 2008. Our teams like this style due to the ease of understanding the code, simplicity of build, and high testability.

In general these architectures only introduce general-purpose frameworks when we feel they really pull their weight. So we've tended to avoid using Spring, although we do use Hibernate (as we don't want to fight a land war in Asia). We used Shiro which did a good job of handling our security needs.


A common frustration with Java web projects is the long cycle time between modifying the code and executing it in the web server container. Delays between modification and execution makes it harder to experiment and tweak behavior - particularly when looking at the output of a complex UI.

We built a WAR file with an embedded http server.

We set the WAR file up so that we could start a server with the servlet from the command line

rapid cycle time

We can make a change and hit shift-F10 to test the result in seconds.

easy deployment

Starting from the command line makes it easy to deploy the server software into any JVM environment. It can also be deployed into a servlet containers from Jetty to WebSphere.


A simple framework handles routing

The servlet calls the router with the request/response data and the picocontainer

The router contains a list of route objects which are wired into the router at application startup.

Each route is asked in turn if it matches in incoming request. The first one that answers true is told to process the request.

To add a new route to the system, you just subclass the abstract route class and implement a couple of methods.

We found the resulting framework was simple to build, understand, and use. Since the routes were simple we didn't have any performance concerns that would necessitate a more sophisticated algorithm


The controllers act as a mapper between the database and view

Controllers extract data from the database via repositories. The repositories are wired up at application start using PicoContainer.

The controller populates a data object to pass to the view consisting of the name of the HTML template and a hashmap of data for the template.

The view uses StringTemplate to produce the HTML which is sent back to the client.

We chose StringTemplate for templating because we've had good experiences in the past with it. It's fast and its logic-free nature prevents logic creeping into the templates.


Repositories resolve queries with row data gateway objects

Clients request data from repository objects which are injected into clients by PicoContainer. Each repository has methods for the various queries that clients want to use. The repository implements these query methods as Hibernate Critieria objects which Hibernate then maps to SQL queries.

Hibernate maps database data to Java objects. These objects are Row Data Gateways, in that they do not include any business logic and their structure mirrors the structure of the database.

Row Data Gateway is the appropriate pattern here as there is little domain logic to execute. The OrderPad team has control of the database schema and the mapping from schema to screen is straightforward.


Updating the restock orders

A mainframe job generates suggested restock orders and exports them via file transfer. OrderPad reads the file and populates the database.

The newly imported orders are all marked as dirty. A regular task in OrderPad reads any dirty orders and sends them to the mainframe as MQ messages.

(Orders are all sent back and forth as the future plan is to have OrderPad generate suggested orders.)

Suggested orders are displayed on the tablet as the staffer looks at the shelves. The staffer updates restock orders and these are saved to the database (marked as dirty).

The usual update task picks up the changed orders and send them to the mainframe where they overwrite the older orders.


Most data import is done using direct database access and stored procedures

Various data feeds come from the mainframe and are imported into staging tables using Oracle's SQL data loader tool. Feed frequencies vary from weekly to several times a day. The schema of the staging table mirrors the structure of the transfer file, which is negotiated between the OrderPad and mainframe teams. Import files can be several gigabytes of data creating millions of rows in the database..

Conversion routines move data from staging tables into the main tables that are used by OrderPad. Most of these routines are done as PL/SQL stored procedures since they are the fastest way of handling the large data volumes and the logic involved is fairly simple. However there are some conversions that require some business logic, these are done in Java and make use of the Hibernate Row Data Gateways.


The functional test suite is fast and provides high confidence

The functional tests are business-facing tests which comprise some 200 scenarios. They run in around 10 minutes. Over time the execution time did creep up which highlights the need to constantly refactor these sort of tests into user journeys. Few bugs got past this screen - leading to our high confidence in these tests.

These are broad-stack tests executing against the browser and running against an Oracle database. Using HTMLUnit and InProcTester allowed in-browser tests to run rapidly. (Due to the speed of tests we didn't expend any effort to use a test double for the database.)


Early collaboration on criteria decimates defect counts

Writing out acceptance criteria as specification-by-example leads to great discussions between analysts, QAs and developers to flesh out the details of a story before we decide to play it.

The criteria code is written during development, preferably by QA/developer pairs.

The initial build of the functional testing framework took one pair-week, with about the same time spent on enhancing the framework during the project.

10-20% of story development time is in writing these functional tests.

This earlier collaboration on acceptance criteria means 80% stories sail through QA with minor UI changes or less.

We've seen only about one bug a month escape to production - making this a Very Low Defect Project

We use “decimate” in the title because this defect rate was a order-of-magnitude smaller than prior experience in similar environments.


The functional testing code divides into four layers separating acceptance criteria from html manipulation

Scenarios are the acceptance criteria for the desired behavior. They are written in an internal DSL in Given-When-Then style.

Scenario parsers use Expression builders to convert the fluent interface style of the scenario criteria into the page object API.

Page Objects provide an API written in terms of the user of an application, hiding the details of the page's structure.

HTML wrappers manipulate the HTML. This code is external library code - in this case using HTMLUnit and Selenium

HTMLUnit's ability to synchronise AJAX calls reduced non-determinism, making the test suite more reliable.

This is a crucial layer - without it the tests are very brittle to UI changes making them prohibitively expensive to maintain. We've seen many teams reject testing through the UI because of this, but we found that a good page object layer allowed these tests to take a primary role in the test portfolio.


Dependable tests allow us to take advantage of breakthroughs

In Domain-Driven Design, Eric Evans talks of domain breakthroughs - cases where you realize a fundamental problem in your design and how to fix it. Usually this fix involves changing some core concepts of the design.

In OrderPad, we had the concept of an order schedule which indicated which days a product could be ordered. (The same time slot would be used for each day on the schedule). This mirrored the way we obtained the data, but led to awkward logic.

We replaced the order profile with a set of order opportunities for each day that we could make a future order. This simplified our domain logic significantly.

Breakthroughs like this always look obvious in hindsight, but usually take a while to see when you're doing the work. A strong set of tests enable you to take advantage of these insights when you see them, which simplifies future development.


Jenkins is great for getting a team up and running with C.I.

Jenkins is widely known, easy to install, and comfortable to get a Continuous Integration server running quickly.

… but is too much hard work for deployment pipelines

Deployment Pipelines are not a first-class concept in Jenkins. The team spent an excessive amount of time jerry-rigging Jenkins to support them. In hindsight it would have been better to use a tool with better support such as TeamCity or Go.

(Disclaimer: Go is a product of Thoughtworks studios)


Project started

Application used in a single pilot store.

Started production roll-out into more stores

Completed production roll-out to 500+ stores

Product moves into support

Development team consisted of 5 developers, 1 business analyst, 1 tester, and 1 project manager

Production code is 20K LOC of Java, 10K javascript. Test code is 15K LOC of Java and 15K of javascript for unit tests, plus 20K of java for the functional tests. Build and unit test took a couple of minutes. We had about 60% confidence in the unit tests and 90% confidence in the functional tests.

(where “confidence” is Rob's gut feel whether a bug would be caught by that test suite.)


We set up a pilot to explore requirements

In most software projects one of the biggest challenges is figuring out what really needs to be built. For OrderPad, we decided to use a pilot store as key part of discovering this.

Many projects consider a pilot to be a place to deploy the first version of a system to resolve teething problems. OrderPad, however, used the pilot in a much more exploratory manner.

…using a minimally usable system

The pilot store was given a version of the software as soon as there was something that we thought they could make some use of. We then evolved the software in collaboration with them, observing their use, listening to their ideas, and providing features for them to try.

We agreed the timing of new releases with them, so that the releases fitted with their schedule and our ability to make a meaningful update. On average this worked out at a release every 10 days although the interval varied from next day to a few weeks. We collaborated with them through a mix of group meetings, observation of usage, training, and phone/email messages.

…with architectural short-cuts

The pilot version included some architectural short-cuts that would not work with the full complement of stores. One of these was using a file-transfer to send data to the mainframe rather than MQ, which wouldn't perform well once many stores were active. The MQ integration ended up being tricky to do in a performant manner, so it was handy to use a simpler mechanism to allow the pilot to get ahead earlier.


We learned about…

many things that hadn't been identified earlier on, or had been identified but discounted as not that useful.

…features

The combinations of product views which were most helpful to the staff at different stages of their work

Wrong orders would get approved at stressful parts of the day. We learned how best to counter this by adding the capability to identify likely errors for review

…workflow

How to time the data import so to balance the desire to get data early to have more time, against later to include changes

Honing the continuous delivery cycle

How best to provide support to users

…business case

Time saved in using the tablet over the old system

Time needed to train staff

A serious cost concern when you have 10,000 users (it was 15 minutes).

Measurements like these enabled us to build a more accurate ROI assessment.


We regret not getting performance tests going earlier

Later stages of the project have raised performance issues that probably would have been avoided by incorporating performance tests into the deployment pipelines at earlier stages of the project.

Some of the performance issues we found were on the client side, generally related to the interactions with the DOM. We generally found these through manual testing but there are some interesting looking Javascript performance testing frameworks available now too. Load testing would not uncover these issues and it is important to test your pages with the biggest page payload.

As is common with projects of this kind, many of the performance issues involved the database. These were usually due to missing indexes or poor queries. The poor queries were often due to Hibernate (like most ORMs you should expect to rework about 10% of its SQL), but also due to other database quirks.

A performance threshold test in the deployment pipeline would alert us a should code change cause a significant slow-down in performance. This would help us deal with database problems as soon as they appeared.

Periodic soak tests (say once per month) would have given us earlier warning of the need to make architectural changes (such as sharding) to handle the full production volumes.

Although we believe we should have put more effort into performance issues earlier, we have to recognize that diverting effort to this would have other consequences. Introducing performance tests would have taken time away from building features, which would have slowed down the learning we gained during the pilot phase. Furthermore it was only during this period that we understood complexities in the domain that had an impact on performance.


To sum up, here were our three biggest lessons from this project

Architectural simplicity enables the team to go fast

Pick or build lightweight libraries that do what you need and use well-understood patterns to build your application around those libraries

Build a functional testing framework you can lean on.

In an application with a complex user interface, a fast and reliable functional testing framework targeted at that UI allows confident refactoring and dramatically reduces bug counts.

Early feedback from users is invaluable

Be ruthless in cutting scope to get something in front of real users as early as possible and then allow this feedback to guide further development