Introduction
The Flagship project is inspired by the recent buzz around twelve-factor applications and Microservices. The project is intended to show that it is fairly easy to build modern RESTful services with just a few dependencies.
While building the app, I've tried to highlight a few notable characteristics of twelve-factor apps -
- Explicitly declare and isolate dependencies
- Store config in the environment
- Exports HTTP as a service by binding to a port
- Maximize robustness with fast startup and graceful shutdown
- Stateless and share-nothing
Twelve-factor apps aside, this example is slightly more inspired by the notion that everyone should understand how to build Microservices from scratch with just a few dependencies. Most of the recent server-side apps I've built tend to start with the basic concepts and app structure described below. There are also several open source projects that start with similar foundations; Spring Boot, Netflix Pytheas and Dropwizard are a few examples.
Understanding the building blocks will help you understand your chosen framework. Additionally, I hope that it illustrates that there are a variety of options for each building block in the web stack. For example, the project uses Mybatis for data access although, could have just as easily use JPA or Spring JDBC templates.
As applications grow (multiple apps, multiple services), I'm also a slight fan of having all the services or components within large systems reside in a single, versioned repo although, I'll save that conversation for a future example.
Project Structure
The example walks you through a basic Java Microservice in a few steps. For discussion purposes, I've captured the steps via the below Git tags -
git tag -ln
v0.1 Initial App
v0.2 Introducing Guice and App Environment
v0.3 Introducing Jetty and Jersey
v0.4 Introducing HealthChecks and Jackson
v0.5 Basic persistence with MyBatis and MySQL
(v0.1) Initial App
The v0.1 tag starts with a simple Java class, App.java. The App includes both start and stop methods as well as an initial test class. The test class extends AppRunner which will be used for both integration and acceptance tests.
I'm using Maven's pom file for dependency declaration and java -jar for dependency isolation. In fact, all the dependencies are "vendored" via maven within a single shaded jar.
Current dependencies are slf4j and junit.
Run the app -
$ java -jar target/flagship-1.0-SNAPSHOT.jar
(v0.2) Introducing Guice and App Environments
v0.2 makes a slight nod to Agile web development and Rails by introducing Development, Test, and Production environments. The Environment class leverages both Java property files and local environment variables. Storing the config the environment is a characteristic of twelve-factor apps.
I understand bundled environments are in slight contrast with twelve-factor apps although, I've found that using the 2 patterns in conjunction hasn't introduced config explosion or brittleness over the years. That said, behavior shouldn't change based on a config setting.
The Environment is injected via Guice although, Spring would have worked just as easily. New dependencies include Guice.
Run the app -
$ export application_environment=development
$ java -jar target/flagship-1.0-SNAPSHOT.jar
(v0.3) Introducing Jetty and Jersey
I've used the Guice Servlet lib and Jersey's Guice contribs in conjunction with Jetty (Guice and Jersey) to stitch together the server component. I'll typically pull in Jetty (by habit) although, Netty and Tomcat are also great choices. Line 24 illustrates exporting HTTP as a service by binding to a port; in this case, Jetty is bound to port 8080.
You'll need to understand a bit about event listeners and filters - essentially the servlet specification - to fullly grasp the lines 24-25 in the App class. At a high level, the Listener class enables Jetty with Guice support and the ResourceModule class describes the available REST endpoints via classes and annotations. FWIW - I always recommend reading the servlet spec if you're a serious Java developer - or at a minimum having at least one person on your team that has read the spec ;)
I've penciled in a RESTful NoopController that uses the javax.ws.rs package and simply returns plain text. The next tag will support the JSON media type. Note, the AppTest now becomes more interesting leveraging apache http components for acceptance tests which run via mvn clean install.
The new code allows for fast startup via embedded Jetty and a graceful shutdown via the Java's Runtime shutdown hook. New dependencies include Jersey and Jetty.
(v0.4) Introducing HealthChecks and Jackson
Production support is always interesting - and, with a modern PaaS, support becomes much easier. I've created a BasicHealthCheck to return app specific health; memory usage, connected sub-systems, etc. JMX also works well although, the HealthCheck provides a nice step to introduce Jackson for JSON responses.
New dependencies include Jackson.
(v0.5) Basic persistence with MyBatis and MySQL
I've always enjoyed MyBatis. It's a data mapping tool that's a step below pulling in a full-blown O/RM.
The last tag includes a Project model, controller, and data access object. The test directory includes both integration and acceptance tests for the GET Projects endpoint. The README.md file includes manual instructions for setting up your local database. It also makes a few assumptions around naming conventions.
New dependencies include MyBatis and MySQL.
Run the app -
$ export application_environment=production
$ export DATABASE_URL='jdbc:mysql://localhost:3306/flagship_development?user=flagship&password=flagship'
$ java -jar target/flagship-1.0-SNAPSHOT.jar
Summary
That's it for now. I hope the example helps and illustrates that you could create a twelve-factor microservice from scratch with a few basic dependencies.
Thanks! (@barinek) and thanks to (@dajulia3) for the copy edit!