Building Portable Cloud Functions with Spring Cloud Function and Hexagonal Architecture - Part 2 (AWS)

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:

  • Users cannot create more than 5 high-priority tasks per day
  • Task descriptions must be unique per user per day
  • Users cannot have more than 50 open tasks
  • Tasks must have valid priorities (LOW, MEDIUM, HIGH)

Read Operations:

  • Users can only access their own tasks

Update Operations:

  • Completed tasks cannot be modified

Delete Operations:

  • Users can only delete their own tasks

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:

Article content

2. DynamoDB Entity Mapping

AWS DynamoDB requires a bean class with appropriate annotations:

Article content

3. DynamoDB Repository Implementation

Article content

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

Article content

Infrastructure as Code: Terraform for AWS

Main Resources

Article content

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

Article content

3. Infrastructure Flexibility

Swapping from Firestore to DynamoDB requires only:

  • New outbound adapter implementation
  • Configuration change
  • Zero domain logic changes


What's Coming Next

Part 3: Azure Implementation

  • Same domain code
  • Azure Functions adapter
  • Cosmos DB repository
  • Terraform for Azure resources


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

Article content

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.

Like
Reply

This is a fantastic real-world example of how hexagonal architecture truly enables cloud portability !

Like
Reply

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!

To view or add a comment, sign in

More articles by Erick Beloti

Explore content categories