Building and Testing a Microservice in a Service-Oriented Architecture
by June 23, 2014

Filed under: Performance Monitoring

AppNeta no longer blogs on DevOps topics like this one.

Feel free to enjoy it, and check out what we can do for monitoring end user experience of the apps you use to drive your business at www.appneta.com.

Building a single app is hard enough. Building out multiple capabilities into that app is even harder. As part of our ongoing experience to build a full-stack monitoring tool, we recently refactored our logins out of the individual modules we’ve built and into a Microservice, built in Java. We would like to share our experience in building this service.

What is a Microservice?

The microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery.

Martin Fowler

The business case for us was the AppNeta Identity Manager (or AIM in short). The goal of AIM is to provide a shared global organizational information for all our products: PathView, TraceView, FlowView, and AppView.

Inspired by the recent series of “An Opinionated Guide to Modern Java”, we thought it would be useful to share how we built our Microservice and the logic behind our tool choices.

Architecture of Microservice

For companies that have multiple products in their portfolio, Microservices usually sit behind the products in terms of the infrastructure. For instance, if we have 2 microservices and 3 applications, some applications may consume both microservices, and others may only consume one or the other.

microservice service oriented architecture

Technology Stack

One of the advantages of architecting your application in this style is that Microservices aren’t tied to a particular technology stack. This gave us the flexibility to choose technologies instead of defaulting to a technology that may or may not make sense. When we had our first meeting to discuss what technology stack we should use, we came up with a short list of preferred stacks: NodeJS/JS, Ruby/Sinatra, Python/Flask, Java/JAX-RS. Out of the four, we decided to use Java/JAX-RS due to our experience managing Java based applications (JVM has excellent tools around it) and the excellent tracing capabilities of our TraceView stack.

We also decided to stick with the same persistence technology as the rest of the applications: PostgreSQL on Amazon RDS (PostgreSQL). Here’s a simplified version of our architecture.

microservice service oriented architecture

The entry point to our microservice is the Amazon Elastic Load Balancer that can distribute the load to any of the EC2 instances within the Auto Scaling Group. Each microservice has it’s own autoscale group, where Amazon is responsible for adding or removing EC2 instances depending on the load.

Libraries and Frameworks

Java has one of the richest ecosystems among other programming languages/platforms out there and when it comes to tools, libraries, and frameworks, there are endless options. We decided to use select few of the Spring libraries (and not the whole framework) to augment JAX-RS. The Spring libraries that we use are as follows: Spring Security, Spring Core, Spring-JAX-RS integration, and Spring-Data to augment our JPA/Hibernate ORM.

We made a couple of specific choices that made building this service much easier.

1. Spring-Data

Those who have used JPA before knows that the EntityManager can only handle simple fetch by object “id”. Any other queries would require you to write JPQL.

Let’s assume we have a User table schema as follows:

id

username

email

active

1

appneta

AppNeta@appneta.com

true

To fetch the user based on case insensitive email address, one must write the following JPQL query in JPA:

String queryInJpql = “SELECT u FROM Users u WHERE lower(u.email) = ?1”;
List<User> users = em.createQuery(queryInJpql)
  .setParameter(1, email).getResultList();

Spring-Data, on the other hand, came up with magical solution that requires no query language involved:

// Note that this method exist in a Java interface without any implementation provided
// Spring will inject the actual implementation via dependency-injection
Collection<User> findByEmailIgnoringCase(String emailAddress);

What if the operation requires a transaction? For instance, an update operation?

@Modifying
@Transactional
@Query(“UPDATE User u SET u.active = true WHERE u.email like ?1”)
void activateUserBasedOnEmail(String email);

It looks like Spring Data still needs a bit of JPQL help but there is still not much Java code involve here as all of the code above are: annotation, method declaration (interface, no implementation), and a little bit of JQPL.

2. JAX-RS

I can’t say enough of good things about JAX-RS. This is a well-designed RESTful library that is part of the JavaEE standard ever since version 6. JAX-RS has gotten better with every release since its inception.

Let’s say we have a very simple endpoint for User that should support CRUD (Create-Retrieve-Update-Delete).

Here’s what the method that will execute the operation looks like:

@Path("/user")
public class UserResource{
@POST
@Produces(APPLICATION_JSON)
@Consumes(APPLICATION_JSON)
public User create(User newUser){ … }
@GET
@Path(“/{id})
@Produces(APPLICATION_JSON)
public User retrieve(@PathParam(“id”) int id){
    // will throw javax.persistence.NoResultException
    // but we have implemented an exception mapper
    // that maps NoResultException => 404
    User user = userRepository.find(id);
    return user;
}
@PUT
@Produces(APPLICATION_JSON)
@Consumes(APPLICATION_JSON)
public User update(User user){ … }
@DELETE
@Path(“/{id})
public Response delete(@PathParam(“id”) int id){ … }
}

To create a user, perform an HTTP POST (the @POST annotation) to the following relative URL: “/user” and send the User object in JSON format (denotes by @Consumes annotation) as the Request body. If the operation is successful, the caller will receive the newly updated resource in JSON format as well (the @Produces annotation).

The best part about JAX-RS is that you can specify multiple request/response format without affecting your business logic code (assuming your object can be serialize/deserialize):

@Produces({APPLICATION_JSON, APPLICATION_XML})
@Consumes({APPLICATION_JSON, APPLICATION_XML})

Let’s say your client demanded that they will only process XML and don’t know what this funky thing called JSON.

Let’s compare JAX-RS resource class with Ruby on Rails ActionController

class UsersController < ApplicationController
  def index
    @users = User.all
    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render xml: @users}
      format.json { render json: @users}
    end
  end
end

In Rails, you’d have to write the rendering code in each of the controller methods and it is part of your method logic.

Here’s another example in Sinatra:

get ‘/user/:id’ do
  content_type :json
  @user = User.get(params[:id]
  if @user
    @user.to_json
  else
    halt 404
  end
end

In Sinatra, you’d have to explicitly handle the exceptional case in your code; in this case, if user with certain ID is not found, 404 will be returned. Content type handling is more or less the same with Rails: embedded within the logic of your code.

JAX-RS, on the other hand, has a concept of Exception Mapping (albeit more code and more verbose):

@Provider
public class ResourceNotFoundMapper implements ExceptionMapper<NoResultException> {
  @Override
  public Response toResponse(NoResultException e) {
    return Response.status(Status.NOT_FOUND)
            .entity(new ErrorResponse(Status.NOT_FOUND.getStatusCode(), e))
            .type(APPLICATION_JSON).build();
  }
}

Once we mapped NoResultException to 404, anytime JAX-RS resource class encountered that particular exception before sending the response, it will translate it to a proper WebApplicationException with status code 404. This makes your Java logic code straightforward without specific logic that ties to the REST/HTTP paradigm.

In short, JAX-RS makes dealing with REST super simple!

3. Database Migration

The last piece of the development libraries we use is Flyway to handle our database migration.

Flyway is an interesting database migration library for 2 reasons:

  • It does not perform rollback (design principle, since um… you know, certain complex migration cannot be rolled back anyway)
  • It allows you to write the migration using either Java or SQL

Flyway can be executed in two ways: outside or as a part of the software launch. Outside the running application means that someone or a script will be executed before the software is launched.

An example of Flyway being executed while the application starts up is when you deploy a web application to Tomcat; during the start-up of the service/web application, Flyway will run first. If the migration does not work, the deployment can be marked as failure hence the service/web-application will not be deployed/work.

Flyway relies on convention over configuration for the migration script/code.

microservice service oriented architecture

(The above image is taken from the Flyway website)

Java-based migration example:

/**
 * Example of a Spring Jdbc migration.
 */
public class V1_2__Another_user implements SpringJdbcMigration {
    public void migrate(JdbcTemplate jdbcTemplate) throws Exception {
        jdbcTemplate.execute("
INSERT INTO user (id, username, email, active)
VALUES (1, ‘appneta’, ‘AppNeta@appneta.com’, true)"
        );
    }
}

Conclusion

With AIM launched and in production, we’ve been really happy with this architectural style. It allowed us not only to refactor a bunch of common code into one place, but also to separate the operational concerns of authentication vs. data processing and storage. With these libraries, we’ll be looking to build out further Microservices in other parts of the stack.

But we’re not done here! In part 2, we’ll look at how we tested this service prior to rolling it out.