Microservices require new architectural considerations and the use of different development abstractions, said...
experts at the JavaOne 2014 Conference in San Francisco. From a service-oriented architecture perspective, it's a good practice to build microservices around business capabilities said Alexander Heusingfeld, senior consultant at innoQ, a software architecture engineering firm in Germany. Start with a business use case and then the microservice should tackle it in the same domain.
Heusingfeld said there are a number of challenges developers come across when thinking about microservices. The first is service registration and discovery. There needs to be a mechanism to discover and reuse services at runtime. The system should self-register and make information available.
Another challenge is resilience. It is important to compartmentalize systems to keep an application running when components are not in one place. Distributed configuration capabilities ensure the same configuration can be leveraged for different instances of a given microservice. Good metrics help to determine the health of different instances of microservices that can be published and made available to a centralized metrics collector for the operations team.
The business angle
Microservices should not share processes, said Martin Eigenbrodt, a senior consultant at innoQ. They can be started and stopped without affecting other microservices. This approach emphasizes safer containers for microservices that use separate runtime processes. They are loosely coupled—best done by coupling data formats.
The approach allows microservices to be developed independently so that they can evolve at different rates. For example, a product catalog microservice might have a more rapid update frequency than a user management one. The goal of this is to advance application lifecycles based on data exchanges rather than on other services.
Eigenbrodt said sometimes people look at using an enterprise service bus for microservices. However, he said a better approach is making the endpoints smart and the pipes dumb. A message queue may be used, but little routing logic is deployed within the queue.
Heusingfeld said that implementing microservices with Java is easier with a framework. But there is still a lot manual coding involved with existing frameworks. It's important to look at the business case to see what framework is best suited for connecting microservices together.
- Drop Wizard includes support for distributed system primitives, helps decouple service dependencies and provides metrics. But it's missing service discovery capabilities.
- Spring Boot is focused on the Spring ecosystem and provides better startup times for systems built with the Spring Framework. It includes support for externalized configuration management and support for metrics, but developers will need to manually code logic for building resilient distributed systems.
- Play 2 includes components for implementing several microservice patterns like circuit breakers and provides a good packaging mechanism, but it lacks support for metrics, service discovery and distributed configurations.
New software patterns required
Asynchronous programming techniques provide better performance for distributed systems that use microservices since they eliminate the bottlenecks found in synchronous programming techniques, said Tim Ward, senior consulting engineer at Paremus and author of Enterprise OSGi in Action.
Ward noted that many Java programmers and architects seem to believe that asynchronous programming is a recent development. But the concepts were first introduced in the 1970s. The main idea behind asynchronous programming techniques is to create applications that scale better so the main application threads don't spend long periods of time waiting for high-latency calls. They also provide more reasonable failure characteristics.
Ward said the main problem is that human brains work asynchronously internally, but have challenges with coping with asynchronous logic. This is why procedural programming techniques are easier to follow. He believes that asynchronous programming is only useful when it's possible to get rid of the extra cognitive overhead.
Asynchronous programming requires an extra set of primitives that help address the following challenges:
- A "promise" is one of the basic primitives for programming the asynchronous microservices. A promise represents a delayed value that will be resolved in the future and either set into a specific value or a failure value.
- Listener "callbacks" are used to fetch the data contained in promises.
- A "deferred primitive" is held by the provider of a promise and can be used to set the promise value or failure.
It is better to use promises with callbacks so that application is never blocked, Ward said. Callback should be fast. It's possible to chain promises together to compose sequences of events. Ward said this allow developers to create complex behaviors without branching, creating a functional flow for distributed programs. OSGi uses the idea of microservices differently than Web-based microservices. In OSGI, microservices tend to be smaller and lighter and follow simple patterns. Web-based microservices tend to be bigger and less dynamic than OSGi services.
Before embarking on a microservice initiative, it's important to define the term, since "microservices" tends to be used in many contexts and can mean different things, Heusingfeld said. He believes the most useful abstraction for service oriented architects lies in thinking about building more efficient services. Developers may find microservice concepts useful when thinking about more efficient techniques for implementing Web services or OSGi. Asynchronous programming techniques could prove useful across all of these different use cases, Ward argued.