A production-ready starter-kit for Spring Boot applications with MongoDB.
Spring Boot makes it easy to create stand-alone, production-grade Spring-based Applications that you can just run. We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need very little Spring configuration.
Spring Boot is opinionated. This simply means that Spring Boot has its own configurations, application structures, dependencies, Servers and other environment configuration available inside its realm. Thus, to say Spring Boot has its own opinions about an application development environment. For example, most of the Java-based web applications use tomcat server. While working on Spring Boot you need not use any server, because Spring Boot already has an embedded Tomcat container.
Spring Boot is stand-alone. What it means is that you don’t need to use any other third-party library or server to run or develop a spring boot application, it already has all of it.
It is production-grade. This implies that application developed using Spring Boot defaults is able to handle all complexities and requirements of a production environment.
Still very customizable. It is not worth using a framework which has its own rigid opinions, which you can’t customize or change according to your own business requirements. Although Spring Boot is opinionated you can easily change or customize its defaults to suit your own needs.
The greatest thing about Spring Boot is the ability to be up and running in very little time. You don’t have to install a web server like JBoss, Websphere, or even Tomcat for that matter. All you need to do is pull in the proper libraries, annotate, and fire away. If you are going to do a lot of Spring Boot projects, I would highly suggest using the IntelliJ IDEA IDE. It has some great features for making Boot projects really easy to manage. You can of course choose between Maven or Gradle to manage dependencies and builds. This starter kit is based on Maven.
This starter kit focuses on how to use Spring Boot following the best practices that are recommended by Spring Framework 5.0: Ensure the code is loosely coupled and that all the layers in the application are completely independent of each other and talk using neutral objects. While writing this kit, I have done sufficient research around the code structure and usage of appropriate design patterns to make this as an excellent starting point to begin coding your own web application.
The kit would have been incomplete if it did not have a concrete use case to implement. Here, I have taken a case study of a Bus Reservation System and tried to implement an Admin portal which can be operated over browsers and a series of REST APIs to interact with the system using mobile applications or frontend applications written for the browsers. The complete systems has two important actors: Admin user, and the End user.
The Admin user can access this application on browser (laptop or mobile/tablet, doesn't really matter as this is built using bootstrap, material design and is completely responsive) and can perform the following actions:
- Signup
- Login (Spring sessions)
- Update their profile
- Create an agency
- Add buses to the agency
- Add trips consisting of predefined stops and buses
The End user can use their mobile application (yet to be built, however the REST APIs are ready and could be used via Postman or Swagger) to perform the following actions:
- Signup
- Login (and get a JWT token)
- List all available stops
- Search a trip between any two stops
- Filter search results with a date option
- Book a ticket for a given trip schedule
The Admin interface and REST APIs both have their independent authentication mechanisms. The web application uses the cookie-based authentication (provided by default by Spring security) and the REST API uses the JWT authentication for access. This application assumes the availability of MongoDB installation on the localhost where the server will run or the use of docker-compose to boot up a mongodb container and link the application with it within the realm of docker.
Any changes that the admin users will do on the web portal will impact the search results of the end users. There will be certain use cases which you may find missing here. I hope you will appreciate that the overall idea was to present a way to create such an application completely inside the realm of Spring Boot and not to actually building a fully functional reservation system.
The admin user interface is completely written in material design using Bootstrap v4 and is responsive to suite a variety of devices. The template engine used to render the admin views is Thymeleaf since the library is extremely extensible and its natural templating capability ensures templates can be prototyped without a back-end, which makes development very fast when compared with other popular template engines such as JSP.
The current schema looks as follows:
- The authentication and authorization is governed by
User
andRole
collections. - The
Agency
collection keeps the details of agency along with who owns it. - The
Stop
collection keeps the data about all the stops in the system. - The
Bus
collection has the data of all the buses for various agencies along with their capacity and make years. - The
Trip
andTripSchedule
collections keep the information about all the trips that agency owners create within the system. Information like source and destination stops, journey time, data [date?] of travel, tickets sold so far and the available seats is collected in them. - Last, the
Ticket
collection keeps information about all the tickets issued in the application for various trips.
Following libraries were used during the development of this starter kit:
- Spring Boot - Server side framework
- Docker - Containerizing framework
- MongoDB - NoSQL database
- Swagger - API documentation
- Thymeleaf - Templating engine
- Material - UI theming/design
- Bootstrap - CSS framework
- JWT - Authentication mechanism for REST APIs
Spring Boot is an opinionated framework that makes our life very easy since we don't have to choose the versions of different dependencies based on the version of Spring framework - its all taken care of by Spring Boot. I have tried to follow the same ideology while creating the project structure, at first it might seem overwhelming, but do believe me once you start writing your pieces the structure will help you immensely by saving your time and thinking about questions which are already answered. The structure looks as follows:
The various models of the application are organized under the model
package, their DTOs (data transfer objects) are present under the dto
package. There are different opinions about whether we should use DTOs or not. I belong to the set of minds who think we definitely should, and that not using DTOs makes your model layer very tightly coupled with the UI layer which is something that no enterprise project should ever get into. DTOs let us transfer only the data that we need to share with the user interface and not the entire model object that we may have aggregated using several sub-objects and persisted in the database. The mapping of models to the DTOs can be handled using ModelMapper utility, however its only useful when your DTO is almost similar (literally) to the concerned models which is not always the case and hence I prefer using custom mapper classes. You can find some examples under the dto/mapper
package.
The data access objects (DAOs) are present in the repository
package. They are all extensions of the MongoRepository
Interface helping the service layer to persist and retrieve data from MongoDB. The service layer is defined under the service
package, considering the current case study it made sense to create two basic services - UserService
and BusReservationService
to satisfy the different business operations that the users are executing using the UI.
The security setting are present under the config
package and the actual configurations are done under classes present in the security
package. The application has different security concepts for the Admin portal and the REST APIs. For the portal, I have applied the default spring session mechanism that is based on the concept of sessionID and cookies. For the REST APIs, I have used JWT token based authentication mechanism.
Last, but the most important part is the controller layer. It binds everything together right from the moment a request is intercepted till the response is prepared and sent back. The controller layer is present in the controller
package. The best practices suggest that we keep this layer versioned to support multiple versions of the application and the same practice is applied here. For now, code is only present under v1
, but over the time, I expect to have different versions having different features. The Admin portal related controllers are present in the ui
package and its concerning form command objects are located under the command
package. The REST API controllers are located under the api
package and the corresponding request classes are located under the request
package.
Again, there are different opinions amongst the fraternity regarding the usage of separate classes for mapping the incoming request vs using the DTOs. I am personally a fan of segregating the two as far as possible to promote 'loose coupling' between UI and controller layer. The request objects and the form commands do give us a way to apply an additional level of validations on the incoming requests before they get converted to the DTOs which transfer valid information to the service layer for persistence and data retrieval. We could use DTOs here and some developers prefer that approach as it reduces some additional classes. However, I usually prefer to keep the validation logic separate from the transfer objects and hence am inclined to use the request/command
objects ahead of them.
The static resources are grouped under the resources
directory. All the UI objects and their styling aspects can be located here.
I have tried to experiment a bit with the RuntimeExceptions
and come up with a mini framework for handling the entire application's exceptions using a few classes and the properties file. If you carefully observe the exception
package. It consists of two Enums - EntityType
and ExceptionType
. The EntityType enum contains all the entity names that we are using in the system for persistence and those which can result in a runtime exception. The ExceptionType enum consists of the different entity level exceptions such as the EntityNotFound
and DuplicateEntity
exceptions.
The BRSException
class has two static classes EntityNotFoundException
and DuplicateEntityException
which are the two most widely thrown exceptions from the service layer. It also contains a set of overloaded methods throwException
which take the EntityType
, ExceptionType
and arguments to come up with a formatted message whose template is present under the custom.properties
file. Using this class I was able to empower the entire services layer to throw entity exceptions in a uniform manner without cluttering the code base with all sorts of NOT_FOUND
, DUPLICATE
entity exceptions.
For example: While logging in, if you try to use a email address which is not registered, an exception is raised and thrown using the following single line of code:
throw exception(USER, ENTITY_NOT_FOUND, userDto.getEmail());
This results in clubbing the names of these two Enums USER(user)
and ENTITY_NOT_FOUND(not.found)
and coming up with a key, user.not.found
, which is present in the custom.properties
files as follows:
user.not.found=Requested user with email - {0} does not exist.
By simply replacing the {0} param with the email address included in the thrown exception you can get a well formatted message to be shown to the user or to be sent back as the response of the REST API call.
Another important part of this mini framework is the CustomizedResponseEntityExceptionHandler
class. This class takes care of these RuntimeExceptions
before sending a response to the API requests. It's a controller advice that checks if a service layer invocation resulted in an EntityNotFoundException
or a DuplicateEntityException
and sends an appropriate response to the caller.
Last, the API response are all being sent in a uniform manner using the Response
class present in the dto/response
package. This class allows us to create uniform objects which result in a response as shown below (this one is a response for the api/v1/reservation/stops
api):
{
"status": "OK",
"payload": [
{
"code": "STPA",
"name": "Stop A",
"detail": "Near hills"
},
{
"code": "STPB",
"name": "Stop B",
"detail": "Near river"
}
]
}
And when there is an exception, like searching for a trip between two stops which are not linked by any bus, the following responses are sent back (result of api/v1/reservation/tripsbystops
GET request):
{
"status": "NOT_FOUND",
"errors": "No trips between source stop - 'STPD' and destination stop - 'STPC' are available at this time."
}
{
"status": "NOT_FOUND",
"errors": {
"timestamp": "2019-03-13T07:47:10.990+0000",
"message": "Requested stop with code - STPF does not exist.",
"details": "Requested stop with code - STPF does not exist."
}
}
As you can observe, both type of responses, one with a HTTP-200 and another with HTTP-404. The response payload doesn't change its structure in both cases and the calling framework can blindly accept the response knowing that there is a status and an error or payload field in the JSON object.
The user interface for the Admin portal is designed using material design with the help of Bootstrap and responsive web app concepts. The UI is server side rendered using Thymeleaf templates (preferred templating engine in Spring). The standard way of working with Thymeleaf is to use includes. This quite often leads to repetitive code, especially, when a website has many pages and each page has several reusable components (e.g. header, navigation, sidebar, and footer). It is repetitive as each content page has to include the same fragments at the same locations. This also has a negative effect when the page layout changes, e.g. when putting the sidebar from the left to the right side.
The decorator pattern used by the Thymeleaf Layout dialect solves these issues. In the context of template engines, the decorator pattern doesn't work with includes on content pages anymore, but it refers to a common template file. Each page basically only provides the main content and by describing which basic template to use the template engine can build the final page. The content is being decorated with the template file. This approach has advantages compared to the standard way of including fragments:
- The page itself only has to provide the content
- As a template file is being used to build the final page global changes can be applied easily
- The code becomes shorter and cleaner
- As each content page references which template file to use, it is easy to use different templates for different areas of the application (e.g. public area and admin area)
The layout for Admin portal is arranged as follows:
The individual areas in this layout serve the following purpose:
- Header: This fragment is used for the static imports, CSS & JavaScript, the title and meta tags
- Navigation: The navigation bar
- Content: The content placeholder that will be replaced by the requested page
- Sidebar: A sidebar for additional information
- Footer: The footer area that provides the copyright info
These components can be located in the resources/templates
directory at the root as well as under the sub-directories fragments and layout. The content area in this layout will host the following pages:
- Dashboard
- Agency
- Bus
- Trip
- Profile
Also, an error page for any unhandled exception is designed with the name error.html
. The login and signup pages are designed separately from the portal accessible to a logged in user.
To be able to run this Spring Boot app you will need to first build it. To build and package a Spring Boot app into a single executable Jar file with Maven, use the below command. You will need to run it from the project folder which contains the pom.xml file.
mvn package
Or, alternatively, you can use:
mvn install
To run the Spring Boot app from a command line in a terminal window you can use the java -jar command - provided your Spring Boot app was packaged as an executable jar file.
java -jar target/springboot-starterkit-1.0.jar
You can also use Maven plugin to run the app. Use the below example to run your Spring Boot app with Maven plugin:
mvn spring-boot:run
If you do not have a mongo instance running and still just want to create the JAR, then please use the following command:
mvn install -DskipTests
This will skip the test cases and won't check the availability of a mongodb instance.
You can follow any/all of the above commands, or simply use the run configuration provided by your favorite IDE and run/debug the app from there for development purposes. Once the server is setup you should be able to access the Admin interface at the following URL:
And the REST APIs can be accessed over the following base path:
Some important API endpoints are as follows:
Endpoint | Protocols | Methods |
---|---|---|
http://localhost:8080/api/v1/user/signup | HTTP | POST |
http://localhost:8080/api/auth | HTTP | POST |
http://localhost:8080/api/v1/reservation/stops | HTTP | GET |
http://localhost:8080/api/v1/reservation/tripsbystops | HTTP | GET |
http://localhost:8080/api/v1/reservation/tripschedules | HTTP | GET |
http://localhost:8080/api/v1/reservation/bookticket | HTTP | POST |
Command to build the container:
docker build -t spring/starterkit .
Command to run the container:
docker run -p 8080:8080 spring/starterkit
Note: When you build the container image and if mongodb is running locally on your system, you will need to provide your system's IP address (or cloud hosted database's IP) in the application.properties
file to be able to connect to the database from within the container.
Another alternative to run the application is to use the docker-compose.yml
file and utility. To build the application using docker-compose simply execute the following command:
docker-compose build
And to run the application, please execute the following command:
docker-compose up
It's as important to document (as is the development) and make your APIs available in a readable manner for frontend teams or external consumers. The tool for API documentation used in this starter kit is Swagger3, you can open the same inside a browser at the following url:
http://localhost:8080/swagger-ui/index.html
It will present you with a well-structured UI which has two specs:
- User
- BRS
You can use the User spec to execute the login API for generating the Bearer token. The token then should be applied in the Authorize popup which will by default apply it to all secured apis (GET, POST, etc).
The configuration of Swagger is being taken care of by class BrsConfiguration. I have defined two specs there with the help of "swaggerBRSApi" and "swaggerUserApi" methods. Since the login part is by default taken care of by Spring Security we don't get to expose its apis implicitly as the rest of the apis defined in the system and for the same reason I have defined a controller in the config package with the name "FakeController". Its purpose is to facilitate the generation of swagger documentation for login and logout apis, it will never come into existence during the application life cycle as the "/api/auth" api is being handled by the security filters defined in the code base.
Here are the various screens of the Admin portal that you should be able to use once the application is set up properly:
This project (including this README document) is an extension of the work of Arpit Khandelwal on spring-boot starterkit.