Blockchain technologies and smart contracts are becoming mainstream research fields in computer science and researchers are continuously investigating new frontiers for new applications. Likewise, microservices are getting more and more popular in the latest years thanks to their properties, that allow teams to slice existing information systems into small and independent services that can be developed independently by different teams.
A symmetric paradigm applies to Smart Contracts as well, which represent well defined, usually isolated, executable programs, typically implementing simple and autonomous tasks with a well-defined purpose, which can be assumed as services provided by the Contract. In this work we analyze a concrete case study where the microservices architecture environment is replicated and implemented through an equivalent set of Smart Contracts, showing for the first time the feasibility of implementing a microservices-based system with smart contracts and how the two innovative paradigms match together.
The Proposed Architecture
Before describing the details of the case study we implemented, we compare the microservice and the smart-contract architecture.
In order to map a general microservice architecture into an architecture where services are provided by smart contracts, we consider as paradigmatic example that of an information system where doctors can keep track of their patients and of the diagnosis they made.
Microservices-based systems are usually built with three layers. The graphical user interface, the API-Gateway and the microservices (see Figure 1).
Any device can be connected to the system through the API-Gateway. The role of the API-Gateway is to provide a custom interface for each type of device, usually exposing a set of REST APIs. As example, the API-Gateway can implement a set of interfaces for a web application, other interfaces for a mobile application and other interfaces to expose data publicly. The API-Gateway then calls the different microservices and returns the result to the caller. The direct communication between the graphical user interface and the single microservice is considered an anti-pattern, even if technically possible[3][4]. The API-Gatweay can also be responsible of the load balancing. Microservices usually adopt lightweight message queues to communicate, adopting the publisher-subscriber pattern. A microservice commonly publishes the message into a channel of the message bus. Other microservices can subscribe to the same channel and read the messages received.
Similarly to the common microservice architecture, the corresponding smart contracts architecture is built on two layers as depicted in Figure 2. The first layer is the interface between external applications and the blockchain. It provides the Application Binary Interface (ABI) to interact with the smart contracts and the User Interface in which ABI are embedded. Specifically, an external App can use ABI exposed by each smart contract to compose and perform service requests. The ABIs allow to specify the functions to call and manage the correct format of data exchanged with the smart contracts.
The second layer is composed by the set of smart contracts deployed into the blockchain. In this architecture our solutions is the simplest: each
Micoservice is implemented by a single “atomic” smart contract, which allows to simply convert the software code usually embedded in a Java class providing the microservice into the corresponding Solidity code for each smart contract.
Other solutions can be devised, but we consider this the easiest for mapping one architecture to the other for our purposes.
Layer communications is governed by remote procedure calls (RPC) by means of the Web3.js library provided by Ethereum 4, which is designed for implementing Javascript code for executing blockchain transactions, calls to contracts, queries and for generally manage the interaction among external Apps and blockchain.
In this layer, the blockchain features, specific for each blockchain and each contract programming language, can then be used for implementing specific services. In our model, each user is identified by its Ethereum address. An account service is provided (for example through a mapping
construct) by a smart contract which registers and manages the accounts. For example, one can define services for an online shop (which is a typical example for microservice architectures) and Customers profiles can be defined so that different permits or privileges can correspond to different services provided by the layer.
Data is automatically and permanently stored on the blockchain, so that local resources are not needed for this purpose since each blockchain node (which can be implemented in a private blockchain) holds its own copy. Login and registration procedures are implemented each by an atomic microservice in separated smart contracts. For the online shop example, an inventory service can record information on products on the blockchain and can expose the information on the Storefront web page or directly to the Apps.
An ad-hoc smart contract can manage the delivery service recording all the needed data.
The Microservices-Based System
To validate the feasibility of our approach, and for reason of simplicity, the system of this case study is composed by only three microservices. The same implementation approach could be extended to more complex systems by adding more microservices.
In this case study, we adopted a simple microservice-based application composed by three microservices. The system is an extension of the tutorial presented by Edureka[2]
The system is composed by three microservices written in Java: Doctor, Patient and Diagnosis. The goal of the system is to allow doctors to keep track of diagnosis for the diseases of their patients. The Edureka tutorial shows
how to implement the system by microservices and how to call them to produce an output by composing the
formatted outputs (in JSON format)
obtained by calling other microservices.
The microservices are accessed from the web user interfaces through an API-Gateway that routes the requests and forward the messages. The system is depicted in Figure1 while the original source code is available online[2].
Beside its simplicity, the system implemented includes several characteristics of real and bigger systems. It exposes APIs to connect to the graphical user interface, it enables microservices to communicate between each other, and stores the data in independent non-sql databases.
Blockchain Implementation of the Case Study
In this section, we describe how we implemented the microservices-based system described in Section IV with smart contracts based on Ethereum blockchain.
We started from the premise that Ethereum allows the creation of decentralized applications operating in devices connected to the peer-to-peer network. That application works in the Ethereum Virtual Machine and is named smart contracts. To create a running smart contract, developers have to write its source code in Solidity language, to compile it with the most updated version of the Solidity compiler, and to deploy the smart contract in the blockchain. Once deployed, the smart contract becomes a unique and independent resource, characterized by its address, its logic, and its ABI. The smart contract address represents the resource URL from the blockchain’s point of view. For those reasons, and to replicate the behavior of the case study, we implemented a total of three atomic smart contracts, one for each microservice we wanted to implement. Our goal is to create a remotely callable smart contract Doctor which generates requested data by calling other two remote contracts % of which it knowing only their address and their interface.
To easily manage the dependencies between elements declarations we wrote our three smart contracts in a single source code compliant with the version 0.5.0 of Solidity. We decided to use one of the experimental additional features of the solidity compiler. In particular, our system uses the “AbiEncoderV2” to enable functions to returns composite data type like “structs”.
First of all, we reproduced the microservice Patient and Diagnosis services written in Java source code that aims to collect data and to provide simply getter and setter functionalities.
To reproduce the concept of data encapsulation, we implemented these two classes as solidity libraries. In facts, if library functions are called, their code is executed in the context of the calling contract. In addition, libraries can refer to their own elements using the keyword self. This behavior is similar to that of a class calling a class method in object-oriented languages.
Subsequently, we reproduced the class Doctor. Variables of this class include Diagnosis and Patient that are respectively instances of the classes Diagnosis and Patient. In our smart contracts, class instances are implemented as “struct” variables managed by the libraries functions.
The library Patient includes variables ID, patient_name, patient_email, and related getters and setters. The library Diagnosis includes variables ID, disease_name, disease_ description, and related getters and setters.
After created the libraries, we reproduced the behavior of classes PatientRest and DiagnosisRest by Solidity contracts.
In the original case, the two Java classes manage an array of instances of Patient or Diagnosis. Instead of arrays, we used the mapping data type. This is a key-value data structure that is more efficient than arrays in the Ethereum blockchain in terms of resources and gas consumption.
Rest calls have been replaced by specific “public functions”. For this reason, we call our contract PatientPseudoRest and DiagnosisPseudoRest. In Solidity the keyword public is a visibility modifier that allows the function to be callable both by other contracts and by blockchain users. The public function of the contract PatientPseudoRest representing the microservice is getPatient. It takes as input an ID of a patient and returns a Patient data. The public function of the contract DiagnosisPseudoRest representing the microservice is getDiagnosis, which takes as input an ID of diagnosis and returns the complete set of data matching with the ID.
Finally, we reproduced the behavior of the class DoctorRest. It knows the URI of the microservices Diagnosis and Patient and provides a callable REST function that, given the ID of a patient and the ID of a disease, calls and acquires information generated by the two microservices. Finally, it returns a composition of the data.
We implemented a contract DoctorPseudoRest that calls at run time the contracts. For this reason, DoctorPseudoRest must know the addresses of PatientPseudoRest and DiagnosisPseudoRest and so the contract constructor requires these two addresses.
The DoctorPseudoRest provides the function submitOrder replicating the behavior of the equivalent REST call in the original case study. The calling of remote contracts is performed by means of the definition of a contract pointer that allows the contract to execute its public functions.
The function submitOrder produces a formatted output that includes data related to the specific “patient” and to the specific “disease”.
Conclusion
Results show that it is possible to implement a simple microservices-based system with smart contracts maintaining the same set of functionalities and results. The result could be highly beneficial in contexts such as smart voting, where not only the data integrity is fundamental but also the source code executed must be trustable.
The implementation of both systems and the complete report can be found in [1]
References
[1] “Implementing a microservices system with blockchain smart contracts”, in 2nd International Workshop on Emerging Trends in Software Engineering for Blockchain – Colocated with SANER19, 2019. Download Publication
,[2] S. Kappagantula (2018). ”Microservices Tutorial Learn all about Microservices with Example” Edureka. Online: https://www.edureka.co/blog/microservices-tutorial-with-example
[3] D. Taibi, V. Lenarduzzi, and C. Pahl (2018) Architectural Patterns for Microservices: A Systematic Mapping Study, in 8th International Conference on Cloud Computing and Services Science, CLOSER
[4] D. Taibi, V. Lenarduzzi (2018) On the definition of microservice bad smells. IEEE Software, 35(3), 56-62. doi:10.1109/MS.2018.2141031