Microservice Orchestrator

The Microservice Orchestrator activity is at the heart of MDW's service orchestration capabilities. It uses a declarative Service Plan to give you a flexible and expressive way to control which services are called invoked and exactly how they are invoked. MDWHub provides a drillable, end-to-end runtime view enabling users to track all services and their dependencies.

Topics

Service Plan

The best way to understand how to create a Service Plan to drive your Microservice Orchestrator activity is to look at a working example. The MDW automated tests include a straightforward example in the Microservice Orchestration process:

Implementation for the Invoke Services activity starts upstream in the Create Plan Kotlin Script activity. Here's the source code that populates the servicePlan variable according to runtime conditions:

variables["servicePlan"] = servicePlan(runtimeContext) {
    services {
        microservice {
            name = "admin/createUser"
            method = "POST"
            url = "${runtimeContext.props["mdw.services.url"]}/services/Users"
            bindings {
              "requestMapper" to "com.centurylink.mdw.tests.microservice/UserRequestMapper.groovy"
              "responseMapper" to "com.centurylink.mdw.microservice/StatusResponseMapper.groovy"
            }
        }
        microservice {
            name = "admin/createGroup"
            method = "POST"
            url = "${runtimeContext.props["mdw.services.url"]}/services/Workgroups"
            enabled = (variables["request"] as Jsonable).getJson().has("group")
            bindings {
              "requestMapper" to "com.centurylink.mdw.tests.microservice/GroupRequestMapper.groovy"
              "responseMapper" to "com.centurylink.mdw.microservice/StatusResponseMapper.groovy"
            }
        }
    }
}

The left side of the assignment on the first line is what sets the variable value. The special servicePlan() constructor uses a super friendly Kotlin DSL to specify which services are executed and with what values. Each microservice represents a designated endpoint that's to be run zero or more times based on its setup. For example, the first microservice declaration says to POST to the createUser service with a property-driven URL from mdw.yaml. Using Kotlin infix notation, it also spells out any input variable bindings to be passed to the invocation subflow. Dynamic values for each microservice can be assigned using variables["myvar"] syntax, or Kotlin String Template expressions. At runtime, all these values are substituted into the servicePlan YAML process variable.

There are many more options available for configuring each microservice than those depicted in this example. The best documentation for these options is the ServicePlanModel.kt source code that contains all of the default values.

data class Microservice(
        private val runtimeContext: ActivityRuntimeContext = MockRuntimeContext("dummy"),
        var name: String = "",
        var url: String = "",
        var method: String = "",
        var subflow: String = "com.centurylink.mdw.microservice/\${StandardInvoke}.proc",
        var enabled: Boolean? = true,
        var count: Int = 1,
        var bindings: MutableMap = mutableMapOf(
          "microservice" to name,
          "serviceUrl" to url,
          "serviceMethod" to method,
          "request" to runtimeContext.docRefs["request"],
          "requestHeaders" to runtimeContext.docRefs["requestHeaders"],
          "serviceSummary" to runtimeContext.docRefs["serviceSummary"],
          "requestMapper" to "com.centurylink.mdw.microservice/IdentityRequestMapper.groovy",
          "responseMapper" to "com.centurylink.mdw.microservice/IdentityResponseMapper.groovy"
    )

For example, notice in this source code that by default enabled is true and count is one. Either of these can be overridden by dynamic or conditional values based on process runtime state.

By far the most important microservice setting is its subflow property. An essential aspect of microservice orchestration is that each invocation spawns an individual subprocess suited to its specific endpoint. Notice that the default value points to a prebuilt .proc asset from the com.centurylink.mdw.microservice package. In fact, the $ prefix tells us that this is a Template Process. The ${StandardInvoke} process provides a sequence that should prove useful in many circumstances. If ${StandardInvoke} does not meet your needs, you should point to your own subflows, which may or may not be template processes.

Any number of bindings may be used to pass values to a subflow. In ${StandardInvoke}, requestMapper and responseMapper variables are needed to transform the main flow's request to the serviceRequest variable used by each subflow adapter. This transformation is accomplished in a Crossmap activity. By default the identity request/response mappers are passed, indicating no translation required.

Template Processes

Template processes are effectively no different from regular processes, except that their runtime instances are labeled according to their microservice name. So in the example above all instances are defined by ${StandardInvoke}, but in MDWHub they appear as admin/createUser or admin/createGroup. Technically that's the only difference between a template process and any other process. However, by convention template processes have dynamic placeholders in many places that ordinarily would be hard-wired. For example, in ${StandardInvoke} the REST adapter activity defines its HTTP method as ${serviceMethod} instead of selecting GET, POST, PUT, PATCH or DELETE from the dropdown. Upstream, serviceMethod is bound to the servicePlan's "method" parameter.

Service Summary

A big part of Microservice Orchestrator's value proposition is centered around the end-to-end Service Summary. This gives you a lot of free goodies as long as your flow abides by the convention of declaring a Jsonable serviceSummary input variable in each subflow along the way.

For a working example, clone and run the mdw-demo project which contains the microservice test flows. Once you have mdw-demo running, submit a request like this one from the microservice test package: https://github.com/CenturyLinkCloud/mdw-demo/blob/master/assets/com/centurylink/mdw/tests/microservice/mainRequest.json Then, on the MDWHub Workflow tab, change the filter to include [Any] status and drill into the latest MicroserviceOrchestration instance. You can look at the serviceSummary variable JSON for all the gory details, but the new Service Summary tab provides friendlier access.

Here you can view the status of all invocations at a glance, or click to drill in to any specific instance.

TODO: describe invocations and updates

Dependencies

(TODO)