chris - Fotolia
Workflow is fundamental to any multicomponent application. Information moves from the source through a set of processes, and a response of some sort returns as the last step. There are a limited number of ways to steer workflow, and the first step in designing a microservice application is to decide which of those workflow options is best. From there, you can pick from a number of API workflow design patterns.
In traditional applications, work is steered by application processes. A microservice is then invoked because the logic of another recognizes the need to run it. The most common model for application steering of work is the API broker design pattern, where a master component (the broker, in this case) receives work from the source process and passes it to a microservice for execution.
An alternative model inherited from the days of SOA is the service bus design pattern. With this design pattern, a series of services are attached to a message bus, and the message bus governs the logic (often written in Business Process Markup Language, or BPML) that decides what service to run next.
The benefit of this approach is that the processing logic services it needs to facilitate execution is separated from workflow logic, now in BPML form. That makes component reuse easier, because microservices are now simply process snippets. It's also a step toward the third model: step orchestration.
Best practices for microservices dictate they be resilient and scalable, which means that they cannot retain burdensome state data from execution to execution. Otherwise, only the last service that worked on an input can execute the next workflow step, which makes it difficult to scale or redeploy.
State control in microservice applications depends on separating data from logic and providing that data to any related microservice. The data element can originate from the workflow source and move along with the workflow (front-end state control), reside in a database accessible to all microservices and run when work arrives (back-end state control), or operate under a finite step orchestration machine.
Step orchestration describes the sequencing of microservices along a workflow, and service buses are the most basic example. Use a switch/router design pattern where the goal is to push work through a predictable sequence of processes, essentially creating a modern implementation of service broker logic. But for more sophisticated orchestration of complex applications, it's essential to bring in the question of state control.
The state machine design pattern is the ultimate step orchestration model. Any process system can identify as a migration following a specific state-guided sequence triggered by events. With this design pattern, a logical state and event mapping table define each process sequence. The relationships between specific states and events determine which microservice process to run. Both the mapping table becomes part of the state control data.
State machine orchestration resolves issues around synchronous and asynchronous request handling. In synchronous processing, a request for a microservice is held until that requested service completes execution. For very simple microservice applications, this is manageable, but it always makes it difficult to handle concurrency and resiliency.
With a state machine design pattern, all microservice processes are asynchronous. The calling microservice simply enters a state that waits for an event signal that signals a stop, and one unified state and event management mechanism handles everything
The biggest advantage of the state machine orchestration approach is that it deals with scalability and concurrency in parallel. Each process receives its data and state control information as an input, and a master process translates the state and event information it needs to activate the proper service. This model is also extremely flexible, so it has use in both persistent container and VM models of deployment and serverless deployment.
A final set of design patterns relate to the most general model of microservice communications: service mesh. Service mesh is normally based on either a sidecar design pattern or a specific design pattern that comes from service mesh middleware, such as Istio and Linkerd. The service mesh approach is a good way to handle service bus deployment models, because all the necessary microservice communications and load balancing might reside right in the implementation details.
API selection is based partly on best practices and partly on the specific middleware tools you select to help with your implementation. Generally, it's good practice to use RESTful APIs and think in terms of APIs that represent services rather than actions. JSON and XML are the normal mechanisms for coding data values.
The big API question is whether to pass all the data to a process or just the data the process needs. The former will make maintenance of microservices easier by giving everything access to the workflow data and state information, but it will also increase network burden and delay in moving work around. The "only what is needed" approach should really only apply when the nature of the microservice inherently limits the scope of needed information.
When you design microservices and APIs, try to take a consistent approach across any applications that might potentially reuse those microservices down the line. Many advanced microservice developers hone in on the state machine approach because of its flexibility and because it creates a data model that fully describes the process state and variables in a single place. This makes debugging much easier. But whatever approach you take, remember to be consistent or you'll run into debugging and operations problems later.