Modernize Your Payment System with Microservices: Part 2 – Strategic Decomposition of Monoliths into Microservices.

Tuesday, Mar 05, 2024

As we continue our exploration in the Legacy Migration Series, following the insights from our previous blog, we delve deeper into the transformative world of microservices. This shift from monolithic architectures to microservices is not just a trend but a strategic necessity in the financial services sector. In this second installment, we focus on the strategic decomposition, crucial for businesses aspiring for agility and scalability in a rapidly changing technological landscape.

Why Decompose?

Strategic decomposition involves breaking down a monolithic system into smaller, independently deployable services, each running a unique process and communicating through APIs.  This separation of functional components into microservices allows for more adaptable, manageable, and scalable systems. Importantly, it also dictates that application teams to be re-formed to align with a discrete business domain. 

Key Principles for Decomposition

  • Start small and scale: One of the many benefits of microservices is that they allow for encapsulation of previously intertwined aspects of a monolithic application. This means that adoption can begin small as you identify low-hanging fruit: services that have non-critical or net-new functionality, few downstream logical and/or data dependencies, loosely coupled resources and code, or have low customer usage. The anti-corruption layer architectural pattern can assist with this process by acting as a translation layer between new services and legacy systems, ensuring that the former do not have to inherit or implement the paradigms of the latter. 
  • Strict API usage: To avoid building a “distributed monolith”, close attention and rigour must be applied to the adoption of API contracts, with access to resources and functionality strictly controlled through the API. Common API formats include OpenAPI, AsyncAPI, GraphQL, and gRPC. No contract once deployed should be changed in a way that requires updating more than one service bound to that contract. If you cannot adhere to the API because some services must share a database or there are performance considerations then those services are not good decomposition candidates.
  • Resilience and fault isolation: Design services to be ephemeral but cooperatively resilient, ensuring that failures in one area have limited impact down the dependency chain. Consider partitioning service instances into bulkheads to isolate failure cascades, using asynchronous messaging to coordinate and scale business operations, and implementing sagas or event sourcing to distribute transactions across services.
  • Team/squad communication should reflect the microservices structure: If one squad is consistently asking another for information on dependencies, you likely do not have a microservices structure. Ensure your teams can update and push code to production as often as they can, so long as they respect the API contracts in place.
  • Benefits must be outlined and analysed: there is little reason to perform a potentially costly refactor if there are no clear advantages for the development teams or the business.

Real-World Domain Breakdown

Complex relationships can be modelled and decomposed into various subsystems by leveraging domain-driven design (DDD) and/or event-driven architecture (EDA) strategies. A non-exhaustive strategic decomposition of a payments system into several domains can be as follows:

  • Payment Processing: Segregating payment gateways for handling diverse payment methods, limits, and currencies.
  • User Management: Creating separate services for managing user profiles, authentication, and authorization.
  • Fraud Detection: An independent service dedicated to identifying and mitigating fraudulent activities.
  • Messaging Services: Incorporating a dedicated subsystem for handling internal alerts as well as ensuring seamless external communication with users.

Risks of Poor Domain Separation in Microservices Architecture

Inadequate domain separation in transitioning to a microservices architecture can inadvertently create a distributed monolith. This misstep fails to deliver the benefits of a true microservices model and introduces several risks:

  • Increased Complexity and Maintenance Challenges: Improperly separated domains lead to tightly coupled services, negating microservices’ modular benefits. This complexity complicates maintenance, as changes in one service might unexpectedly impact others.
  • Performance Issues: Poorly defined domains result in redundant processing and increased latency, harming system performance and escalating operational costs.
  • Scalability Limitations: Microservices’ scalability is compromised when domains are not isolated, impeding the ability to scale specific components efficiently.
  • Troubleshooting and Fault Isolation Difficulties: Ambiguous domain boundaries make pinpointing issues challenging, leading to prolonged downtime and reliability concerns.
  • Reduced Team Autonomy: Effective team independence, a key microservices feature, is hindered by increased dependencies due to poor domain separation.
  • Compromised Data Integrity: Shared databases or data models between domains that should be distinct can cause data inconsistency, undermining the system’s reliability for decision-making.
  • Challenges in Continuous Delivery: The goal of seamless continuous delivery in microservices is jeopardized, complicating deployment processes and undermining automation and consistency efforts.

Closing Thoughts

This journey from monolithic systems to microservices, as explored in this second installment of the Legacy Migration Series, marks a strategic transformation crucial for