Rethinking Service Integrations with Microservices Architecture

The dawn of the microservices architecture (MSA) begun revolutionizing the software paradigm in the past few years by revealing a new architectural style for optimizing the infrastructure usage to its optimal level. MSA defines a complete methodology for developing software applications as a collection of independently deployable, lightweight services in which each service would run on a dedicated process with decentralized control of languages and data. In spite of the wide variety of frameworks introduced for implementing business services in this architectural style nearly none were introduced for implementing service integrations. Very recently WSO2 initiated a new open source programming language and a complete ecosystem for this specific purpose.

A new programming language? Yes, you heard it right, it’s not another integration framework with many different domain-specific languages (DSLs). It’s a purposely built programming language for integration, with native constructs for implementing enterprise integration patterns (EIPs) including support for industry standard protocols, message formats, etc optimized for containerized environments. It may be worth to note that Ballerina is designed ground up with nearly a decade of experience in implementing integration solutions at WSO2 with the vision of making service integrations much more easier to design, implement, deployable, and more importantly adhere to MSA.

The Impact on Microservices Architecture

Figure 1: Using a Monolithic ESB in Outer Architecture

Today most enterprises seek for mechanisms for integrating services from various internal and external service providers for meeting their business needs. Traditionally this could be achieved using an integration framework, ESB or using an integration suite depending on the complexity of the integrations. As illustrated in figure 1, one option would be to use a monolithic ESB in the outer architecture while implementing business services in the inner architecture inline with MSA. Despite the fact that it is technically feasible it may contradict the main design goals of MSA as an ESB or an integration suite would consume a considerable amount of resources while taking longer to bootstrap, having to use in-process multi-tenancy, with comparatively higher development and deployment cost, etc.

For an example WSO2 ESB would need around 2 GB of memory for running a typical integration solution, it would take around 20 to 30 seconds to bootstrap, it may not evenly share resources among all the tenants with in-JVM multi-tenancy, the development process may take longer as it may depend on a single set of configurations and data stores, and finally the deployment would utilize more resources than optimally needed. Considering all of the above plus the vision of adapting serverless architecture, a much lighter, ultra-fast integration framework with a higher throughput would be needed for gaining the best out of MSA.

Figure 2: A Reference Architecture for Implementing Integration Services in MSA

The above figure illustrates a reference architecture for implementing integration services in MSA. Unlike an ESB where a collection of integration workflows are deployed in a single process, in this architecture, each integration work flow will have its own process and a container. Hence services can be independently designed, developed, deployed, scaled and managed. More importantly, it will allow resources to be specifically allocated for each integration service container cluster for optimizing the overall resource usage. Moreover, container cluster managers such as Kubernetes provide completely isolated contexts within a single container host cluster for managing multi-tenancy. Therefore this approach would naturally fit in MSA for implementing integration services.

Ballerina Language Design

As explained earlier Ballerina language has been carefully designed by studying constructs of widely used programming languages such as Java, Golang, C, C++, etc. The following section illustrates the high-level language design in brief:

Packages

The package is the topmost container in Ballerina which holds functions or services. It is important to note that package definition is optional and if a package is defined, ballerina source files need to be stored in a hierarchical folder structure according to its package hierarchy.

Functions

A function represents a set of instructions that performs a specific task that is intended to be reusable. Mainly there are two types of functions; native functions which support returning multiple return parameters, and throwing exceptions.

Main Function

The main function is the entry point in Ballerina executable programs. Executables can be used for implementing integration logic which needs to be run in the background on a time interval or an event trigger.

Services

Ballerina services allow integration workflows to be exposed as services. Services are protocol agnostic and can be extended to work with any messaging protocol with required message formats. Currently, services can be exposed as HTTP REST APIs, WebSockets, HTTP/2 services, and messages can also be delivered to mediation pipelines via JMS topics/queues (using an external broker), and files.

Resources

A resource represents a functional unit of a Ballerina service. A service exposed via a given protocol would use resources for managing different types of messages. For an example HTTP REST API would use resources for implementing API resources, a JMS service would use resources for receiving messages from a topic/queue,

Workers

A worker is a thread according to general programming terms. Workers provide the ability to execute a series of integration functions in parallel for reducing the overall mediation latency of an integration service.

Connectors

Connectors provide language extensions for talking to well known external services from Ballerina such as Twitter, Google, Medium, etcd, Kubernetes, etc. Moreover, it also provides the ability to plug-in authentication and authorization features to the language.

Ballerina Composer

Figure 3: Ballerina Composer Design View

Composer is the visual designer tool of the Ballerina language. It has been designed as a web application and shipped with the Ballerina Tools distribution. Execute the below set of commands to download it and run, once started access http://localhost:9091 in a web browser:

$ version=0.8.1 # change this to the latest version
$ wget http://ballerinalang.org/downloads/ballerina-tools/ballerina-tools-${version}.zip
$ unzip ballerina-tools-${version}.zip
$ cd ballerina-tools-${version} # consider this as [ballerina.home]
# cd bin/
$ ./composer

Not only Composer provides a charming graphical designer, it also provides a text editor with syntax highlighting and code completion features, and a Swagger editor for HTTP based services. Composer provides all language constructs and native functions needed for implementing integration programs and services. More interestingly those can be run and debugged using the same editor.

Figure 4: Ballerina Composer Source View

For detailed information on Composer please refer this article.

Ballerina CLI

Ballerina ships two distributions, one for the Ballerina runtime and the other for the tooling. Ballerina runtime only includes features required for running Ballerina programs and services. The tools distribution include features for executing test cases, generating API documentation, generating swagger definitions and building Docker images:

$ cd [ballerina.home]/bin/
$ ./ballerina --help
Ballerina is a flexible, powerful and beautiful programming language designed for integration.
* Find more information at http://ballerinalang.org
Usage:
ballerina [command] [options]
Available Commands:
run run Ballerina main/service programs
build create Ballerina program archives
docker create docker images for Ballerina program archives
doc generate Ballerina API documentation
swagger Generate connector/service using swagger definition
test test Ballerina program
Flags:
--help, -h for more information
Use "ballerina help [command]" for more information about a command.

Ballerina Packaging Model

Ballerina programs and services can be packaged into archive files for distribution. These files will take the extension BSZ. Consider the below sample HTTP service, the source code of this can be found here:

.
└── hello-ballerina
├── README.md
└── org
└── foo
└── bar
├── helloWorldService.bal
└── helloWorldServiceTest.bal

Following command can be executed for generating an archive file for this service:

$ cd /path/to/hello-service/
$ /path/to/ballerina-home/bin/ballerina build service org/foo/bar/

The generated bar.bsz file would contain following files:

.
├── BAL_INF
│ └── ballerina.conf
├── ballerina
│ └── test
│ └── assert.bal
└── org
└── foo
└── bar
├── helloWorldService.bal
└── helloWorldServiceTest.bal

Ballerina API Documentation Generator

Ballerina tools distribution ships an API documentation generation tool called Docerina as a part of the Ballerina CLI. This allows developers to generate API documentation for Ballerina functions, connectors, structs, and type mappers. Currently, it does not include API documentation generation for Ballerina services as they are already supported with the Swagger integration for HTTP based services. In a future release, it may support non-HTTP services such as JMS and file.

API documentation of Ballerina native functions of v0.8 release can be found here. Execute ballerina doc help command for more information on generating API documentation for Ballerina code:

$ cd ballerina-tools-${version}/bin/
$ ./ballerina doc --help
generate Ballerina API documentation
Usage:
ballerina doc <sourcepath>... [-o outputdir] [-n] [-e excludedpackages] [-v]
sourcepath:
Paths to the directories where Ballerina source files reside or a path to
a Ballerina file which does not belong to a package
Flags:
--output, -o path to the output directory where the API documentation will be written to
--native, -n read the source as native ballerina code
--exclude, -e a comma separated list of package names to be filtered from the documentation
--verbose, -v enable debug level logs

Ballerina Test Framework

Ballerina provides a test framework called Testerina for implementing unit tests for Ballerina code. In v0.8 release, following native test functions are available for starting services, asserting values and setting mock values:

package ballerina.test;
startService(string servicename)
assertTrue(boolean condition)
assertTrue(boolean condition, string message)
assertFalse(boolean condition)
assertFalse(boolean condition, string message)
assertEquals(string actual, string expected)
assertEquals(string actual, string expected, string message)
assertEquals(int actual, int expected)
assertEquals(int actual, int expected, string message)
assertEquals(float actual, float expected)
assertEquals(float actual, float expected, string message)
assertEquals(boolean actual, boolean expected)
assertEquals(boolean actual, boolean expected, string message)
assertEquals(string[] actual, string[] expected)
assertEquals(string[] actual, string[] expected, string message)
assertEquals(float[] actual, float[] expected)
assertEquals(float[] actual, float[] expected, string message)
assertEquals(int[] actual, int[] expected)
assertEquals(int[] actual, int[] expected, string message)
package ballerina.mock;
setValue(string pathExpressionToMockableConnector)

Following is a sample HTTP service written in Ballerina:

package org.foo.bar;
import ballerina.lang.messages as message;
@http:BasePath ("/hello")
service helloService {
@http:GET
resource helloResource(message m) {
message response = {};
message:setStringPayload(response, "Hello world!");
reply response;
}
}

It can be tested by implementing a test case as follows:

package org.foo.bar;
import ballerina.lang.messages as message;
import ballerina.test;
import ballerina.net.http;
function testHelloService () {
message request = {};
message response = {};
string responseString;
string serviceURL = test:startService("helloService");
http:ClientConnector endpoint = create  http:ClientConnector(serviceURL);
response = http:ClientConnector.get(endpoint, "/hello", request);
responseString = message:getStringPayload(response);
test:assertEquals(responseString, "Hello world!");
}

Ballerina Container Support

Ballerina Docker CLI command can be used for creating Docker images for Ballerina program archives. Execute the below command for more information on this:

cd ballerina-tools-${version}/bin/
$./ballerina docker --help
create docker images for Ballerina program archives
Usage:
ballerina docker <package-name> [--tag | -t <image-name>] [--host | -H <docker-hostURL>] --help | -h --yes | -y
Flags:
--tag, -t docker image name. <image-name>:<version>
--yes, -y assume yes for prompts
--host, -H docker Host. http://<ip-address>:<port>

Conclusion

Ballerina is a brand new open source programming language purposely built for implementing integration services in MSA. It provides a complete ecosystem for designing, developing, documenting, testing and deploying integration workflows. Feel free to try it out, give feedback, report issues and most importantly to contribute back. Happy dancing with Ballerina!!

References

[1] Serverless Architectures, https://martinfowler.com/articles/microservices.html

[2] What are Microservices, https://smartbear.com/learn/api-design/what-are-microservices

[3] Introduction to Microservices, https://www.nginx.com/blog/introduction-to-microservices

[4] Microservices: Building Services with the Guts on the Outside, http://blogs.gartner.com/gary-olliffe/2015/01/30/microservices-guts-on-the-outside/

[5] The Future of Integration with Microservices, https://dzone.com/articles/the-future-of-integration-with-microservices

[6] Ballerinalang Website, http://ballerinalang.org

[7] Ballerinalang Documentation, http://ballerinalang.org/docs

[8] Ballerinalang Github Repository, https://github.com/ballerinalang/ballerina