This is part three of a three-part blog series on designing microservices. You can start with Part 1 here.
Microservices often have organic origins, emerging from the bubbling cauldron of existing monolithic applications to fill an immediate need. Given the desire for improved delivery speed that drive the adoption of microservices, developers often take a "code first, ask questions later" approach and iterate their way to a useful result. This is okay, but is it optimal? Answering that question leads to another question: what design considerations should be made up front before developing a microservice? As software architect Simon Brown likes to say, "Big design up front is dumb, but no design up front is even dumber."
Borrowing from the lean canvas approach designing business models, I would like to introduce the microservice design canvas. This canvas is a tool that should help you capture the most important up front attributes of your service before you build it. The canvas takes an outside in approach that lends itself well to loosely coupling your interface from its underlying implementation.
You can start by filling in a freeform "Description" of your service in the bottom box. From here, you should complete the "Consumer Tasks" box by documenting the tasks your service's consumers need to perform - the "jobs-to-be-done" that are enabled by your service. Enumerating the consumers of the service along with the tasks they need to perform helps to crystallize the purpose of the service and provides the material inputs needed to design the interface. This information should then help you fill in the "Interface" box. Here, consumer tasks can be broken down into specific interactions with the service interface. Classifying interactions according to patterns-queries, commands, events-will help shape the underlying service implementation, and ultimately help drive the design of your API. In addition to the tasks and interactions for the service-what it does-we must also consider the non-functional aspects of the service-what it is. You can use the "Qualities" box to identify service properties such as availability and performance levels, extensibility approaches, and security expectations. This will help further the consumers' understanding of the service and also influence its implementation. Taken together, the consumer tasks, interface, and qualities define the "surface" of the service. This surface is crucial to the design of the system, as it captures the most important information needed by other stakeholders in the system.
Beneath the surface, there are still some key considerations. The "Logic/Rules" and "Data" boxes provide a place for service designers to document key considerations in these areas. Resist the temptation to go too deep at this stage. It is not necessary to write up a full internal system design for the workings of the service, but you may want to note the items that you feel are unique and needed in supporting the surface properties. Finally, service "Dependencies" should be listed in order to call out what tasks the service requires. For task-heavy microservices featuring a fair amount of business logic, it is natural to require interactions with more data-oriented services. However, in the spirit of microservice architecture, the goal is to minimize these dependencies.
Using some of the services identified in my last post on designing a system of microservices, let's look at a couple of example canvases. First up is a canvas for the Customer-Centric Payments Management service:
Note that this service looks like it will be straightforward in its interactions, and relatively self-contained with its data. From a non-functional perspective, it is not mission critical and does not require the highest level of transactional integrity. Therefore, its implementation may be fairly simple.
Next up is a canvas for the Customer-Centric Payments Authorization service:
There are certainly some more complex considerations for this service. First of all, it is expected to be high volume and low latency. That has a definite impact on the engineering of the service, including the need for a complex cache of authorization information. Secondly, since its interactions involve the movement of money, transactional integrity and auditing are paramount. Lastly, there are some event interactions that mean the interfaces will go beyond typical RESTful APIs. In fact, seeing these non-functional differences make it clear why it was a good design decision to split up the authorization and management functions of the Customer-Centric Payments solution into separate services. Iterating between the system view and the service view will be helpful in defining service boundaries, the goal of my last post.
I hope that this microservice design canvas can be a useful tool for those embarking on their microservices journey. It clearly ties into the API design, and from that perspective ties into the great work of my colleagues in the API Academy. Check out Mike Amundsen's API design methodology as a next step, especially the potential of ALPS as a means of defining the semantics of a service. Also, Ronnie Mitra's Rapido tool to see how you can sketch out an API design in a similarly intuitive way. And Erik Wilde's patterns for robust extensibility will help you flush out qualities that will help you define more evolvable microservices.
To hear more about these topics and interact directly with the members of the API Academy, please join our Microservices and APIs Virtual Summit which begins on July 11 and continues through the balance of the year. Register once to gain access to all summit recordings. See you there!