Developing microservices with MicroProfile

Eclipse MicroProfile defines a programming model for developing microservice applications in an Enterprise Java™ environment. It is an open source project under The Eclipse Foundation, to bring microservices to the Enterprise Java community. MicroProfile is supported by Liberty.

MicroProfile defines a number of specifications for building microservices that are resilient, secure and easy to monitor.
Table 1. Included in Eclipse MicroProfile 1.2
Specification Description
JSR 346: Contexts and Dependency Injection for Java EE 1.1 CDI defines a set of services that manage the injection and lifecycle of objects in an Enterprise Java runtime.
JSR 339: JAX-RS 2.0: The Java API for RESTful Web Services JAX-RS is a Java API for RESTful Web Services.
JSR 353: Java API for JSON Processing JSON-P is a Java API for processing JSON.
Eclipse MicroProfile Config 1.1 Config is a Java API and SPI for managing application configuration.
Eclipse MicroProfile Fault Tolerance 1.0 Fault Tolerance provides strategies for coping with failures when calling external services.
Eclipse MicroProfile Health Check 1.0 Health Check allows components to report their liveliness to the wider system.
Eclipse MicroProfile Health Metrics 1.0 Health Metrics provide a unified way for applications to expose monitoring data.
Eclipse MicroProfile JWT Propagation 1.0 JWT Propagation allows JSON Web Token (JWT) to be used for authentication and authorization with Java EE role-based access control (RBAC).

Known Restrictions

  • CDI is used extensively in the MicroProfile APIs, however Liberty does not support CDI in OSGi web applications that are packaged in enterprise bundle archives (EBAs). Instead, package applications that use MicroProfile in web application archives (WARs) or enterprise application archives (EARs).
  • MicroProfile Fault Tolerance 1.0 is designed to manage calls made to other services. It is not designed to manage updates to resources in a transactional context. CICS® resources should not be updated in methods annotated @Bulkhead, @CircuitBreaker, @Fallback, @Timeout or @Retry. CICS cannot guarantee that these updates will be recovered when exceptions occur, even when JTA is used.
  • When the feature mpJwt-1.0 is enabled in the server.xml of a Liberty JVM server, all authentication must be done by using JWT bearer tokens. To use any other form of authentication, a separate Liberty JVM server must be used.

Service Architectures in CICS Liberty JVM servers

Monolithic Architecture

Monolithic architecture implements the application in a single unit. Internally the logic can be modular, but externally the application is either entirely available, or not available at all. Monoliths perform well compared to microservices and are less complex when managing security and transaction context. Scaling monoliths involves adding instances of the entire application, individual parts cannot be scaled.
Figure 1. Monolithic architecture
Monolithic architecture implements the application in a single unit. Internally the logic can be modular, but externally the application is either entirely available, or not available at all.

Backing Services

Backing services allow the back end data and programs to be decoupled from the main application. By making the data and programs into separate applications, they are called using a platform agnostic communication method, for example HTTP, socket, message queues, etc. Instead of the main application holding all the logic for communicating with these sources, some responsibility is given to these services.

In CICS, z/OS Connect is used to expose CICS programs as backing services through a REST API. In the example, the application uses JDBC to communicate with a database. SMTP is used to send emails and HTTP is used to call a CICS program through z/OS Connect.

Figure 2. Backing services
Backing services allow separation between an application and the applications resources. Rather than contain all the application logic in a single unit, services can be exposed to provide easy access to internal and external services

Hosted Services

Services are hosted in CICS to further decouple the main application from the various components. Similar to backing services, additional functionality is exposed in CICS through CICS Web Services, or in CICS Liberty with applications using technologies including servlets, JAX-RS and JAX-WS.

JAX-RS is a popular technology for creating RESTful web services, JAX-WS is used to create remote procedure call (RPC) orientated web services.
Figure 3. Hosted services
Hosted services have multiple parts of an application in a central core, which means they are controlled by their own callable services.
Note: Both REST and RPC are equally valid options for communication in microservices. REST focuses on resource management. RPC focuses on actions. A microservice architecture does not mandate REST, RPC or any other technology.

Microservices

A full microservice architecture is an interconnected web of isolated services with no single central point, though there can be dedicated entry points. Services can communicate with one another as required. Scaling microservices involves adding instances of the parts which require scaling. Microservices are more resilient to failures than monoliths.
Figure 4. Microservices
A full microservice architecture is an interconnected web of isolated services with no single central point, though there can be dedicated entry points.

Scaling Services in CICS

Services can be scaled in several ways in CICS, depending on region topology and setup. Microservices are typically isolated into a single container. In CICS, a service or set of services could be isolated within a region or JVM server. Scaling can be achieved by running multiple CICS regions hosting the same service or set of services. You can also scale the JVM server by increasing the number of threads.

Securing Microservices

Where possible, microservices should be kept off public networks. API gateways can be used to provide controlled access to microservices. MicroProfile offers a method for using Open ID Connect (OIDC) based JSON Web Tokens (JWT) for role based access control (RBAC) of microservice endpoints. Security tokens offer lightweight and interoperable propagation of user identities across different services.

MicroProfile JWT Authentication 1.0 provides functionality to authenticate and authorize users based on a JWT bearer token. The token can be injected into the service code and used to propagate the identity across the microservice network. Propagation of the JWT can be done manually by including the JWT as a bearer token in the Authorization HTTP header on the outbound request. Alternatively, Liberty can automatically propagate the JWT by configuring a webTarget element in server.xml with an authnToken configured, for example:
<webTarget uri="http://microservice.example.ibm.com/protected/*" authnToken="mpjwt" />
Important: JWT identities are not automatically mapped to a user registry and will not be propagated into the CICS task user ID. To enable identity mapping, add mapToUserRegistry=”true” configuration attribute to the <mpJwt> element in server.xml.
For more information on configuring MicroProfile JWT Authentication in Liberty see Configuring the MicroProfile JSON Web Token.

Data Consistency in Microservices

Microservices cannot easily make use of distributed transactions. Instead alternative transaction strategies are used, such as the saga pattern, where events are published after an updated in a service. For example, if service A and service B have updates which should both happen, the following sequence occurs:
  1. A updates into a pending state
  2. A sends a message to B
  3. B updates into a complete state
  4. B sends a message to A
  5. A updates into a complete state

When to use Microservices

Microservices are best applied where an application can be deconstructed into smaller, isolated, services. A microservice allows for controlled scaling, independent deployment and more autonomous development. The architecture of microservices can create additional complexity, particularly in deployment and data consistency. Communicating over protocols such as HTTP produces a larger performance cost compared to calling in memory. Components can be made more resilient to failure by allowing them to scale individually. Monitoring solutions become more important to aid diagnosis of unhealthy services when managing a microservice architecture.