An easy introduction to micro services with Vertx and Java

In this post I want to focus on implementing a micro service architecture with Vertx framework and Java. While there are different articles and tutorials (and many are brilliant!) on this topic, I want to explain a Endpoint-Service-DAO model, that I use to develop reactive backends for mobile apps.

In a nutshell, it is a same microservice system, but is focused on faster development for mobile backends. If you just moving to micro services, it may be a good introduction for you.

Why do we need to use microservices?

Adapting an microservices architecture is breaking down an application into smaller units of computation and deploying and running them separately. But what advantages would it bring to us? There are following reasons to implement this pattern:

  1. Faster time-to-market for new functionality
  2. Using the best suitable tech for each concrete domain
  3. Better fault isolation and less impact on other services
  4. Better scalability
  5. Better reusability
  6. Promise of Improved Return on Investment (ROI), and better Total Cost of Ownership (TCO), achieved through faster development, and the use of cheaper hardware needed to run smaller services

Migrating from a monolith architecture into microservices one usually employ a breaking down your application into single domains.

What is Endpoint-Service-DAO?

Verticles are an important concept for Vertx ecosystem. A verticle is a unit of deployment, that allows to developers to encapsulate code for specific purposes. Verticles seem to be a good starting point for you, if you want to move into a micro services architecture, however to avoid (at least partly) a common drawback of this approach – increasing complexity of development and deployment. I like to call it Endpoint-Service-DAO architecture or “a semi-microservice pattern”, because it is not a fully micro service methodology in strict theoretical sense. However it enables reactivity in your apps and makes them easy to maintain and develop. And, importantly, it is much easier to deploy.

Microservices attempt to deploy independent services with bounded contexts.

Source: Rick Hightower (2016). Microservices Architecture.
http://www.mammatustech.com/java-microservices-architecture

Let dive into practice. Consider this graph 1 bellow. This is a simplified representation of the Endpoint-Service-DAO architecture. You could say that is just a micro service pattern, and you would be close to truth.

Graph 1. A helicopter view of the Endpoint-Service-DAO application

Commons

  • There is an API gateway, represented by ApplicationEndpoint instance. It is responsible for routing and authentication
  • We use messaging (via EventBus)
  • Clients talk to server using REST API
  • Services are decomposed by subdomains
  • Each Service has its own Database per service

Differences

  • AuthProvider is not a separate service, but a part of API gateway. It is done due to its specific nature. We need it to assert JWT tokens inside router and restrict access immediately for wrong tokens, so it is cheaper to have inside and to avoid overloading with EventBus with tons of tokens
  • REST API is unified for all clients. Unlike to the normal approach, I don’t believe in separate gateways for web and mobile clients, and I tend to offer same APIs for both types of clients

Start with AppEndpoint…

Now, we can get hands dirty and write some Java code. Vertx.Core provides all major functionality we need to implement Endpoint-Service methodology – Verticles, EventBus and Consumer-Sender messaging. We also need Vertx.Web to implement routing. Take a look into graph 2, that represents an architecture of our sample application:

Graph 2. A structure of the example application for this post

AppEndpoint is responsible to creating a web server and authentication of incoming requests. It communicates with separated Services via EventBus. We would not focus on security and routing concerns now, because it is outside of a scope of this post (full code for AppEndpoint is available on github):

private EventBus bus;
private AuthProvider authProvider;

@Override
public void start(Future<Void> startFuture) throws Exception {
    super.start(startFuture);
    Future<Void> initSteps = initAuthProvider().compose(v->initServer());
    initSteps.setHandler(startFuture.completer());
}


private Future<Void> initServer(){

    Future<Void> fut = Future.future();

    //initialize web server and router
    HttpServer server = vertx.createHttpServer();
    Router router = Router.router(vertx);

    //Don't forget to get an eventbus reference!
    this.bus = vertx.eventBus();

    //secure routes with AuthProvider
    router.route("/secure/*").handler(JWTAuthHandler.create(authProvider));

    //assign hanlder to routes
    router.get("/secure/chat/:id").handler(this::getChats);

    //assign server
    server.requestHandler(router).listen(4567, res->{
        if (res.succeeded()){
            System.out.println("Created a server on port 4567");
        } else {
            System.out.println("Unable to create server: \n"+res.cause().getLocalizedMessage());
            fut.fail(res.cause());
        }
    });

    return fut;
}

/*...Other stuff, like initAuthProvider and login handler...*/

Let look deeper on getChats() method. In this code snippet we tell our Endpoint to:

  1. Dispatch a route parameter – get an id of the requested chat
  2. Create Request. It is a plain JsonObject, we use to communicate between verticles. For Vertx it does not matter what you would send via EventBus – you literally can send your own POJOs. But, due to the polyglot nature of Vertx, it is preferable to stick to generic JSONs
  3. Send a message to EventBus. We specify the requires address, payload and set handler to deal with sending result.
  4. Finally, we return a data to user.
private void getChats(RoutingContext ctx){

    String chatId = ctx.pathParam("id");

    JsonObject request = new JsonObject();
    request.put("chatId", chatId);

    //send request to consumer
    bus.send("consumer.service.messages.getchat", request, res->{
        if (res.succeeded()){
           ctx.response()
              .setStatusCode(200)
              .end(Json.encodePrettily(res.result().body()));

        } else {

            System.out.println("Unable to get a chat:\n"+res.cause().getLocalizedMessage());
            ctx.response().setStatusCode(404).end();
        }
    });
}

Note, that we pass to end() a raw result’s body, without any conversion. I believe, that all POJOs conversions must be performed inside services, to prevent coupling of the AppEndpoint with business logic.

…continue with MessageService.

Next, let create a MessageService (for chats, don’t confuse it with eventbus messages). This class also extends AbstractVerticle, and in start() method we create a consumer to consume a data from Endpoint.

@Override 
public void start() throws Exception { 
    super.start();

    //get a reference of Eventbus instance
    EventBus bus = vertx.eventBus();

    //create consumer to consume request data
    bus.consumer("consumer.service.messages.getchat").handler(msg->{

        //get request from Message
        JsonObject request = JsonObject.mapFrom(msg.body());

        //perform DAO operation to fetch data from datasource
        Chat result = chatDao.find(request.getString("chatId"));

        //reply to Sender with reply()
        msg.reply(Json.mapFrom(result));
    });
 }

I think, that the mentioned code snippet is self-explanatory and does not need deep explanations. We use to exclude specific data access code and to inject it with dependency injection.

Conclusion

In this post, I have highlighted an idea of the Endpoint-Service-DAO architecture, that I like to call a “semi-microservice” pattern, because it is not strictly a microservice pattern in its theoretical sense. We have created an AppEndpoint that serves as an API Gateway, and MessagesService that is responsible for messaging between users (chats, don’t confuse it with EventBus messaging ­čÖé ).

If you would like to have a complete practical introduction in the topic of microservices with Java and Vert.x, I suggest you to follow my free tutorial:Build a micro service application with Vert.x.

References

To get a better understanding of concepts, I used in my article/code, I encourage you to review these materials. However, it is completely up to you ­čÖé

  1. Yuri Mednikov (2019). ‘How I reinvented a wheel: Building SMS auth in Vertx with Java’. Mednikov.net Read
  2. Dr. Andr├ę Fachat. Challenges and benefits of the microservice architectural style (2019). IBM Developer Read
  3. H├╝seyin Akdo─čan (2018). ‘How to Work With Multiple Verticles and Communication in Vert.x’. DZone Read
  4. Rick Hightower (2016). ‘Microservices Architecture’. Read
  5. ‘Core J2EE Patterns – Data Access Object’. Oracle Technology network Read