Microservices is an architectural style used for creating applications made of individual fine-grained services, which encourages functional decoupling, re-usability, and scalability. This is becoming increasingly popular compared to the monolithic applications we generally build. Monoliths generally lack the flexibility given by microservices in separating out functional components to have their own independent development teams, lifecycles, and deployment. Java still remains the most popular language developers use in developing enterprise applications. And with no surprise, there are plenty of Java frameworks that support microservices development as well. Among these, is arguably the most popular option out there. Amidst these options, takes a different approach in providing a programming language purpose-built for optimizing microservices development (full disclosure - I contribute to the Ballerina project). Spring Boot Ballerina In this post, I will start with a Spring Boot application implementation and show the same functionality implemented using Ballerina. The sample use case I’ll be implementing is based on a student registry scenario. Here, an in-memory registry is kept to insert, lookup, update, and delete student records. The interface for this registry is through an HTTP API. In order to cover the most often used functionality in writing similar services, I’ve added the following requirements to the use case. Student record lookup returns a JSON student representation with an "HTTP 200 OK" or an "HTTP 404 Not Found" if the record is not there. Student record insertion returns an "HTTP 200 OK" or if the record is already there, should return an "HTTP 400 Bad Request". Student record update returns "HTTP 200 OK" on success, or else, if the record is not there already, should return an "HTTP 400 Bad Request". Student record deletion will remove the record if it exists, and will always return "HTTP 200 OK". The student insertion and update schema must be validated, and the request should fail for invalid input. The student “major” field is an enumeration with three values: “CS”, “Physics”, and “Chemistry”, and it has the default value “CS”. Let’s first start off with the Spring Boot implementation. Spring Boot Implementation We will be using Gradle to set up and build our Java project. The following build.gradle file contains the basic configuration to set up a typical Spring Boot service application. { { mavenCentral() } { ( ) } } apply plugin: apply plugin: apply plugin: bootJar { baseName = version = } { mavenCentral() } = = { ( ) testCompile( ) } buildscript repositories dependencies classpath "org.springframework.boot:spring-boot-gradle-plugin:2.2.1.RELEASE" 'java' 'org.springframework.boot' 'io.spring.dependency-management' 'student-registry-service' '0.1.0' repositories sourceCompatibility 1.8 targetCompatibility 1.8 dependencies compile "org.springframework.boot:spring-boot-starter-web" 'org.springframework.boot:spring-boot-starter-test' Create a directory named “student_registry” and drop in the above file. Also, create the directory structure “ ” inside this directory. In Unix-like systems, you can use the following command. src/main/java/org/demo mkdir -p src/main/java/org/demo Inside the “org.demo” Java package, we will be having three classes. These are introduced below. Application The following " " file is the entry point to the Java application, which contains the main method. This will bootstrap the Spring application by locating the service implementations in the classpath and initialize the required components. Application.java org.demo; org.springframework.boot.SpringApplication; org.springframework.boot.autoconfigure.SpringBootApplication; { { SpringApplication.run(Application.class, args); } } package import import @SpringBootApplication public class Application public static void main (String[] args) Student The following " " file represents the model class to represent the resource information used in the service. Student.java org.demo; { String id; String name; Major major = Major.CS; { id; } { name; } { major; } { .id = id; } { .name = name; } { .major = major; } } Major { CS, Physics, Chemistry } package public class Student private private private String public getId () return String public getName () return Major public getMajor () return public void setId (String id) this public void setName (String name) this public void setMajor (Major major) this enum StudentRegistryController The following " " file contains the class representing the resource controller which is used as the target for where the messages are dispatched to. It will contain annotations that map request patterns to methods. StudentRegistryController.java package org.demo; java.util.HashMap; java.util.Map; org.springframework.http.HttpStatus; org.springframework.web.bind.annotation.DeleteMapping; org.springframework.web.bind.annotation.GetMapping; org.springframework.web.bind.annotation.PathVariable; org.springframework.web.bind.annotation.PostMapping; org.springframework.web.bind.annotation.PutMapping; org.springframework.web.bind.annotation.RequestBody; org.springframework.web.bind.annotation.RestController; org.springframework.web.server.ResponseStatusException; @RestController public { private < , Student> registry = HashMap<>(); @GetMapping( ) public Student lookupStudent(@PathVariable( ) id) { Student student = .registry.get(id); (student == ) { ResponseStatusException(HttpStatus.NOT_FOUND, ); } student; } @PostMapping( ) public addStudent(@RequestBody Student student) { ( .registry.containsKey(student.getId())) { ResponseStatusException(HttpStatus.BAD_REQUEST, ); } .registry.put(student.getId(), student); } @PutMapping( ) public updateStudent(@RequestBody Student student) { (! .registry.containsKey(student.getId())) { ResponseStatusException(HttpStatus.BAD_REQUEST, ); } .registry.put(student.getId(), student); } @DeleteMapping( ) public deleteStudent(@PathVariable( ) id) { .registry.remove(id); } } import import import import import import import import import import import class StudentRegistryController Map String new "/registry/{id}" "id" String this if null throw new "Student with the given id does not exist" return "/registry/" void if this throw new "Student with the given id already exists" this "/registry/" void if this throw new "Student with the given id does not exist" this "/registry/{id}" void "id" String this Running the Service With the above source files available in our Java package, we can build the project by executing “gradle build” in the base directory. The jar file that is created contains all the dependencies and the service implementations added. We can now simply run the generated executable jar file with the following command. java -jar build/libs/student-registry-service-0.1.0.jar The execution starts up its own embedded web server and starts serving requests to the service. A sample session with the service is shown below. $ curl -H -X POST -d http://localhost:8080/registry/ $ curl http://localhost:8080/registry/W01 { : , : , : } $ curl -H -X PUT -d http://localhost:8080/registry/ $ curl http://localhost:8080/registry/W01 { : , : , : } $ curl -X DELETE http://localhost:8080/registry/W01 $ curl http://localhost:8080/registry/W01 { : , :404, : , : , : } $ curl -H -X POST -d http://localhost:8080/registry/ $ curl http://localhost:8080/registry/W02 { : , : , : } "Content-Type: application/json" '{"id":"W01", "name":"Jack", "major":"CS"}' "id" "W01" "name" "Jack" "major" "CS" "Content-Type: application/json" '{"id":"W01", "name":"Jack", "major":"Physics"}' "id" "W01" "name" "Jack" "major" "Physics" "timestamp" "2019-11-27T05:27:40.185+0000" "status" "error" "Not Found" "message" "Student with the given id does not exist" "path" "/registry/1" "Content-Type: application/json" '{"id":"W02", "name":"Saman"}' "id" "W02" "name" "Saman" "major" "CS" In order to view the return HTTP status code, the curl command can be given the “-v” switch to enable verbose mode. In the next section, let’s see how we can implement the same with a Ballerina service. Ballerina Implementation Ballerina has two entry points to its applications. It can either be a main function or a service. For our use-case, a service is directly applicable. Let’s look at the full Ballerina implementation shown in the following "student_registry.bal" file and how to build and run the service. ballerina/http; Major { CS, Physics, Chemistry } type Student record {| string id; string name; Major major = CS; |}; map<Student & readonly> students = {}; service /registry on http:Listener( ) { resource function get [string id]() returns Student|http:NotFound { Student? student; lock { student = students[id]; } { {body: }; } { student; } } resource function post .( :Payload Student student) returns http:Ok|http:BadRequest { lock { students.hasKey(student.id) { <http:BadRequest> {body: }; } students[student.id] = student.cloneReadOnly(); } <http:Ok> {}; } resource function put .( :Payload Student student) returns http:BadRequest|http:Ok { lock { !students.hasKey(student.id) { <http:BadRequest> {body: }; } students[student.id] = student.cloneReadOnly(); } <http:Ok> {}; } resource function delete [string id]() returns http:Ok { lock { _ = students.removeIfHasKey(id); } {}; } } import enum new 8080 student if is () return "Student with the given id does not exist" else return @http if return "Student with the given id already exists" return @http if return "Student with the given id does not exist" return return The above code can be built and run using the following commands. bal build student_registry.bal bal run student_registry.jar The Ballerina service implementation is similar in functionality to the earlier defined Spring Boot application. Thus, we can use the same HTTP requests in testing the service. Now let’s analyze the code to see how each aspect of the Spring Boot service is fulfilled by the Ballerina service. Resource Model Representation The resource model implementation in the Spring Boot application, done using the “Student” Java bean class, is done using the “Student” record type in Ballerina. Since this is a structure that simply needs data and no behavior, the record type is the exact candidate for this. Also, an enumeration “Major” is defined using constants to represent the possible major values for the student. One of these values is also given as a default value in the “major” field in the “Student” record type. The default values, along with other record type level concepts such as optional fields and values, are directly handled by the data binding operations of Ballerina services. Application and REST Controller The Spring Boot application and the REST controller is represented by the Ballerina service construct. A Ballerina service represents a network service, which is bound to a specific type of network listener. Here, we have used an HTTP listener, but this can also be other types of network listeners, such as gRPC, WebSocket and so on. Individual resource functions represent the service mappings of incoming individual requests. These are similar to the Spring Boot REST Controller methods. Service and resource level annotations are used to provide additional metadata on the services, such as base paths, resource paths, and supported HTTP methods. Also, in Ballerina, we have access to the incoming request caller, in order to send explicit responses back to the client — which is done using caller->respond(). This has the added advantage of the user having full control of the return communication back to the client. For example, if there is an error in the response communication, this is directly seen and accessible in the Ballerina code. Thus we can provide additional logic to handle this scenario. In the Spring Boot code, we return a Java object or we throw an exception to signal the return communication to the client. And thereafter, we do not directly have access to the return communication back to the client. Graphical View In Ballerina, an additional aspect that is covered is the auto-generation of the sequence diagrams for the code. This is possible since the Ballerina language itself is designed ground-up to be based on a sequence diagramming concept. Here, all our clients, listeners, and remote endpoints are visualized as actors in the diagram, and the messages passing between them are shown using remote function calls denoted using the “->” operator. Summary The above explanation shows a scenario on how to convert a typical Spring Boot HTTP service application to a Ballerina service. Here are a few resources that were used in the article and will be useful to you to learn Ballerina further: Full source code for the Spring Boot application The corresponding Ballerina code More resources on learning Ballerina Ballerina by Examples for grasping language features quickly