Converting a Spring Boot Service To Ballerina
There seem to be increasing interest for people in knowing how to migrate a Spring Boot application to Ballerina. Spring Boot has been a very popular platform for writing HTTP/RESTful services; and then came along Ballerina, which is introduced as a programming language that is specialized in writing network-aware applications, thus promising much more productive user experience for the developer. With these new additions, many are curious to know how to possibly rewrite their Spring Boot applications in Ballerina to get the long term benefits.
In this post, I will dissect a Spring Boot application, and show how their concepts are mapped to Ballerina programs. The sample use case I’ll be implementing is based on a student registry scenario. 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 HTTP 200 OK or an HTTP 404 Not Found if the record is not there.
- Student record insertion returns a HTTP 200 OK or else 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 already there, should return an HTTP 400 Bad Request.
- Student record delete 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 an 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 setup and build our Java project. The following build.gradle file contains the basic configuration to setup a typical Spring Boot service application.
Create a directory named “student_registry”, and drop in the above file. Inside this directory, also create the directory structure “src/main/java/org/demo”. In Unix-like systems you can use the following command.
mkdir -p src/main/java/org/demo
Inside the “org.demo” Java package, we will be having three classes, these are introduced below.
Application
This 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.
Student
This is a model class to represent the resource information used in the service.
StudentRegistryController
This class represents the resource controller which is used as the target for where the messages are dispatched to. It will contain annotations which maps request patterns to methods.
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 start serving requests to the service.
A sample session with the service is shown below.
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, and how to build and run the service.
The above code can be built and run using the following commands.
$ ballerina build student_registry.bal
$ ballerina 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 only data and no behavior, the record type is the exact candidate for this. Also, a new type “Major” is defined using constant values to represent the possible major values for the student. One of these values are also given as a default value the 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 a 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 represents 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, remote endpoints are visualized as actors in the diagram, and its message passing between them are shown using remote function calls, denoted using the “->” operator.
Summary
The above shows a scenario on how to convert a typical Spring Boot HTTP service application to a Ballerina service. The full source code for the Spring Boot application can be found here [1], and the corresponding Ballerina code can be found at [2].
[1] https://github.com/lafernando/samples/tree/master/springboot/student_registry
[2] https://github.com/lafernando/samples/tree/master/ballerina/student_registry