Case Study: Building Payment Microservice
Our customer lacked capacities in his own development team. They needed to build a stable, complex application to manage their business processes that is ready for the fast growth of the company. Therefore the app had to be easy to extend and maintain for future development. One of the requirements was to automate the process of paying out funds from an external payment service to clients’ bank accounts. Plus the code had to be friendly to the new programmers in the future. How have we done that?
Our customer’s goal is to create opportunities for communities to live in one place. They deal with building and renting residential premises with shared kitchens, living rooms, laundry rooms and bathrooms. They create conditions for people to spend some time together.
What the company needed, was stable application that manages business processes between clients, real estate management companies and customer’s team itself. They lacked development capacity in their own team. Another challenge was to make the application ready for the future growth of the company.
Therefore we had to develop an application that will be easy to extend and maintain in the future. So we separated a module for managing payments from the monolithic application and created a microservice to provide the opportunity for easier and faster development. The code is well documented to be friendly for new programmers in future. Also, we reduced some company costs by automating some processes.
Project and application overview
When we started, customer has had a monolithic application to manage business processes in the company. It contained several modules, each of which was responsible for different functionality. One of the requirements was to deal with processing payments between clients, real estate management companies and our customer.
The main types of payments were:
● monthly rental fees
● subscription to ‘helping hands’ service
Moreover, depending on the payment type and the rented building, the system had to handle an additional fee that was added and two ways of making deposits and withdrawals of funds i.e. bank transfers and card payments.
Because of the fact that the application was constantly growing, they created a library to separate implementation that contained:
● communication with external payment service
● monitoring resources in the external service
● processing bank transfers and card payments
● managing subscriptions, properties and customers.
Problems and client goals
Despite the fact that the application had to meet the requirements of end customers, it also had to keep up with the development of the company, which was constantly growing. That is why one of the client’s goals was to automate the process of paying out funds from an external payment service to clients’ bank accounts. Thanks to this, the company could dedicate human resources to other areas that would increase its profits and ensure faster growth.
The development of the company goes hand in hand with the development of software, and this requires easy introduction of new functionality. For this to be possible, you need to constantly look after and improve the quality of the code. Our client was well aware of this, which is why it was another goal of our cooperation.
The last goal we faced was to separate the module for managing payments from the monolithic application and to create a microservice with which it could communicate.
Solutions and our goals
A team of five developers was appointed to work on the project: Eduardo from Brazil, Iain from Vietnam, Ondrej from the Czech Republic, Sergey from Russia and me from Poland. In addition, the client assigned two people from his side.
The first step to provide the client with stable and easily extensible software was a profound analysis of existing implementation and understanding of the business domain. We had to know not only what requirements the business sets for us, but also what changes we can expect in the future. To achieve this, we organized regular meetings with the team members on the client’s side who had been involved in creating the software from the beginning. This contributed to the continuous deepening of our knowledge about business, design, technologies and it made our domain knowledge good enough to provide a solid and well-tested code that solves customer’s problem.
As our knowledge about the project grew, we proceeded to separate the previously mentioned module into a microservice. We decided that the communication between the main application and the new service would take place via HTTP and we built a GraphQL API on top of that. Additionally, we had to identify entities that were associated with the module and place them in a new database available only for the newly created service.
Below you can see a simplified diagram of the entire system after the introduced changes:
Every month, people from the accounting department had to deal with cash withdrawals from an external payment service to bank accounts. The functionality of the external service was sufficient until the number of customers and withdrawals reached the level where more human resources were needed.
To solve the problem of rising costs, we created a service that automated this process using the payment service API. It was monitoring transactions and checked which of them had to be paid out to customers. In order for everything to run successfully it was necessary to additionally monitor the balances of real estate accounts and supplement them with the appropriate amount of money in order not to reach a negative balance.
● Clojure – dynamic, general-purpose programming language (https://clojure.org)
● Datomic – distributed, functional database (https://www.datomic.com)
● Pedestal – set of libraries used for building services (https://pedestal.io)
● Lacinia – Clojure library implementing GraphQL interface (https://github.com/walmartlabs/lacinia)
● Lacinia pedestal – library exposing lacinia as HTTP endpoints (https://github.com/walmartlabs/lacinia-pedestal)
● Quartzite – scheduling library (http://clojurequartz.info)
● Bond – spying and stubbing library used for testing (https://github.com/circleci/bond)
● Cljfmt – tool library used for code formatting (https://github.com/weavejester/cljfmt)
● Venia – Clojure/ClojureScript library to generate graphql queries (https://github.com/Vincit/venia)
● Reverse venia – Clojure library used for parsing GraphQL queries into Venia syntax (https://github.com/ondrs/reverse-venia)
Our team works full time remotely. It is a very comfortable work style which implies physical and psychological comfort. Personally, it makes me more productive than when I work in the office. In addition, our team do not waste time on travelling to the workplace and can devote that time on relaxing or to additional work if necessary. It might seem to be difficult to organize a meeting when everybody has flexible working time, but each of us is aware of this. Therefore we plan the time in advance to perform our tasks and to attend important meetings with no difficulties.
We regularly organize meetings with programmers from other teams such as pair and team programming sessions, during which we solve problems, analyze application architecture, design new solutions, learn new technologies and get to know new tools that improve the quality of created software and accelerate its development. If I can speak in the name of the whole team, we enjoy sharing our experiences, ideas and suggestions about particular problems and solutions, because we believe that we can learn a lot from each other.
We know that customer will be satisfied in the long term only if the product we are working on will be reliable, easy to extend and maintain. We know that by setting such requirements, we give our clients software that will reduce their costs.
Our cooperation was successful – the same priorities translated into a good understanding between the teams and the pursuit of the same goals. Frequent meetings and discussions on the project and needs helped us develop the product very quickly. We took care of every detail of the implementation and wrote tests for our solutions. We documented the written code to minimize the time needed for its good understanding but also to be friendly for new programmers in the project.