Taking the opportunity to define Microservices, not in terms of what they are not, but in terms of what they are.
This is my definition.
A Microservice is a discrete, isolated, and named piece of logic that consumes
0…N inputs, produces
0…N outputs, and which is executed for the benefit of an invoker—it is performed as a service.
The reason for calling it a Microservice instead of a Service is not because it has N lines of code where N is a small number, or that the size of the deployable is max N kilobytes or another arbitrary metric. The reason is that it has a small number of responsibilities: one. It does one thing, and does it well.
Isolation means that it operates separately from other things, which makes it an inherently distributed entity—it does no longer matter on which physical hardware it is located or in which OS process it runs. It also fails in isolation—cascading failures can as such be avoided. Furthermore, this also allows it to evolve and be upgraded separately.
Since Microservices are isolated we need a way to locate and refer to them. The name in combination with any types of the inputs and outputs of the Microservices can be said to form the signature or identifier of the Microservice.
Inputs & Outputs
Given a language that supports type information, the inputs and outputs can be typed, which means that those types can be reflected in the handle. This means that the invoker can know what a Microservice consumes and produces.
Since it is not the invoker that executes the logic of the Microservice and Microservices are naturally distributed, invocations need to avoid blocking the invoker while the invocation is being performed—the invoker must not be held hostage by the Microservice until it has produced its outputs. This means that an abstraction that lets us receive the outputs of the Microservice eventually is needed. In other words, invocations are asynchronous.