As we’ve talked about previously on this blog, our Survey Junkie application has a large codebase. The Survey Junkie application implements a hybrid architecture. Some parts of the application are developed and deployed as microservices, while the rest of the application remains monolithic.
We determine which parts of the monolith to turn into microservices based on business considerations and the following technical considerations.
- Scalability: Identifying the parts of the application that need to scale more than others.
- Security: Determining which parts of the application are prone to the most security threats. Breaking these parts into microservices can help isolate them and mitigate the security risks they pose.
- Applying Updates: One crucial benefit of microservices is that they provide the flexibility to update parts of an application without disturbing other parts.
We have multiple teams working on the transition from monolith to microservices. And currently, we have broken off parts of our monolith into eight microservices.
Object-Oriented Programming (OOP) has helped us improve code modularity and maintainability as we’re transitioning. We’ll also talk about Single Responsibility in SOLID design and using Traits in hybridizing the Survey Junkie application.
In general, it’s less difficult to transition from monolith to microservices if the codebase follows OOP principles. One example of this is large classes versus classes with a single responsibility. OOP emphasizes that a class should focus on doing one thing well and behave as a single unit. If an object’s complexity exceeds a certain level, we refactor it into two or more separate entities. Large classes can be difficult to read, maintain, and extend.
The main principles that we’ve employed are:
- Polymorphism: We have multiple cases in the codebase where we use method overloading or method overriding according to the functionality needed in that specific class.
- Inheritance: As a team, we focus on identifying parent/child classes and keeping the code DRY (Don’t Repeat Yourself).
- Encapsulation: We identify the variables and methods we want to bundle together within classes and their scope.
- Abstraction: Multiple services within the application perform various tasks, but we employ the abstraction principle to reveal the necessary details of each service and reduce complexity.
As a team, we have a code convention of adding detailed comments to complex methods. These comments describe the method or function and the inputs and outputs. Since we re-use function names, this code convention makes it easier to use polymorphism.
The S in SOLID stands for Single Responsibility. A class should have one and only one reason to change, meaning that a class should have only one job.
By following the Single Responsibility Principle, we get better code maintainability, and it makes it easier for developers to find classes that perform a specific function. Single Responsibility also allows for extensibility. If new functionality is needed, it’s added to the appropriate class or set of classes.
In the Survey Junkie application, multiple services handle different functionalities for a user. For example, our UserService class handles CRUD operations on a user. Then, there is a UserRewardService that handles the logic to reward users. Since we have these separated, it will be much easier to transition this functionality to microservices if the team decides to in the future.
Breaking down classes in this way allows for ease of maintenance and greater flexibility as the codebase grows. Also, this provides ease of code readability for a new person who may be seeing the codebase for the first time. I can speak on this from personal experience since I’m in my first 90 days at DISQO and am still getting accustomed to the codebase.
One of my very first tasks was to expand our unit test coverage. I could easily see each class’s purpose since the codebase has discreet services. I could also quickly navigate the class structure and identify the various touchpoints to construct the unit tests.
At my previous company, I implemented Traits to decrease repetition and file size. When I started at DISQO, I wanted to see if code repetition was handled via Traits. Traits are a feature offered by PHP to handle multiple inheritance.
By default, PHP only supports single inheritance, which means that a child class can only inherit from one single parent. Although this may seem like a massive disadvantage, the objective of allowing single inheritance is to avoid confusion between one class inheriting properties of multiple classes (for example, multiple classes having the same function names).
A common use case for a Trait is to combine methods in multiple classes. For example, in Survey Junkie, we have a ConfigTrait that sets the configuration for multiple survey providers. In the context of Survey Junkie, a survey provider is an external company or entity that provides specific surveys for the user to complete to earn rewards. Each survey provider is a separate entity (so, a separate class), but there are shared methods between them.
To accommodate these shared configuration methods, we implement the ConfigTrait and use it in all external survey provider classes that require the functionality. Since we implement Traits, we can use multiple different Traits for the external survey provider classes and make them available to us when needed. Multiple Traits can be used within a single class and inherit the methods of each Trait. This is useful since it allows developers to write methods reusable in any number of classes, keeping the code DRY (Don’t Repeat Yourself) and maintainable.
It is often easy to neglect the core OOP principles, especially when working in a fast-paced startup environment. It may not seem useful to spend the extra time to understand the principles and apply them. But if the application codebase starts by following the principles, it’s much easier to maintain and scale. The SOLID principles go a step further in terms of software development guidelines. In this article, I chose to talk about the S in SOLID, or Single Responsibility, because this is a crucial concept we strive to employ as we transition from monolith to microservices. Lastly, PHP Traits are a great tool for keeping code DRY and maintainable. The concept of reusing code in multiple places is extremely powerful, especially when working on a large application.