DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Navigating and Modernizing Legacy Codebases: A Developer's Guide to AI-Assisted Code Understanding
  • Tired of Spring Overhead? Try Dropwizard for Your Next Java Microservice
  • Build a Stateless Microservice With GitHub Copilot in VSCode
  • From Prompt to Running Microservice: ServiceBricks Step-By-Step

Trending

  • Mastering Advanced Traffic Management in Multi-Cloud Kubernetes: Scaling With Multiple Istio Ingress Gateways
  • Designing for Sustainability: The Rise of Green Software
  • Navigating Double and Triple Extortion Tactics
  • After 9 Years, Microsoft Fulfills This Windows Feature Request
  1. DZone
  2. Data Engineering
  3. Data
  4. How to Write a "Hello, World!" Microservice

How to Write a "Hello, World!" Microservice

By 
Martin Goodwell user avatar
Martin Goodwell
·
Mar. 13, 15 · Interview
Likes (7)
Comment
Save
Tweet
Share
31.0K Views

Join the DZone community and get the full member experience.

Join For Free

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.

microservice

Opinions expressed by DZone contributors are their own.

Related

  • Navigating and Modernizing Legacy Codebases: A Developer's Guide to AI-Assisted Code Understanding
  • Tired of Spring Overhead? Try Dropwizard for Your Next Java Microservice
  • Build a Stateless Microservice With GitHub Copilot in VSCode
  • From Prompt to Running Microservice: ServiceBricks Step-By-Step

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!