Archive: 2015-11

Defining Microservices

By on

I see microservices as an architectural pattern or style. Some styles are well described in the literature (Roy Fielding's description of REST is an example). Unfortunately, there was no clear description of the microservices style when it became popular. And the growing buzz has contributed to the confusion because different people try to add nice-to-have characteristics to what a microservice should be. For example, based on things I've read, one might conclude that microservices should:

  • Have few lines of code (some say 10-100 LOC).
  • Not be part of circular calls across services.
  • Be developed by a team that can be fed by 1 or 2 pizzas.
  • Avoid synchronous communication (request-response calls).
  • Offer coarse-grained operations (though microservices are "fine-grained services").
  • Be run by the team who built them.
  • Not participate in complex service compositions.
  • Have the freedom to use new languages, frameworks, platforms at will.
  • Manage their own databases.
  • Be monitored by sophisticated logging and monitoring infrastructure.
  • Be developed by teams split around business capabilities motivated by Conway's law.
  • Match DDD bounded contexts.
  • Be dynamically discovered.
  • Be small enough to be rewritten in two weeks.

Thus, requirements for a microservice are all over the place, and often mention good software engineering practices known for decades.

I'd rather stick with the well-known description by Lewis and Fowler: "the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. "

From an architecture perspective, the microservice style belongs primarily to the deployment view. It dictates that the deployment unit should contain only one service or just a few cohesive services. The deployment constraint is the distinguishing factor. As a result, microservices are easier to deploy, become more scalable, and can be developed more independently by different teams using different technologies. Beyond that constraint, other characteristics describe a lean SOA service that does not use orchestration or ESB.

Despite the lack of clarity about what microservices really are, it's exciting to see the many ideas, tools, and discussion sprung from them. I'm not sure we're past the peak of inflated expectations with microservices, but I certainly look forward to seeing what consolidated shape microservices will have when they reach the plateau of productivity. At the moment, for us software developers, more important than having a clear definition is to understand the tradeoffs of using microservices.

Microservices is one of those buzzwords that generate a lot of hype but is ill defined. In another post, I comment about defining microservices. In this post, my main goal is to discuss pros and cons of using microservices in contrast with the monolithic approach, so let's cut to the chase.

What you gain and what you lose

Benefits of using microservices:

  • Deployability: more agility to roll out new versions of a service due to shorter build+test+deploy cycles. Also, flexibility to employ service-specific security, replication, persistence, and monitoring configurations.
  • Reliability: a microservice fault affects that microservice alone and its consumers, whereas in the monolithic model a service fault may bring down the entire monolith.
  • Availability: rolling out a new version of a microservice requires little downtime, whereas rolling out a new version of a service in the monolith requires a typically slower restart of the entire monolith.
  • Scalability: each microservice can be scaled independently using pools, clusters, grids. The deployment characteristics make microservices a great match for the elasticity of the cloud.
  • Modifiability: more flexibility to use new frameworks, libraries, datasources, and other resources. Also, microservices are loosely-coupled, modular components only accessible via their contracts, and hence less prone to turn into a big ball of mud. Dynamic discovery and binding via a registry (e.g., Apache ZooKeeper, Netflix Eureka) is sometimes used for location transparency.
  • Management: the application development effort is divided across teams that are smaller and work more independently.

Challenges of using microservices:

  • Deployability: deployment becomes more complex with many jobs, scripts, transfer areas, and config files for deployment.
  • Performance: services more likely need to communicate over the network, whereas services within the monolith may benefit from local calls. Also, if the microservice uses dynamic discovery, the registry lookup is a performance overhead.
  • Availability: if you use a registry for dynamic discovery, unavailability of the registry may compromise the consumer-service interaction.
  • Modifiability: changes to the contract are more likely to impact consumers deployed elsewhere, whereas in the monolithic model consumers are more likely to be within the monolith and will be rolled out in lockstep with the service. Also, mechanisms to improve autonomy, such as eventual consistency and asynchronous calls, add complexity to microservices.
  • Testability: automated tests are harder to setup and run because they may span different microservices on different runtime environments.
  • Management: the application operation effort increases because there are more runtime components, log files, and point-to-point interactions to oversee.
  • Memory use: several classes and libraries are often replicated in each microservice bundle and the overall memory footprint increases.

What about autonomy?

Much of what I read about microservices mentions autonomy as a benefit. For example, Sam Newman's Building Microservices book says increased autonomy is a huge advantage of microservice architecture. But what is autonomy? And why didn't I list it in the pros of microservices above (or the cons either)?

First off, there's design autonomy: how much freedom you have to design, implement, and modify a software element. But more important to our discussion is runtime autonomy: how much control a component has over everything that is executed when the component is invoked. A service that doesn't need to interact with any other components outside its boundary is highly autonomous. If a service calls another service, accesses a shared database, sends a message or event to a message broker, then it loses a little bit of autonomy. A design goal is not to get rid of these kinds of interactions, but to avoid problematic ones so that the level of service autonomy doesn't compromise performance or reliability.

And what about the autonomy of microservices? They certainly beget high degrees of design autonomy--the team can employ different technologies and patterns to design and implement each microservice, and can change and redeploy each microservice independently. On the other side, services within a monolith often have to follow the same design constraints and change+deploy cycles--less design autonomy.

As for runtime autonomy, I'm afraid the very idea of microservices goes in the other direction. We're splitting the monolith into small distributed services. The overall business logic that was once collocated in the monolith is now spread across microservices. It's more likely that a microservice will interact with other microservices over the network--that interaction decreases autonomy. If the interaction between microservices involves changing data, the need for a transactional boundary further compromises autonomy. To quote Newman, "Don't also be surprised if things like distributed transactions or CAP theorem start giving you headaches."

The good news is that much of the schooling about microservices I've seen emphasizes this apprehension with autonomy. To avoid autonomy issues, the experts advise you to employ techniques such as eventual consistency, event-driven architecture, CQRS, cache (data replication), and aligning microservices with DDD bounded contexts. My point is that these great techniques are not inherent to microservices, but are desirable to prevent service autonomy issues that are inherent to microservices.

Researchers at the Carnegie Mellon University Software Engineering Institute are seeking volunteers to participate in a study. The Quantifying Uncertainty for Early Lifecycle Cost Estimation research project is conducting a public call for participation from individuals who have recent experience executing a Department of Defense (DoD) program. This study explores how to calibrate individual and group expert judgment in the context of decisions about uncertain events within DoD programs. The study also explores the impact of those uncertain events on the cost of executing a program.

The call for participation is open now through November 30, 2015. Participants will gain knowledge of measuring expert judgment and techniques to improve their own judgments and estimations. After the experiment, participants will receive their personal test results in addition to summarized group results in a format that maintains individual confidentiality.

The experiments will be conducted across two half-days of remote participation. For more information about the experiment and to volunteer, visit www.sei.cmu.edu/quelce-experiment-cfp.