This article explores how to achieve cloud-agnostic business logic in serverless applications, mitigating vendor lock-in while still leveraging native cloud capabilities. It demonstrates structuring FaaS applications using Clean Architecture, Spring Cloud Function, and Gradle modules to isolate core business logic from infrastructure concerns. The presentation includes a live demo of deploying portable Kotlin services across AWS Lambda and Azure Functions using Terraform CDK.
Read original on InfoQ CloudServerless architectures, particularly Function-as-a-Service (FaaS), offer benefits like automatic scaling, pay-per-use billing, and reduced operational overhead. However, a significant concern for architects and developers is vendor lock-in, as specific cloud provider SDKs and deployment models can tightly couple business logic to a single platform. This presentation addresses this by proposing an architectural approach that enables business logic portability across different cloud providers.
Key Principle
The core idea is to encapsulate the pure business logic in a module that has no direct dependencies on any cloud SDKs or FaaS runtime specifics. Cloud-specific integration details are handled by separate adapter modules.
The approach demonstrates how, even with Spring Cloud Function, some cloud-specific dependencies for triggers and entry points are often unavoidable. The solution is to use Gradle modules to isolate these dependencies. For instance, an Azure-specific module would contain the Azure Spring Cloud adapter and the Azure Function entry point, while an AWS-specific module would handle the AWS Lambda adapter and its entry point. The crucial business logic remains in a shared, independent module.
To deploy these portable services, Terraform CDK (Cloud Development Kit) is utilized. Terraform CDK allows developers to define infrastructure using familiar programming languages (like Kotlin in this case), which then synthesizes into Terraform configuration files. This enables a unified approach to provisioning the necessary FaaS resources (Lambda functions, API Gateways, Azure Function Apps) across different cloud environments, pointing to the same core business logic artifact.
// Example of a cloud-agnostic business logic interface (Kotlin)
interface DocumentProcessingService {
fun processDocument(document: Document): ProcessedDocument
}
// Cloud-specific adapter for AWS Lambda
class AwsDocumentProcessorHandler : SpringBootRequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent>() {
// Delegates to DocumentProcessingService
}
// Cloud-specific adapter for Azure Functions
class AzureDocumentProcessorFunction(@Autowired val service: DocumentProcessingService) {
@FunctionName("uploadDocument")
fun uploadDocument(@HttpTrigger(name = "req", methods = [HttpMethod.POST], authLevel = AuthorizationLevel.FUNCTION) request: HttpRequestMessage<Optional<String>>): HttpResponseMessage {
// Delegates to DocumentProcessingService
}
}