Recently, service-oriented architecture has been buzzing with lively debates, especially when it comes to Microservices architecture and its industry-wide adaption. This sparked my curiosity to look back into the realm of Microservices, exploring the fascinating challenges and intricate nuances of its implementation.
I have been actively engaged in large-scale solutioning and implementations, primarily utilizing Microservices architecture.
I have compiled a summary of the real-life experiences and challenges my team and I have faced during solution design, development, and implementation. No single architectural pattern suits all use cases and scenarios, and this applies to Microservices as well. There are situations where Microservices might be excessive, unnecessary, or maybe an overkill. Solution Architects with prior experience and exposure to such implementations are in the best position to take such calls.
Microservices
Microservices, as you may already know, is an architecture pattern that advocates breaking down application functionality into multiple independent small deployment units. Designing your services and defining their functional boundaries is a separate exercise best done in collaboration with your architects with inputs from BAs, business stakeholders, and product managers. You should also consider other non-functional influencers like your budget, timelines, security, scalability, etc. Please refer to my previous blog for more details https://www.sachintah.com/post/implementing-microservices-part-i
What's Covered?
This blog primarily focuses on Pre/Post implementation checklists when planning to design and implement enterprise applications using Microservice architecture. The debate often revolves around aspects such as performance, scalability, and the complexity of such implementations. I would like to invite you to discuss these issues and checklists further, as I believe it will be beneficial for our understanding and future projects.
When developing large-scale enterprise applications, there are common requirements such as security, fault tolerance, scalability, accessibility, interoperability, and cloud compliance. Additionally, it is essential to create applications that are straightforward to build, deploy, and maintain.
Post-COVID, another challenge has emerged: teams are now often remote and globally distributed, making coordination, collaboration, and alignment, particularly within engineering teams, more complex.
With such intent, Microservices still hold good because of the way functional boundaries are defined, independent implementations are possible, cross technologies can be leveraged etc.
Implementation Checklist
Decomposing monolithic applications into multiple services introduces additional complexities, making development, management, and maintenance more challenging. This process requires a team of experienced and skilled professionals; otherwise, the entire platform could become unmanageable.
Define Service Boundaries – This is a design phase checklist that requires a comprehensive understanding of business functions, actors, and use cases, particularly across transaction boundaries. Prior experience is essential to define a clear application scope with minimal overlap of functionalities and data. Adhering closely to SOLID design principles is recommended.
Manage Data Duplication – After all, it’s all about data, it is important to understand and realize that using Microservices is going to result in data duplication across services and databases. It is important to devise a strategy for how data will be processed across these services. One-time Inserts are easy, but special precautions are required in handling data updates.
Messaging queues are always a savior here, using the Pub-Sub pattern which again needs to be carefully designed and implemented. Having too many Pub-Subs will cause system overheads and performance challenges.
Handle Distributed Transactions – If you have implemented DB per Microservices pattern then managing transactions spanning across Microservices will be distributed transactions. Distributed transactions need special handling and attention. You may need to implement the Saga pattern for the same. Services spanning across multiple enterprise systems (Legacy/Modern) will again impose further challenges and may require efficient handshake mechanisms. You can find more specific details on transaction handling on my earlier blog https://www.sachintah.com/post/implementing-microservices-part-ii
Manage Interservice Communications – One of the most important aspects of Microservices is that these services should be able to communicate with each other, the best option is to use a messaging queue, however, there could be scenarios where you may need to go for API-based communications. Using out-of-the-box services like Service Mesh can also help in effective and secure service-to-service communication.
Use Service Registry – One of the most amazing capabilities of a Microservice-based ecosystem is the ability to scale and perform load balancing at individual service levels. This feature is so powerful that it gives you immense flexibility to scale only a desired Microservice at a time depending on the application load. To comply and benefit, it is important to ensure your microservice follows design guidelines and supports such auto-scaling functions. Service discovery and registry are also two important aspects of Microservices. You need to implement a service registry feature within your Microservice. You can use either third-party registry services like Consul or registry services offered by platforms like Kubernetes.
Manage Security - Managing service security for inter-service communication along with overall application security needs good planning, tool understanding, and the underlying deployment ecosystem. Using service mesh is also one of the options to implement centralized governance. Security can also be implemented using an Identity provider either custom or third-party using a Token-based authentication mechanism.
Perform Integration Testing – Integrating and testing monolithic applications is easy and natural as your deployments are consolidated under one roof. Enterprise use cases may have 10 or more services working in sync with each other and deployments may or may not be under one roof or ecosystem. Integrating and then testing these services and ensuring test coverage is a challenge and requires significant collaboration between test and development teams. Integration test scenarios are also difficult to envision, write, and execute.
Perform Performance Testing – Performance testing for microservices should be conducted at two levels. The first level involves evaluating the performance of individual services, while the second assesses performance in an integrated environment where all services operate collectively. Testing the performance of reactive jobs presents challenges, as conventional scripts may not generate adequate loads for reactive services.
Implement Application Observability – Observability enables automated and proactive data collection from various systems and subsystems which enables valuable app insights used to monitor and detect issues even before they arise. Application observability is a broad topic and works in conjunction with the application along with the set of available tools and platform/ecosystem you are selecting for production deployment.
Plan Deployment & Scaling – If you are planning to deploy your services inside a VM or a single server, this is a waste of your time and effort in building service-based architecture.
Microservices should be designed in such a way that it should be easy to deploy in any container of choice. You should not be writing service code that is container or deployment ecosystem-specific. You may need to write code to integrate these services with a service registry of choice, however, using configuration files can help you avoid such tight couplings.
A non-container-based data storage policy should be prioritized, ensuring all file uploads are stored either in your database or on network drives. Temporary storage can enhance user experience, but it is crucial to clear this storage once the upload is finished and transfer the file to a permanent system. Microservice deployment should be integrated into your deployment scripts, and each service should operate independently of its deployment environment.
Get Analytics & Reporting – The distribution of data across microservices poses challenges for obtaining insights necessary for analytics and reporting. To address these issues, it is crucial to implement data collection strategies, such as establishing a data lake, to enable effective analytics and reporting.
I am eager to further explore the intricacies of MS-oriented architecture. Please feel free to comment or message me if you believe I have overlooked anything.
Sachin