Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

How to Write a "Hello, World!" Microservice

DZone's Guide to

How to Write a "Hello, World!" Microservice

· Java Zone
Free Resource

Just released, a free O’Reilly book on Reactive Microsystems: The Evolution of Microservices at Scale. Brought to you in partnership with Lightbend.

What does implementing microservices mean for a software developer? Especially, for the rookies, greenhorns, and newbs out there? I’m not talking about microservice software architecture here; this is about microservices software development. And not just that, the ultimate implementation goal should be “microservices done right”.

For this post, I’ll go with Java. Yes, it’s wordy. Yes, it’s resource intensive (especially when used for the sole purpose of returning a single string). However the concept of classes and objects goes well with my intention of explaining how to do microservices correctly. Plus, it makes sense to use microservices in environments that are heavily biased towards Java.

Anyway, please feel free to add your own “Hello, World!” microservice in your favorite language in the comments section below.

Hello, monolith!

As a prerequisite, you should be familiar with the following piece of code, what it does, and why it has to look the way it does (read this tutorial  if you don’t):

class Starter {
  public static void main(String[] args) {
    System.out.println(“Hello, World!”);
  }
}

This is a simple console application that yields the string “Hello, World!” This is not written in the microservice way. This is an example of when not to use the microservices approach: if all you need on your console is a single string, this is all you need.

Hello, code duplication!

In addition to this console application, I want this string to be available on the web by calling http://localhost:80/helloWorld.servlet from a browser. Here is the required code, implemented as plain HTTP servlet (yes, it’s wordy. Get over it.)

class HelloWorldServlet extends HttpServlet {
  public void doPost(HttpServletRequest request,
    HttpServletResponse response)  throws ServletException, IOException {
  
    response.getWriter().println(“Hello, World!”);
  }
}

The string “Hello, World!” has to be “implemented” again. Sure, this is no big deal. But this simple string could be so much more. It could be the result of a complex calculation or it could be the result of a time consuming search query.

So, just imagine that the string “Hello, World!” is the result of a week’s worth of hard work (If you’re new to programming, it may very well be...). How should you go about making it available to apps and services that you create?

Step 1: HelloWorldService.java

To save yourself from duplicating a week’s worth of coding, allow me to introduce the HelloWorldService class:

class HelloWorldService {

  public String greet() {
    return “Hello, World!”;
  }

}

You can re-use this fine piece of software craftmanship in all your apps and classes without re-implementing or duplicating code.

Here’s our console application again:

class Starter {
  HelloWorldService helloWorldService = new HelloWorldService();
  
  public static void main(String[] args) {
    String message = helloWorldService.greet();
    System.out.println(message);
  }

}

The same goes for servlets:

class HelloWorldServlet extends HttpServlet {
  HelloWorldService helloWorldService = new HelloWorldService();

  public void doPost(HttpServletRequest request, 
    HttpServletResponse response) throws ServletException, IOException {

    String message = helloWorldService.greet();
    response.getWriter().println(message);
  }
}

It also works great for Spring MVC controllers:

@Controller
class HelloWorldController {
  HelloWorldService helloWorldService = new HelloWorldService();

  @RequestMapping("/helloWorld")
  public String greet() {
    String message = helloWorldService.greet();
    return message;
  }
}

I could go on and show more examples, but I think you get the point (spoiler: it’s the bold lines that matter).

Those of you who are familiar with microservices could point out that this may be fine for getting rid of code duplication, but this is no microservice.

You’re right, but to get to “microservices done right,” you have to be able to separate you app’s concerns, which is what I did here in the most possible basic way: I separated the app’s frontend concerns from its backend concerns. The frontend is either a console app or a servlet, the backend is HelloWorldService.

Serviceward, ho!

To go down microservice lane from here, all we have to do is wrap HelloWorldService into some kind of web component that makes it accessible via HTTP, right? Let’s see…

First, we could just use our servlet code from above, as it conveniently returns the string as a response to any HTTP request. But we won’t. Why? Because there’s something missing: fault tolerance.

What could possibly fail when returning a simple string? That’s not the point. What matters is that the client side (the code that calls HelloWorldService) should be given enough information to effectively react to failures.

We face two possible problems:

  1. The service as a whole may be unavailable

  2. The service may be unable to return a proper response

The service is unavailable

If a service is unavailable, it’s the client that is responsible for dealing with the situation. Frameworks like unirest.io save you the effort of writing many lines of code when dealing with HTTP requests.

Future<HttpResponse<JsonNode>> future = 
  Unirest.post("HTTP://helloworld.myservices.local/greet")
  .header("accept", "application/json")
  .asJsonAsync(new Callback<JsonNode>() {

    public void failed(UnirestException e) {
      //tell them UI folks that the request went south
    }
  
    public void completed(HttpResponse<JsonNode> response) {
      //extract data from response and fulfill it’s destiny
    }

    public void cancelled() {
      //shot a note to UI dept that the request got cancelled
    }
  }
);

With this code, the client now knows when the service is not available or has timed out following no response. Wee can easily have an error message displayed in place of the string we expected to receive. Try/catch is probably the right solution here.

Invalid responses however pose more of a challenge.

The service fails

If the service fails, we can just return a string with an appropriate error message. But how can you know if a message is an error message or a correct response? Yes, you can start every error message with [ERROR] or invent another “smart” (read: not-so-smart) workaround, but this won’t be a solution you’ll be proud of. And, there’s always the possibility that even valid responses may begin with ERROR because it’s simply part of the message.

I’d go with JSON or XML for wrapping the answer. I prefer JSON because it’s a little less wordy than XML. And I really like using the JSON-HTML tool over at json.bloople.net  for visualizing results during development. Of course, you might go for any of the numerous alternatives, like protobuf or a proprietary solution of your own. The main point is that you need to be able to apply structure to responses:

{
  “status”:”ok”,
  ”message”:”Hello, World!”
}

By checking the status attribute, you can easily decide whether to handle an error or to display an appropriate message.

{
  “status”:”error”,
  ”message”:”Invalid input parameter”
}

The possibilities are endless here. You can add an error code or additional properties. This all boils down to a single important point: apply structure to your responses.

Structure, why?

Because structure not only helps you keep your code maintainable, it also serves as the foundation of the API of your service.

An API definition consists of more than a URL like this:

GET HTTP://helloworld.myservices.local/greet

API definitions also consist of the response structures that can be expected as a response (you know this already from a few lines back):

{
  “status”:”ok”,
  ”message”:”Hello, World!”
}

Most important takeaway

Keeping the API specifications of a service’s request and response stable is a key requirement for succeeding with microservices.

Conclusion

Are you (and your project) ready for microservices? If you read this and kept asking yourself, what good is all the overhead of microservices, then either your project won’t benefit from microservices or you’re just not there yet (for mindset perspective see my  previous post about the value of microservices ).

If you can’t stop thinking about microservices, then you probably are ready.

Strategies and techniques for building scalable and resilient microservices to refactor a monolithic application step-by-step, a free O'Reilly book. Brought to you in partnership with Lightbend.

Topics:

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}