Monday 14 January 2019

How to build simple microservice using Spring Boot

How to build simple microservice using Spring Boot

Nowadays many organizations prefer building their enterprise applications using MicroService architecture. In java community, SpringBoot is the most widely used framework for building MicroServices.

Why MicroServices?

Monolithic applications are large enterprise applications which builds in modularised fashion but finally deploy them together as a single deployment unit(EAR or WAR). These kind of applications have some issues like:
  • Large codebases become mess over the time
  • Multiple teams working on single codebase become tedious
  • It is not possible to scale up only certain parts of the application
  • Technology updates/rewrites become complex and expensive tasks
But, A MicroService is a service built around a specific business capability which can be independently deployed. So, to build large enterprise applications we can identify the sub-domains of our main business domain and build each sub-domain as a MicroService using Domain Driven Design (DDD) techniques. But in the end, we need to make all these microservices work together to serve the end user as if it is a single application tasks.

Advantages of MicroServices

  • Comprehending smaller codebase is easy
  • Can independently scale up highly used services
  • Each team can focus on one (or few) MicroService(s)
  • Technology updates/rewrites become simpler
For an explanation about microservices, read this article of Martin Fowler.

Build a simple MicroService using SpringBoot:

You can download this example code from here.

I am going to build a simple EmployeesAPI which can be accessed through REST. For this example I am going to use in-memory database H2 to store and retrieve employees.I have exposed 4 REST endpoints with which we can:
  • get all employees names: GET /api/employee
  • get a specific employee: GET /api/employee/{id}
  • add an employee: POST /api/employee
  • delete an employee: DELETE /api/employee/{id}
We need to create all these files:

  • pom.xml
  • EmployeesApplication.java
  • Employee.java
  • EmployeeRepository.java
  • EmployeesDataController.java
  • application.properties
  • data.sql
How to build simple microservice using Spring Boot

Let us look at pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.engineeernitesh</groupId>
    <artifactId>employeesapi-docker</artifactId>
    <version>1.0.1</version>
    <packaging>jar</packaging>
    <name>Employees database</name>
    <description>A simple application in which you can store and retrieve employees</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
        <relativePath />
    </parent>

    <properties>
        <docker.image.prefix>niteshkumar</docker.image.prefix>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!-- h2 db -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.194</version>
        </dependency>
        <!--springfox dependency -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.7.0</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <!-- dockerfile plugin -->
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>dockerfile-maven-plugin</artifactId>
                <version>1.3.6</version>
                <configuration>
                    <repository>${docker.image.prefix}/${project.artifactId}</repository>
                    <tag>${project.version}</tag>
                    <buildArgs>
                        <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
                    </buildArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>


</project>

You can see I added docker related properties and springfox dependencies in pom.xml but I will discuss these 2 in detail in my next post.

We have 1.5.9.RELEASE as a basis. We then have 5 dependencies:
spring-boot-starter-web for libraries to creating the rest service. 
spring-boot-starter-data-jpa for the jpa capability.
spring-boot-starter-security for spring security
h2 for in-memory database.
springfox for swagger implementation.

I am also adding the spring-boot-maven-plugin to be able to run it from maven using Tomcat.

EmployeesApplication.javaThis is the starting point of our simple service. It will look like this:

package com.engineeernitesh.employeesapi;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication
@EnableJpaRepositories
public class EmployeesApplication {

    public static void main(String[] args) {
        SpringApplication.run(EmployeesApplication.class, args);
    }

}
@SpringBootApplicationThe entry point of the Spring Boot Application is the class contains @SpringBootApplication annotation. This class should have the main method to run the 
Spring Boot application. @SpringBootApplication annotation includes Auto- Configuration, Component Scan, and Spring Boot Configuration
@EnableJpaRepositories - @EnableJpaRepositories is to use spring and JPA for database access.

Employee.java: This is a POJO class and this looks like:

package com.engineeernitesh.employeesapi;

import io.swagger.annotations.ApiModelProperty;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "employee")
public class Employee {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @ApiModelProperty(notes = "The generated employee id", hidden = true)
    private long id;
    @ApiModelProperty(notes = "The employees firstname", required = true)
    private String firstname;
    @ApiModelProperty(notes = "The employees lastname", required = true)
    private String lastname;
    
    
    public Employee(){
    }
    
    public Employee(String firstname, String lastname){
        this.firstname = firstname;
        this.lastname = lastname;
    }
    /**
     * @return the id
     */
    public long getId() {
        return id;
    }

    /**
     * @param id the id to set
     */
    public void setId(long id) {
        this.id = id;
    }

    /**
     * @return the firstname
     */
    public String getFirstname() {
        return firstname;
    }

    /**
     * @param firstname the firstname to set
     */
    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }

    /**
     * @return the lastname
     */
    public String getLastname() {
        return lastname;
    }

    /**
     * @param lastname the lastname to set
     */
    public void setLastname(String lastname) {
        this.lastname = lastname;
    }
    
 @Override
    public String toString() {
        return String.format("Employee[id=%d, code='%s', name='%s']",
                id, firstname, lastname);
    }

    
}
This POJO class have 3 attributes mapping to the 3 columns with getter and setters. The annotation which does all the magic here is @Entity. This tells spring that it can be used for Object Relational Mapping. 

EmployeeRepository.java: This class is used to fetch, save and delete data.

package com.engineeernitesh.employeesapi;

import javax.transaction.Transactional;
import org.springframework.data.repository.CrudRepository;

@Transactional
public interface  EmployeeRepository extends CrudRepository<Employee, Long> {

    Employee findById(long id);
    
    Employee findByLastname(String Lastname);

}
Here extended spring's CrudRepository and defined 2 extra interface for retrieving an employee based on id and last name.
EmployeesDataController.javaNow for the service part. Spring also has easy ways to accommodate this using the @RestController annotation.


package com.engineeernitesh.employeesapi;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("api/employee")
@Api(value="employees-home")
public class EmployeesDataController {

    private final static Logger LOGGER = Logger.getLogger(EmployeesDataController.class.getName());

    @Autowired
    EmployeeRepository employeeRepository;
    
    //-------------------Retrieve all employees--------------------------------------------------------
    @RequestMapping(value = "", method= RequestMethod.GET, produces = "application/json")
    @ApiOperation(value = "View a list of employees", response = Iterable.class)
    List<Employee> getEmployees() {
        List<Employee> result;
        LOGGER.log(Level.INFO, "Getting all employees");
        result = new ArrayList();
        Iterable<Employee> employeeList = employeeRepository.findAll();
        for (Employee employee : employeeList) {
            result.add(employee);
        }
        return result;
    }
    
    //-------------------Retrieve a employee by id--------------------------------------------------------
    @RequestMapping(value = "/{id}", method= RequestMethod.GET, produces = "application/json")
    @ApiOperation(value = "Get an employee by id", response = Employee.class)
    public Employee getEmployee(@PathVariable long id) {
        Employee result;
        LOGGER.log(Level.INFO, "Getting employee with id " + id);
        result = employeeRepository.findById(id);
        return result;
    }
  
    //-------------------Add an employee--------------------------------------------------------------------
    @PreAuthorize("hasRole('ADMIN')")
    @RequestMapping(method = RequestMethod.POST, produces = "application/text")
    @ApiOperation(value = "Add a new employee")
    public ResponseEntity saveEmployee(@RequestBody Employee input) {
        LOGGER.log(Level.INFO, "Saving employee " + input.getLastname());
        Employee employee = new Employee();
        employee.setFirstname(input.getFirstname());
        employee.setLastname(input.getLastname());    
 employeeRepository.save(employee);
        return new ResponseEntity("Employee saved successfully", HttpStatus.OK);
    }
    
    //-------------------Delete a employee by id------------------------------------------------------------
    @PreAuthorize("hasRole('ADMIN')")
    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE, produces = "application/text")
    @ApiOperation(value = "Delete an employee by id")
    public ResponseEntity deleteEmployee(@PathVariable long id) {
        LOGGER.log(Level.INFO, "Deleting employee " + id);
 employeeRepository.delete(id);
        return new ResponseEntity("Employee deleted successfully", HttpStatus.OK);
    }
}

The @RestController annotation is used to define the RESTful web services. It serves JSON, XML and custom response.

The @RequestMapping annotation is used to define the Request URI to access the REST Endpoints. We can define Request method to consume and produce object.

The @RequestBody annotation is used to define the request body content type.

The @PathVariable annotation is used to define the custom or dynamic request URI. The Path variable in request URI is defined as curly braces {}.

@ApiOperation, @PreAuthorize annotation will discuss on swagger post.

application.properties: This properties file is used to database, H2 and hibernate settings.

###
#   Database Settings
###
spring.datasource.url=jdbc:h2:mem:climbers-db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.platform=h2
spring.datasource.username = sa
spring.datasource.password =
spring.datasource.driverClassName = org.h2.Driver
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

###
#   H2 Settings
###
spring.h2.console.enabled=true
spring.h2.console.path=/console
spring.h2.console.settings.trace=false
spring.h2.console.settings.web-allow-others=false

###
#   Hibernate Settings
###
spring.jpa.hibernate.ddl-auto = update
spring.jpa.properties.hibernate.show_sql=false
spring.jpa.properties.hibernate.use_sql_comments=false
spring.jpa.properties.hibernate.format_sql=false


#set sql level to debug to see all the sql statements
logging.level.org.hibernate.SQL=debug
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n

data.sql: We will add some data into employee table.

INSERT INTO employee (id, firstname, lastname) VALUES (1, 'Nitesh', 'Kumar')
INSERT INTO employee (id, firstname, lastname) VALUES (2, 'Debarshi', 'Da')
INSERT INTO employee (id, firstname, lastname) VALUES (3, 'Sumantra', 'Pal')
Now run the application using maven or simply go to EmployeesApplication class and run as java application. Application deployed into tomcat and run on default port 8080.

How to build simple microservice using Spring Boot

Now go to postman or RestClient and try to run http://localhost:8080/api/employee. You should have employees list.

How to build simple microservice using Spring Boot

Similarly, we can run http://localhost:8080/api/employee/1 and other URL's.


Next post:

Implementation of swagger in SpringBoot API

How to run SpringBoot API as a Docker container

How to Deploy your docker container on Amazon EC2 Elastic Container Service

4 comments: