At Veriscape, we're developing a new service called Blitz, which will connect back end requisition approval services to Microsoft Teams. This article looks at how we utilised the concept of externalized configuration in development, the benefits it provided as well as a brief look at how to do it.
If you're more on the business side of things, consider this a peak behind the curtains, showing how we strive for quality software.
The basic concept of externalised configuration means that configuration should exist outside the program in some way. The opposite implies configuration is either hard coded into the program or that the configuration file is compiled into the binary. For example, if a program needs to return a certain number of search results, that value could be hard coded into the program. Updating this value would require a recompile, testing, and deployment. Using the external configuration, that value would reside in a file, a ConfigMap, or a database. Now updating the value is trivial and can be done so as minimize service interruption.
The driving idea behind Blitz is to speed up requisition approvals by making the approval process more accessible. The Blitz service is composed of three parts: The integration, the Kubernetes cluster, and Microsoft Teams. As of the time of this writing, there is an integration with IntelleFlow, which Veriscape's requisition approval service. Veriscape is looking at integrating with our technology partners.
The Kubernetes cluster that Blitz runs on is hosted on AWS EKS and is composed of three services: an Integration Gateway, a Message Processor, and a Teams Connector. The Message Processor is the service that receives requisition data and applies the adaptive card
The interesting configuration in Blitz is the Adaptive Card templates ("templates" from here on). The templates determine how to display requisition data in Microsoft Teams as an Adaptive Card. They're stored as a ConfigMap in Kubernetes, generated from a YAML file using kustomize. In the simplified example below, the Microsoft Teams notification is produced by applying the requisition data to the adaptive card template. Note how the total Amount value is propagated from the requisition date, to the template, and how it shows in the notification.
The Microsoft Teams notifications below were generated by Blitz using real world requisition data and templates.
The message processor service "watches" for changes on the ConfigMap containing the templates. When a change occurs to ConfigMap, the new templates are loaded and parsed. Now the next requisition processed can use the updated templates.
This seems very simple, but this enabled four things that were critical to speeding up development. The first thing is that development on the integration, message processor and Adaptive Cards can happen independently. Development was off to the races once the basic template structure was established.
The next advantage is that the Blitz deployment can update templates
with zero down time. It's not necessary to restart services nor rebuild with updated application resources.
The next benefit is we are nicely situated to adapt the templates based on customer feedback. While we put it good effort to ensure a user friendly Adaptive Card,
it's impossible for us to surmise if customers need different data displayed. The flexibility of external configuration enabled us not to have to chase perfection, nor try to display everything. Customer feedback is greatly preferred to our own notions.
Finally, the externalised configuration enabled local development of the message processor service. Blitz has two implementations for externalised configuration. The first being Kubernetes ConfigMaps, already been described. The second implementation enables the service to watch for changes on a file. In all other ways the message processor operates exactly the same.
The extern-config package is pretty simple. It consists of three abstractions. The ResourceWatcher interface defines where the configuration comes from. The ConfigParser interface defines how to parse the configuration. YAML and JSON implementations are provided. Finally, the UpdatableConfig interface defines what uses the configuration. The package also assumes the configuration can be deserialised as a plain old Java object(POJO), which is represented as a generic type argument <T>.
Implementations of ResourceWatcher should compose ConfigParser and UpdateableConfig implementations. This design should work for any database, file, or service which can execute a wait, watch, or trigger type of command. Examples include DynamoDB, Etcd, plain files, and most SQL databases.
A few limitations are worth noting. This package is designed for cases where implementations of UpdatableConfig have the same object lifetime as the executing program. Also, this library requires that the ConfigMap exist before starting the service.
The extern-config package is available on GitHub.
This article described how Veriscape utilised one of the software development industry's best practices called Externalised Configuration, it's benefits to development, and how to do it. It's a direct example of how software coding choices propagate through development, deployment, maintenance and user experience.
The Blitz is just one example of a Veriscape's tech innovation that's designed to optimize supply chain operations.
Do you like principled software development? So do we. If you'd like to work at Veriscape, email info@veriscape.com.