Microservices at Twiga — Design, Technology & Infrastructure

Victor Mshindi
TwigaTech
Published in
10 min readDec 16, 2018
Photo of Falcon Heavy and Starman Landing by SpaceX(https://unsplash.com/@spacex)

In microservices at Twiga, we gave you context on Twiga, talked about where we came from and highlighted our new architecture and the key benefits it provides. If you haven’t read it, you can check it out here. In this part, we would like to tell you more about how we designed the services and about the infrastructure and technology behind our new architecture.

We first needed to design the services, defining what the basics were and by breaking down the monolith functions into microservices. At first, we were super excited and started breaking down every module into a service, I mean as far as we were concerned it made sense to have each module exist independently. However, we quickly realized that it wouldn’t work because some business activities are tightly coupled by default. So we turned to domain driven design(DDD), it is a methodology that discourages teams from focusing primarily on the technology but instead, it encourages teams to look at the business activities that they seek to simplify using technology and then design around that. Because at the end of the day the technology exists to help the business and not the other way round. This is especially true in our context because we are primarily a food company.

Using DDD, the team started by mapping out all the different business activities and placing them under domains based on how dependent an activity was to another activity. Existing modules plus activities helped us determine what features a domain needed, a domain would the form a microservice. However, we also found that activities/features appeared in multiple domains such as payments and would have been inefficient to have different payment implementations in each microservice. In this case, we had to evaluate and determine if a particular feature or activity should exist in its own microservice.

A good example of how we mapped out a domain is the sourcing domain. Twiga sources for produce from farmers which is then transported to a different location for processing. From a high level the activities involved are;

  • Scouting and registering farmers;
  • Visiting farmers and acquiring information on expected harvest dates;
  • Harvesting produce;
  • Transporting produce and paying farmers.
Sourcing Process

By going through all business activities we were able to identify similarities and see all the above-mentioned activities were related and thus under the same domain. However, when mapping out other domains such as sales, we noticed that payments was also a part of the domain, thus we removed it from sourcing and made it a standalone domain. This is because we would have wasted resources if we had different teams implementing the same functionality plus it would cause inconsistencies.

Technology & Infrastructure

After successfully mapping out all the domains and services needed we started looking into technology and infrastructure. We needed to rearchitect our infrastructure and move from one suited for our monolithic architecture to one suited for our new microservices architecture. One that is resilient, persistent and can easily scale. We also needed to decide what technology to use to build our first cohort of microservices.

Framework
At the time we used python for our backend, and our team primarily used and were experienced in python, so we were already sure that it would serve as our primary language. However, we needed a framework to start with and wanted something that was suited for a microservices architecture, simple, bare-bones and with out of the box features suited for the architecture. We considered flask because we were already using it for our backend, our team was experienced in using it and we saw a significant amount of organizations choose to use it for microservices. At the basic, it is a microframework and is very bare-bones especially when compared to frameworks like Django.

We ended up settling for nameko, a microservice framework for Python pronounced as nah — meh — koh. It is an extensible framework that is compatible with almost any protocol, transport or database it comes with powerful built-in extensions suited for microservices. It helps one focus on business logic by managing connections, transports, and concurrency for you. It is scalable and distributed, allowing you to spin up multiple service instances to quickly and effortlessly scale out. While providing easy concurrency by yielding workers as they wait for I/O which allows us to handle multiple requests and not worry about threading. Out of the box it comes with RPC allowing for seamless service to service communication using queues and the pub/sub method. Unlike flask, nameko is built with microservices in mind, is flexible, simple, bare-bones and gave us exactly what we needed and let us figure out the rest. We shall cover and have an extensive tutorial on nameko in a subsequent article, but you can read more at https://www.nameko.io/.

Infrastructure
Moving to microservices needed us to reconstruct our infrastructure to better suit the architecture which also included a change in technologies. We needed infrastructure that wasn’t just VMs but something that allowed services to be autonomous and decoupled but still able to communicate with each other easily. While offering resilience and persistence, to be specific the infrastructure needed to;
- Automate deployments and abstract a lot of infrastructure technicalities from developers;
- Provider each microservice with isolation;
- Handle authentication for all micro-services;
- Provide a single entry point to access all microservices;
- Handle load and effortlessly scale applications;
- Ensure code quality and high tests coverage;
- Optimize developer velocity.

The tools we picked to achieve the above were:
- Kong and GraphQL
- Bitbucket
- Kubernetes and Docker

Kong and GraphQL
Kong is an API gateway built on top of NGINX, it’s like middleware between computing clients and your API-based applications that easily and consistently extends features of your APIs. Some of its popular features available as plugins include authentication, security, traffic control, logging, analytics and monitoring among others. Kong is made up of a server and a data store, the server as previously mentioned is based on NGINX, which is a reverse proxy that processes our client requests to our upstream services. The datastore is where the configurations are stored, allowing for horizontal scale and configuration persistence.

Adding an authentication layer is as simple as adding a plugging available on their plugins page. Which allowed us to add authentication for all our microservices effortlessly, we then use the admin API to configure and manage plugins. It operates at the application level and is broadly compatible with all leading web technologies & orchestration, log management, continuous deployment, and microservice development tools. In fact, it is built with microservice patterns in mind which made it perfect for our architecture and infrastructure.

However, we also wanted one single entry point for our frontend and mobile applications that offered access to multitudinous microservices. This would help us increase velocity for the developers who work on frontend applications while reducing the amount of data our application used because many of our users were operating from rural areas. From the beginning, we knew about GraphQL and knew what it could solve this problem for us. Graphql by definition, “is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data. GraphQL isn’t tied to any specific database or storage engine and is instead backed by your existing code and data.” (Source — graphql.org). One could say that it tries to marry the best of SOAP and REST by providing strong data enforcement, a standard way to expose data operations, the efficiency of modern-day data payloads and really just another form of remote procedure calls(RPC).

We then set out to build a GraphQL API gateway that sat behind the Kong API gateway, and it currently provides us with one single entrypoint that we then use to access all our microservices code and data. Providing applications with the ability to ask for what they need, by simply sending a query to the Gateway and getting back predictable results, nothing more and nothing less. Since then applications have become faster and more stable, they can also requests for multiple resources in one request because queries do not access just the properties of one resource but follow references between them. While typical REST requires a lot more roundtrips to achieve the aforementioned because it has to load data from multiple URLs. Hence GraphQL has provided more efficient data delivery and reduced resources required for roundtrip calls. Also positioning us in a place where we can easily evolve our API without versions, and we can add new fields and types to it without impacting existing queries. Giving applications continuous access to new features, complimenting REST, allowing for customized consumption of services as well as encouraging cleaner and more maintainable code.

Bitbucket
Bitbucket provided us with pipelines which offered continuous delivery in our cloud repository, allowing us to automate and integrate test and builds. Providing a seamless process from development to production, this was essential because the team was growing and would later be divided into smaller teams. We needed to ensure that despite the growth, high code quality would be maintained and that new code would not break anything but at the same time offer a smooth and efficient process that would not negatively impact developer velocity.

Once a developer pushes all code to their feature branch they create a pull request to merge changes. Allowing team members to review the code, having pipelines run both the integration and unit tests as well as have Codacy review the code. After a merge the pipelines run and automating deployment as well as run tests, giving us an extra check before new features roll out. Codacy is a platform that saves us time by handling code review and code quality monitoring, it handles the tiny details allowing our devs to focus on reviewing implementation.

Kubernetes and Docker
Our microservices needed to operate in an isolated environment, and our infrastructure needed to be resilient and scalable. This is where docker and Kubernetes came in because they are built with some of these needs in mind.

Docker provides a way to run applications securely & isolated in a container, packaged with all its libraries and dependencies, ensuring that your application works seamlessly in any environment. A container is a standardized unit which can be created on the fly to deploy a particular application or environment. It could be an Ubuntu container, CentOs container among others. They offer abstraction by which an application can quickly move or be set up with minimal interaction and tinkering. Offering a level of automation that allows us to automate builds and deployments even further. Which is essential in a microservices architecture because each service should be isolated from others and easily scalable.

Kubernetes is a tool used to orchestrate, deploy and manage container clusters, which are a group of containers. This was necessary because at the basic each microservice exists in a container and we should be able to create and manage them quickly. However, it does more than automating and orchestration it provides us with resilience. Because it handles load balancing, ensuring traffic is distributed accordingly to all instances of a service. It self-heals, making sure containers restart once they fail and their tasks are passed to another instance. And most importantly provide us with the ability to horizontally scale, helping solve one of the fundamental problems we faced with our monolith which is resource allocation. With Kubernetes resources are only added in relation to demand of that particular node and not the entire cluster. Allowing our infrastructure team to rest easy if we experience a spike in traffic or resource usage.

A Kubernetes cluster consists of a master node that manages and controls other nodes. Each Node contains one or more pod, the pod then contains at least one application. The master node can then spin up new instances to deal with demand, manages requests to each node and checks the health of each instance.

Going back to the issues we need our new infrastructure to solve we see that automation of deployments and abstraction is seamlessly handled by bitbucket pipelines, docker and Kubernetes. Which work in tandem to ensure deployments are done when new features are added. Kubernetes also handles horizontal and effortless application scaling ensuring that we can meet demand and grow in relation to the business needs. While docker along with Kubernetes ensures microservices exist in isolation regardless of how many instances exist. Plus it allows developers to set up and test on their local machines quickly. Authentication is then seamlessly managed by Kong and our GraphQL gateway providers us with the API of the future by giving us a single powerful endpoint. While Bitbucket helps us ensure high code quality and test coverage. With this new infrastructure tailored for our new architecture, we believed that we were ready to start working on version 2 of our system.

Thank you for reading the article, if you liked it drop us a few claps and for more, follow me and Twiga Tech on twitter & Medium.

--

--