Building Portable Cloud Functions with Spring Cloud Function and Hexagonal Architecture - Part 2 (AWS)
In Part 1, we built a Task Management System using Spring Cloud Function and Hexagonal Architecture, deploying it to Google Cloud Functions with Firestore. The power of this approach becomes evident in Part 2: we'll deploy the exact same business logic to AWS Lambda with DynamoDB, changing only infrastructure adapters.
No domain logic modifications. No business rule changes. Just infrastructure swapping.
This is the promise of true cloud portability.
Recap: What We Built
Our Task Management System implements comprehensive business rules:
Create Operations:
Read Operations:
Update Operations:
Delete Operations:
These rules live in our domain layer, completely cloud-agnostic.
The AWS Implementation Strategy
What Stays the Same (Zero Changes)
✅ Domain entities - Task, TaskId, UserId, Priority, Status
✅ Business rules - All validation logic remains identical
✅ Application services - TaskService orchestration unchanged
✅ Use case interfaces - Same inbound ports
✅ Function definitions - Same Spring Cloud Function beans
What Changes (Infrastructure Only)
🔄 Outbound adapter - DynamoDB replaces Firestore
🔄 Maven configuration - AWS dependencies replace GCP
🔄 Infrastructure as Code - Terraform for AWS resources
🔄 Configuration - AWS-specific application properties
AWS-Specific Implementation
1. Maven Dependencies
The project uses Spring Cloud AWS with a profile-based approach:
2. DynamoDB Entity Mapping
AWS DynamoDB requires a bean class with appropriate annotations:
3. DynamoDB Repository Implementation
Note: This implementation uses scan() with stream filtering for simplicity. In production with large datasets, use the UserIdIndex GSI with query() operations.
4. AWS Configuration
Infrastructure as Code: Terraform for AWS
Main Resources
Deployment Process
Build and Deploy
# Build AWS-specific JAR
mvn clean package -Paws
# Deploy infrastructure
cd terraform/aws
terraform init
terraform apply
# Get Lambda Function URL
terraform output function_url
Testing in Action
Function Routing
Spring Cloud Function uses a routing mechanism to direct requests:
# Create a task
curl -X POST "$FUNCTION_URL" \
-H "Content-Type: application/json" \
-H "function.name: createTask" \
-d '{
"userId": "user123",
"description": "AWS Lambda task",
"priority": "HIGH"
}'
# List user tasks
curl -X POST "$FUNCTION_URL" \
-H "Content-Type: application/json" \
-H "function.name: listTasksByUser" \
-d '{"userId": "user123", "page": 0, "size": 10}'
Business Rules Validation
# Try to create 6th high-priority task (should fail)
curl -X POST "$FUNCTION_URL" \
-H "Content-Type: application/json" \
-H "function.name: createTask" \
-d '{
"userId": "user123",
"description": "Sixth high priority task",
"priority": "HIGH"
}'
# Response: HTTP 422
{
"status": 422,
"errors": [{
"code": "BUSINESS_RULE_VIOLATION",
"message": "Cannot create more than 5 high priority tasks per day"
}]
}
Key Benefits Demonstrated
1. Pure Business Logic
The domain layer contains no AWS-specific code. Rules like "max 5 high-priority tasks per day" are expressed in pure Java.
2. Testable Architecture
3. Infrastructure Flexibility
Swapping from Firestore to DynamoDB requires only:
What's Coming Next
Part 3: Azure Implementation
The Power of True Portability
Our AWS implementation proves that Hexagonal Architecture + Spring Cloud Function delivers on its promise:
✅ Zero business logic changes - Rules work identically
✅ Minimal infrastructure code - Only repository adapter needed
✅ Same development experience - Testing and debugging remain consistent
✅ Risk mitigation - True cloud portability reduces vendor lock-in
Get Started
Ready to deploy the same Task Management System to AWS Lambda?
🔗 Complete source code: GitHub Repository
Repository Structure After AWS Implementation
Quick Commands Reference
mvn clean package -Paws
# Deploy to AWS
cd terraform/aws && terraform apply
# Test the deployment
./scripts/test-functions.sh aws
./scripts/test-business-rules.sh aws
Coming up in Part 3: The same business logic will run on Azure Functions with Cosmos DB. Three clouds, one codebase, infinite possibilities.
Follow me to get notified when Part 3 drops! Have questions about the AWS implementation? Drop them in the comments below.
Love seeing the practical application of Hexagonal Architecture enabling such seamless cloud portability. Moving your Task Management System from GCP to AWS Lambda while keeping all business logic intact is a brilliant example of how to build resilient systems.
This is a fantastic real-world example of how hexagonal architecture truly enables cloud portability !
Really nice article, Erick! I really like how you showed that in Hexagonal Architecture, the business logic stays completely independent from the cloud provider, enabling true portability and flexibility. I'm looking forward to your next posts!
Nice post !