Skip to main content

Cloud Provider Architecture

The Zenoo Hub’s cloud provider architecture implements a clean separation between business logic and infrastructure through a well-defined abstraction layer. This document provides a technical deep-dive into the architecture, design patterns, and implementation details.

Architecture Overview

The cloud provider abstraction consists of three layers:

Module Structure

Hub Domain

Module: hub-domain Purpose: Contains pure domain models shared across all layers. Key Classes:
  • ComponentId - Component identifier (name + revision)
  • StringComponent - Component definition with DSL
  • ApiKeySecret - API key with permissions
  • ApiKeyPermission - Permission definition
  • ComponentApiKeyLookup - API key lookup data
  • SharableRecord - Sharable token record
  • ExecuteRequest - Execution request
  • ComponentConfigId - Configuration identifier
Dependencies: Minimal
  • Jackson (for JSON serialization)
  • Lombok (for boilerplate reduction)
  • Spring Core (utilities only)
Design Principles:
  • No cloud provider dependencies
  • No business logic
  • Immutable where possible (using records)
  • JSON-serializable

Cloud Provider API

Module: cloud-provider-api Purpose: Defines provider-agnostic interfaces that all cloud implementations must satisfy. Key Interfaces:

Storage Interfaces

ComponentStore:
SharableStore:
ApiKeyStore:

Configuration Interfaces

ComponentConfigReader (Blocking):
ComponentConfigStorage (Reactive):

Secrets Management

ApiKeySecretStorage:
Exception Hierarchy:
Error Types:
Dependencies:
  • hub-domain (domain models)
  • Spring Boot (configuration properties)
  • Project Reactor (reactive types)
  • Jackson (JSON serialization)

Backend Adapters

Location: backend/src/main/java/com/zenoo/hub/component/{config,security} Purpose: Bridge between backend’s domain-specific types and cloud provider’s generic types.

Adapter Pattern Implementation

1. AttributeComponentConfigReaderAdapter Responsibility: Convert JSON strings from cloud provider to backend’s Attribute DSL type.
Key Features:
  • Blocking interface for synchronous reads
  • Type conversion: String -> Attribute
  • Null/empty handling with sensible defaults
  • @Order(1) for precedence
2. AttributeComponentConfigStorageAdapter Responsibility: Convert between Attribute and JSON for reactive storage operations.
Key Features:
  • Reactive interface with Mono/Flux
  • Bidirectional type conversion: Attribute <-> JSON
  • Error handling with defaults
  • Transparent delegation
3. ApiKeyStoreAdapter Responsibility: Simple pass-through adapter for API key operations.
Key Features:
  • No type conversion needed (uses domain models)
  • Pure delegation pattern
  • Maintains reactive types

Cloud Provider AWS

Module: cloud-provider-aws (composite module) Purpose: AWS-specific implementations using DynamoDB, Secrets Manager, and CloudWatch.

Sub-module: aws-stores

Purpose: DynamoDB implementations for storage interfaces. Key Classes: ComponentDynamodbStoreBean:
Features:
  • Exception mapping from AWS SDK to cloud-provider-api
  • Retry logic with exponential backoff
  • Optimistic locking support
  • Operation timeouts
SharableDynamodbStoreBean:
  • TTL-based automatic expiration
  • High-performance token retrieval
  • Configurable expiration
ApiKeyLookupStoreBean:
  • Fast component -> API keys lookup
  • Exposed function management
  • Permission tracking

Sub-module: aws-secrets

Purpose: Secrets Manager implementations for configuration and secrets. Key Classes: AwsComponentConfigStorage:
  • Version management with stage labels
  • Multi-region replication
  • Encryption with KMS
AwsApiKeySecretStorage:
  • Secure API key storage
  • Permission management
  • Automatic rotation support
Features:
  • JSON serialization with Jackson
  • Custom serializers for permissions
  • Caching with configurable TTL
  • Audit trail support

Sub-module: aws-metrics

Purpose: CloudWatch metrics publishing. AwsMetricPublisher:
  • DynamoDB operation metrics
  • Secret access metrics
  • Custom dimensions support

Sub-module: aws-spring-boot-starter

Purpose: Spring Boot autoconfiguration for AWS provider. AwsCloudProviderAutoConfiguration:
Features:
  • Conditional activation based on classpath and configuration
  • Component scanning for AWS beans
  • Configuration properties binding
  • Defaults to AWS for backward compatibility

Cloud Provider Local

Module: cloud-provider-local (single module) Purpose: Lightweight, in-memory implementation for local development and testing. Architecture Philosophy:
  • Simplicity over features
  • Zero external dependencies
  • Fast startup and teardown
  • Deterministic behavior for testing
Key Characteristics:
  • Single Module: Unlike AWS (4 submodules), local uses a single module for simplicity
  • In-Memory Storage: All data stored in ConcurrentHashMap instances
  • No Persistence: Data lost on restart (by design)
  • Thread-Safe: Uses concurrent collections and locks where needed
  • Reactive: Returns Mono<T> for API consistency

Storage Implementations

LocalComponentStore:
Features:
  • LATEST pseudo-revision resolution (same as AWS)
  • Component versioning support
  • Optimistic locking for concurrent updates
  • Exception mapping to cloud-provider-api types
LocalSharableStore:
Features:
  • Lazy expiration (checked on access)
  • Scheduled cleanup (optional background task)
  • Configurable cleanup interval
LocalApiKeyStore:
Features:
  • Fine-grained locking with ReadWriteLock
  • Fast component -> API keys lookup
  • Bidirectional sync with ApiKeySecretStorage
LocalApiKeySecretStorage:
Features:
  • Bidirectional sync with ApiKeyStore
  • Permission-based access control
  • Secure storage (in-memory)
LocalComponentConfigStorage:
Features:
  • Configuration versioning
  • LATEST version resolution
  • JSON storage format

Support Classes

LocalStoreSupport (Base Class):
Features:
  • Consistent error handling
  • Exception mapping to cloud-provider-api types
  • Logging support
SharableCleanupScheduler:
Features:
  • Background cleanup task
  • Configurable interval
  • Graceful shutdown

Auto-Configuration

LocalCloudProviderAutoConfiguration:
Features:
  • Conditional activation via hub.cloud.provider.type=local
  • Component scanning for local provider beans
  • Configuration properties binding
  • Startup logging for visibility

Testing Utilities

The local provider exposes testing utilities:

Cloud Provider GCP

Module: cloud-provider-gcp (composite module) Purpose: GCP-specific implementations using Cloud Firestore (Native Mode), Secret Manager, and Cloud Monitoring.

Sub-module: gcp-stores

Purpose: Firestore implementations for storage interfaces. Key Classes: ComponentFirestoreStoreBean:
Features:
  • Exception mapping from Firestore SDK to cloud-provider-api
  • Retry logic with exponential backoff
  • Atomic LATEST pointer updates using transactions
  • Operation timeouts
  • Composite index auto-creation
SharableFirestoreStoreBean:
  • TTL-based automatic expiration (Firestore native TTL)
  • High-performance token retrieval
  • Configurable expiration
  • Native Firestore TTL field support
ApiKeyLookupStoreBean:
  • Fast component -> API keys lookup
  • Bidirectional mapping support
  • Firestore document-based storage

Sub-module: gcp-secrets

Purpose: Secret Manager implementations for configuration and secrets. Key Classes: GcpComponentConfigStorage:
  • Version management with label-based semantic versioning
  • Multi-region replication support
  • Automatic encryption at rest
  • Caffeine caching with configurable TTL
GcpApiKeySecretStorage:
  • Secure API key storage
  • Permission management
  • JSON serialization with Jackson
  • Version limits and automatic cleanup
Features:
  • Label-based semantic versioning (workaround for Secret Manager limitations)
  • Caching with configurable size and TTL
  • Batch operations support
  • Custom serializers for permissions

Sub-module: gcp-metrics

Purpose: Cloud Monitoring metrics publishing. GcpMetricPublisher:
  • Firestore operation metrics
  • Secret access metrics
  • Batch publishing (up to 200 time series per request)
  • Level-based filtering (INFO, ERROR, TRACE)
  • Custom dimensions support

Sub-module: gcp-spring-boot-starter

Purpose: Spring Boot autoconfiguration for GCP provider. GcpCloudProviderAutoConfiguration:
Features:
  • Conditional activation based on classpath and configuration
  • Component scanning for GCP beans
  • Configuration properties binding
  • Application Default Credentials (ADC) support
  • Workload Identity support for GKE

Comparison: AWS vs GCP vs Local

Design Trade-offs

Why single module instead of splitting?
  • Simplicity: Easier to understand and maintain
  • No submodules needed (no external services to abstract)
  • Faster compile times
  • Smaller dependency graph
Why ConcurrentHashMap?
  • Built-in thread safety
  • Good performance for concurrent access
  • No external dependencies
  • Familiar to Java developers
Why no persistence?
  • Keeps implementation simple
  • Forces proper test isolation
  • Encourages proper CI/CD practices
  • Faster test execution
Why scheduled cleanup instead of automatic expiration?
  • JVM has no built-in TTL mechanism
  • Scheduled task is simple and effective
  • Lazy cleanup reduces CPU overhead
  • Configurable for different use cases

Design Patterns

1. Adapter Pattern

Used By: Backend adapters Purpose: Convert between backend’s domain types and cloud provider’s generic types. Example:
Benefits:
  • Clean separation of concerns
  • Type safety in backend
  • Provider independence

2. Strategy Pattern

Used By: Cloud provider selection Purpose: Select cloud provider implementation at runtime. Example:
Benefits:
  • Runtime provider switching
  • Easy testing with different providers
  • Future extensibility

3. Repository Pattern

Used By: Storage interfaces Purpose: Abstract data persistence from business logic. Benefits:
  • Clean data access layer
  • Testability with mocks
  • Provider-agnostic business logic

4. Facade Pattern

Used By: ComponentConfigServiceFacade Purpose: Provide unified interface to multiple config readers. Benefits:
  • Single entry point for configuration
  • Multiple reader support
  • Precedence management

Exception Handling

Exception Flow

Exception Mapping

Retry Logic

Implementation: DynamoDBStoreSupport
Configuration:

Reactive Programming Model

Why Reactive?

  • Non-blocking I/O - Better resource utilization
  • Backpressure - Handle slow consumers
  • Composability - Chain operations easily
  • Error handling - Propagate errors through pipeline

Reactive Types

Mono<T>: Represents 0 or 1 element
Flux<T>: Represents 0 to N elements

Common Operators

Transformation:
Error Handling:
Composition:

Dependency Injection Flow

Bean Registration Order

  1. Cloud Provider Beans (AWS)
    • Registered by AwsStoresAutoConfiguration
    • ComponentDynamodbStoreBean, AwsApiKeySecretStorage, etc.
  2. Adapter Beans (Backend)
    • Registered by component scanning
    • AttributeComponentConfigReaderAdapter, etc.
    • Autowired with cloud provider implementations
  3. Service Beans (Backend)
    • ComponentConfigServiceFacade, ApiKeyServiceBean, etc.
    • Autowired with adapters

Example Injection Chain

Testing Strategy

Unit Tests

Adapter Tests:
  • Mock cloud provider delegates
  • Test type conversions
  • Test error handling
Cloud Provider Tests:
  • Mock AWS SDK clients
  • Test exception mapping
  • Test retry logic

Integration Tests

LocalStack:
  • Full AWS service emulation
  • End-to-end testing
  • No cloud costs
Test Containers:
  • Embedded DynamoDB
  • Isolated test environment

Example Test Structure

Performance Considerations

Connection Pooling

  • AWS SDK manages connection pools automatically
  • Default: 50 connections per client
  • Tune for high-throughput scenarios

Caching

Secrets Manager:
Benefits:
  • Reduced API calls
  • Lower latency
  • Cost savings
Trade-offs:
  • Stale data for up to cacheTtl
  • Memory usage

Batch Operations

  • Use DynamoDB BatchGetItem for multiple reads
  • Use BatchWriteItem for multiple writes
  • Up to 25 items per batch

Security Considerations

Principle of Least Privilege

  • Grant minimum required IAM permissions
  • Use resource-based policies where possible
  • Separate read/write permissions

Encryption

At Rest:
  • DynamoDB: Default encryption with AWS-managed keys
  • Secrets Manager: Automatic encryption with KMS
In Transit:
  • All AWS API calls use HTTPS
  • TLS 1.2+ required

Audit Logging

  • CloudTrail logs all API calls
  • CloudWatch Logs for application logs
  • Structured logging for correlation

Future Extensibility

Adding New Providers

To add a new provider (e.g., Azure):
  1. Create module cloud-provider-azure
  2. Implement interfaces from cloud-provider-api
  3. Map provider exceptions to CloudProviderException
  4. Create Spring Boot autoconfiguration
  5. Add tests

Supporting New Storage Types

To add new storage interfaces:
  1. Define interface in cloud-provider-api
  2. Add domain models to hub-domain
  3. Implement in cloud provider modules
  4. Create adapter in backend (if type conversion needed)
  5. Update autoconfiguration

See Also