Untangling the Serverless Web: A Step-by-Step Guide to Refactoring Your SaaS Architecture

“Architecture is about the important stuff. Whatever that is.” – Martin Fowler

Breaking Free from Serverless: Reimagining Your SaaS Platform Architecture

Decomposing and refactoring SaaS platforms built using a serverless architecture to a different architecture can be complex but offers benefits such as enhanced control, cost optimization, or performance improvements. Serverless architectures, like those based on AWS Lambda, Google Cloud Functions, or Azure Functions, focus on fine-grained, event-driven execution, but they come with limitations in control over infrastructure, long-lived processes, and complex state management.

Here’s an overview of different approaches to moving from a serverless architecture to another architecture, such as microservices, containers, or monoliths:

Serverless to Containerized Microservices
  • Overview: Transitioning from a serverless architecture to containerized microservices involves migrating individual serverless functions into microservices deployed in containers (using Docker or Kubernetes). This provides more control over runtime, scalability, and network configurations.
  • Approach:
    • Service identification: Start by identifying serverless functions that can be grouped into cohesive, bounded contexts. Functions with similar responsibilities or dependencies can be bundled into services.
    • Containerization: Containerize each group of functions into microservices using Docker. Each microservice would have its own runtime environment, enabling fine-tuned control over performance, resource allocation, and scaling.
    • Orchestration: Use Kubernetes or other orchestration platforms (like ECS or Docker Swarm) to deploy and manage these microservices at scale. Leverage container orchestration to handle fault tolerance, scaling, and service discovery.
    • Communication: Replace direct event-based triggers (e.g., AWS Lambda’s API Gateway, EventBridge) with API endpoints or asynchronous communication via message brokers like Kafka, RabbitMQ, or AWS SQS.
  • Challenges: Requires careful orchestration and network design to replace the serverless event-driven nature with microservice-to-microservice communication. Increased operational complexity due to managing containers and orchestration.
  • Benefits: Provides greater control over execution environments, easier debugging, and optimizes cost when there are frequent or long-running processes.
  • Best Use Case: SaaS platforms with complex, resource-heavy operations that suffer from serverless cold starts, limited execution time, or require persistent processes.
Serverless to Monolithic Architecture
  • Overview: Refactoring a serverless platform to a monolithic architecture involves consolidating individual serverless functions into a single application. This can be beneficial for systems that don’t require extreme horizontal scalability and are better served by simpler deployment and operational processes.
  • Approach:
    • Consolidation: Identify serverless functions that belong to the same business domain or bounded context and merge their logic into cohesive modules within a single codebase.
    • Shared state: Manage shared resources, states, or data (which in serverless would be handled by external storage like S3, DynamoDB, or external databases) within the monolith, reducing the overhead of external calls.
    • Single deployment: Deploy the entire application as a single unit, either as a monolithic app in a virtual machine or on a platform-as-a-service (PaaS) such as Heroku, AWS Elastic Beanstalk, or Google App Engine.
    • Centralized routing: Replace the serverless event triggers with a centralized API layer (using Express.js, Spring Boot, Flask, etc.) that handles all incoming requests in a monolithic routing system.
  • Challenges: Reintroduces the limitations of a monolith (e.g., scaling, updates, and failure domains). Transitioning from distributed, event-driven functions back to tightly coupled services can reduce flexibility.
  • Benefits: Simplifies deployment and debugging. Can reduce the operational complexity and cost if the platform doesn’t need to scale to thousands of users simultaneously.
  • Best Use Case: Small or medium-sized SaaS platforms that do not have a high scale or frequent demand spikes, or systems that require transactional consistency and complex state handling.
Serverless to Service-Oriented Architecture (SOA)
  • Overview: Decomposing a serverless architecture into an SOA involves organizing the system around services that communicate over well-defined interfaces (e.g., REST, SOAP). Unlike microservices, SOA may centralize certain components like authentication or orchestration.
  • Approach:
    • Service identification: Map serverless functions into larger services that represent business capabilities (e.g., user management, payment processing). Each service may consist of several former serverless functions combined into one or more modules.
    • Centralized service bus: Replace the event-driven nature of serverless with a service bus (ESB) or API Gateway that handles service communication, routing, and orchestration.
    • API-driven communication: Expose services via standard APIs (REST, SOAP) instead of serverless function endpoints. Use centralized logging, authentication, and authorization for consistent service interactions.
    • Stateful services: Services that handle longer-running processes, transactions, or complex workflows can now manage their state internally rather than relying on external databases triggered by serverless invocations.
  • Challenges: SOA can reintroduce a higher level of coupling and complexity, especially in managing dependencies between services. Requires good service boundary definitions.
  • Benefits: Allows for the reuse of services and better integration with legacy systems. Easier to manage complex transactions and orchestrated workflows.
  • Best Use Case: Platforms that need more control over execution environments and service interactions but want to retain some of the decoupling benefits of serverless.
Serverless to Event-Driven Microservices
  • Overview: Instead of pure microservices, transitioning to event-driven microservices builds on the serverless event-driven model but introduces long-running services that handle more complex, distributed operations.
  • Approach:
    • Event streams: Use event-driven architectures by leveraging technologies like Apache Kafka, Amazon Kinesis, or Google Pub/Sub to handle events instead of individual serverless triggers.
    • Microservices: Break serverless functions into event-driven microservices that listen to message streams, process data, and respond to events, while handling their own state, error recovery, and retries.
    • State management: Implement stateful services where required, using databases like Cassandra, DynamoDB, or Redis for state persistence instead of ephemeral serverless invocations.
    • CQRS (Command Query Responsibility Segregation): Implement CQRS by separating read and write responsibilities between microservices, allowing for optimized handling of complex, event-driven data flows.
  • Challenges: Increased operational complexity and overhead in managing the message brokers, event streams, and maintaining service state.
  • Benefits: Retains the decoupling benefits of serverless while allowing for better handling of long-running processes, larger data volumes, and more complex state management.
  • Best Use Case: Systems with high concurrency, data-driven workflows, and real-time processing requirements (e.g., IoT systems, real-time analytics, or trading platforms).
Serverless to Function-as-a-Service (FaaS) on Containers
  • Overview: Instead of fully leaving serverless, you can refactor the system to use FaaS on containers like OpenFaaS or Kubeless. This allows functions to run in a containerized environment, gaining the benefits of serverless while operating within a more flexible and controlled container ecosystem.
  • Approach:
    • Containerized functions: Move serverless functions to containers using platforms like OpenFaaS or Knative, which allow functions to scale like serverless but provide more control over runtime, networking, and scaling.
    • Event gateways: Replace cloud-provider-specific event triggers (like AWS Lambda API Gateway) with container-native event gateways that trigger containerized functions.
    • Kubernetes: Use Kubernetes for orchestration, allowing functions to run in a managed container cluster that provides higher flexibility and scalability.
  • Challenges: Increased complexity in managing Kubernetes and the function orchestration platform compared to purely serverless.
  • Benefits: Retains the flexibility of serverless with more control over execution environments and scalability.
  • Best Use Case: SaaS platforms looking for more control over their serverless environments without moving completely away from function-based execution models.
Serverless to Hybrid Cloud Architecture
  • Overview: A hybrid approach involves moving part of the serverless architecture to a more traditional infrastructure while retaining serverless functions where they make sense (e.g., lightweight, stateless operations).
  • Approach:
    • Hybrid decomposition: Keep stateless, event-driven functions as serverless while migrating complex, stateful services to microservices, containers, or on-premise infrastructure.
    • Hybrid data management: Use a hybrid data strategy by moving stateful operations or heavy data processing to databases and services running in dedicated VMs or containers.
    • Workload segregation: Migrate compute-heavy or persistent workloads from serverless to traditional infrastructure where they can be better optimized for cost and performance.
  • Challenges: Balancing different architectures can introduce operational overhead and complexity, especially in maintaining consistent communication across hybrid systems.
  • Benefits: Allows for flexibility and cost optimization—serverless for lightweight, ephemeral tasks and traditional architecture for persistent or resource-heavy services.
  • Best Use Case: SaaS platforms with mixed workloads where serverless is cost-effective for some components but not for others (e.g., data processing pipelines, real-time APIs).

Wrapping up…

Each of these approaches involves varying degrees of complexity and operational overhead, but the ultimate choice will depend on specific platform requirements—like scalability, performance, control, and cost considerations. By carefully evaluating the trade-offs, you can select an architecture that meets the evolving needs of your SaaS platform.