Often the term “full stack” development refers to web application development where JavaScript is used on both the client and the server. For those who know only JavaScript, I can tell you that there are more things in computer science than are dreamt of in your philosophy.
This post describes a demonstration application for building and searching a database of book information. This application is constructed with the Spring framework for the Java language. The application dynamically renders web pages on the server side using Java Server Pages (JSP). The application uses the Amazon Web Services (AWS) DynamoDB database to store book information.
In writing this post, I assume that the reader has some familiarity with the Java programming language. I also assume that the reader will refer to the source code, which is available on GitHub. The source code is published under the Apache 2 software license.
The Topstone Software publication A Simple Spring Boot Model View Controller (MVC) Example shows how a very simple web application can be built with Spring MVC. This publication also briefly discusses the Spring Tool Suite, an Integrated Development Environment (IDE) that was also used to develop the Book Search application.
One of the objectives in software development should be to write as little software as possible. This goal can be achieved by leveraging software frameworks like Spring and the massive software resources of the Java ecosystem.
In the past Topstone Software has used the Grails framework to develop full stack Web applications that leverage the Java ecosystem. For example, Topstone Software developed the nderground social network using the Grails framework.
Although Topstone Software still provides Grails application consulting, all new web application development uses the Spring framework. The reasons that Topstone Software has chosen the Spring framework include:
Although the Book Search application is not intended to be a production quality application, it shows how a real “full stack” Spring application can be constructed.
The model consists of the data structures that contains the application data. In this case, there is only one data structure, the BookInfo object. The BookInfo object is the data structure that is persisted into the DynamoDB database.
The controller consists of a set of classes and functions that respond to web page interactions. In the Book Search application there are three controllers:
To avoid having the controller logic become too complex, code that is not involved directly with managing web page interaction is placed in Service objects. In the Book Search application the BookTable service supports reading and writing to the database of book information. The BookTableService abstracts the details about the database. In this case the database is DynamoDB. The database could be changed without changing the BookTableService interface. The DynamoDB Service object supports the BookTable service.
The simplest controller in the Book Search application is the controller for the application index page (e.g., the page at the relative URL “/”). As with all Spring controllers, this controller class is marked with the controller attribute @Controller
The @GetMapping annotation tells the Spring framework that the index function handles HTTP GET requests to the relative URL “/”. The @GetMapping annotation is shorthand for
@RequestMapping(value="/", method=RequestMethod.GET)
The controllers in the Book Search application contain functions that return a String: the relative URL for the associated web page that will be returned to the browser in response to the HTML request.
The main web page of the Book Search application contains a list of forms that allow the user to search the book information. A screen shot is shown below:
The simplest “form” on the application index page is just a button that issues an HTTP POST to the getAllBooks function in the Book Search Controller. This function handles HTTP POST operations to the relative URL /list-all-books. The HTML for this button form is shown below:
The getAllBooks function that handles the button form HTTP POST operation in the Book Search controller is shown below:
The code in the getAllBooks() function calls the BookTableService function getBooks() which returns all of the book information in the database as a List object that contains the BookInfo entries.
The List object is returned as a flash attribute (flash attributes have a “life” of one page display — when the page is refreshed or redisplayed, the flash attribute will be cleared). Standard page attributes are limited to String values. Flash attributes may be consist of any object type (such as the List of BookInfo objects).
The “Add a book” tab on the main page brings the user to the “Enter Book Information” form. The form for this page is handled by the AddBookController class. The saveBook() function in this class saves the information received from the form in the database. The code for the saveBook() function is shown below:
As in the previous examples, the saveBook function is annotated with a @RequestMapping annotation.
The saveBook function shows how the Spring framework can save coding effort. The field data from the form on the web page is packaged by the Spring framework into the BookInfo object that is one of the arguments to the saveBook function (note that the form input field names correspond to String members in the BookInfo class).
The function declaration also contains the @Valid annotation. This tells the Spring framework that it should perform validation on the BookInfo data. If there are problems with the input data, information about the errors will be contained in the Errors object.
Spring framework validation checking uses annotations associated with the BookInfo object. These annotations are shown below.
If a form field (BookInfo element) fails validation, the error can be displayed in the HTML. For example, the JSP logic for an error in the book title field is shown below:
DynamoDB is an Amazon Web Services “noSQL” database. DynamoDB has a number of attractive features. These include:
For the Book Search application, with relatively little data and low data traffic, DynamoDB usage is free, since it falls within the free tier. In contrast, a relational database like AWS RDS/Postgres runs on a server 24-hours a day, seven days a week. This results in monthly AWS charges.
DynamoDB supports index query operations and table scans. Most of the BookTableService search operations use indexed DynamoDB queries. The exception is the findBookByTitle() operation which will the scan the entire book information database for a book title substring.
An example of the book table information is shown below:
DynamoDB tables must have a hash index. The hash index is a value that is unique across all table rows.
For the book table, the hash key is composed of the book title. Since books by different authors may have the same title, the title hash index is combined with a “range key”, consisting of the author name. For this application the book title, author pair is assumed to be a unique value. A query on title, author pair will return only one book.
In order to allow the database to be searched by author, a DynamoDB “global secondary index” is also used. A query on this index may return more than one book.
The other DynamoDB attributes (e.g., publisher, date) are not used to return values in a DynamoDB query. These values can be searched in a DynamoDB scan operation that will read all of the items in the database. Scan operations can be expensive for large databases, since the user is charged for the amount of data read.
The DynamoDB book table is created by the CreateBookTable class. The code that builds the table constructs the hash and range key index from the title and author attributes. A global secondary index is created from the author attribute.
The code that constructs the DynamoDB queries and the book title scan is contained in the BookTableService class.
Software developers are encouraged to follow the practice of “test driven development” where unit test code is written for all software components. In developing software that makes use of DynamoDB, I have found that test driven development is a necessity. Amazon’s DynamoDB documentation could be better and it is easy to write DynamoDB code that does not function properly. Unit tests are often the only way to know in advance that the DynamoDB code works.
The database functions in the BookTableSearch object have versions that take a table name argument. This allows the unit test code to use DynamoDB tables that are created and deleted by the test code.
Topstone software has published the Java Book Search demonstration application on GitHub (see Spring and DynamoDB Book Search application). This software is available under the Apache 2 open source license.
The Java and HTML code was on this page has been “pretty printed” for HTML display using http://hilite.me/
The original version of this publication was posted on the Topstone Software web site.