Serverless architecture with AWS Lambda is an event-driven approach in which functions are executed in
response to events from AWS services. These functions are Plain Old Java Objects (POJOs).
that focus purely on business logic. When there are no incoming events, no functions are active.
This results in zero compute costs.
The BCE pattern naturally fits serverless: Lambda handlers act as Boundaries receiving events, Controls
process business logic
as stateless operations, and Entities represent domain-specific event data extracted from AWS service
payloads.
Business Components
Each Lambda function represents a cohesive business component organized in a BCE structure. Components
are named after
their domain responsibilities (e.g., order processing, payment handling, notification delivery).
The package structure follows:
airhacks.[app-name].[component-name].[boundary|control|entity], ensuring
clear separation of concerns and maintainable event-driven microservices.
Boundary
Lambda handler classes serving as the entry point for AWS events (EventBridge, SNS, SQS, etc.)
Purpose: Receive and transform AWS events into domain objects
Package: [component].boundary
Implementation: Public class with event handler method (e.g.,
onEvent(Object awsEvent))
Naming: Descriptive listeners like OrderEventListener,
PaymentEventListener
Responsibility: Event reception, extraction, logging, and delegation to control
Architecture Role: Lambda runtime entry point - the only externally exposed class
Quantity: One to a few lambdas per business component
Complexity Rule: No business logic, only AWS event to domain transformation
Error Handling: Let exceptions propagate for Lambda retry mechanisms
Data Flow: AWS Event → extract() → Domain Entity → Control processing
Control
Business logic processors implementing event handling workflows
Purpose: Process domain events with business logic
Package: [component].control
Implementation: Interfaces with static methods for stateless processing
Structure: Named after processing action (e.g., EventProcessor,
OrderValidator)
Responsibility: Validation, transformation, orchestration of downstream services
Architecture Role: Pure business logic without infrastructure concerns
Access Pattern: Static method invocation from boundary
Refactoring Origin: Extract complex logic from boundaries for reusability
Stateless Design: No instance state - all data passed as parameters
Reusability: Can be invoked from multiple boundaries or controls
Business Focus: Domain-specific processing rules and workflows
Testability: Simple unit tests with no AWS dependencies
Complexity Reduction: Break complex workflows into smaller static methods
Coordination Role: Orchestrate multiple operations in correct sequence
Entity
Domain-specific event representations extracted from AWS events
Purpose: Represent business events in domain terms
Package: [component].entity
Implementation: Java records for immutable event data
Structure: Named after domain concepts (e.g., OrderEvent,
PaymentEvent)
Responsibility: Hold event payload in type-safe, domain-specific structure
Architecture Role: Data transfer between boundary and control
Business Focus: Model events using business vocabulary, not AWS terms
Persistence Options: Typically ephemeral - persisted via DynamoDB/S3 if needed
Access Pattern: Created in boundary, processed in control
Domain Modeling: Essential fields only - avoid generic maps or JSON
Self-Contained: Include all data needed for processing
Optional Nature: Simple events may pass strings directly to control
Domain Representation: Transform AWS event structure to business concepts
Implementation Characteristics
Serverless Design Principles
No AWS Dependencies: Lambda code uses POJOs without AWS SDK dependencies
Event-Driven: Functions triggered by EventBridge, SNS, SQS, or other AWS events
Stateless Processing: Each invocation independent - state in DynamoDB/S3
Configuration via Environment: Use System.getenv() for Lambda
configuration
Observability: Structured logging with AWS X-Ray tracing enabled
Memory as Performance: Configure RAM (1700MB = 1 vCPU) for CPU allocation
Cold Start Optimization: Minimize dependencies, use ARM64 architecture
Deployment with CDK
Infrastructure as Code: CDK defines Lambda configuration and triggers
Builder Pattern: Fluent API for Lambda stack configuration
Java 21 Runtime: Latest LTS with virtual threads support
ARM64 Architecture: Better price-performance than x86
Tracing Active: X-Ray enabled for distributed tracing
Testing Strategy
Unit Tests: Test control logic without AWS dependencies
Integration Tests: Use AWS SDK to invoke deployed Lambdas
Local Testing: Direct method invocation of handler classes
Documentation
Only intentions, motivations and additional context are documented.
Every component ships with package-info.java with javadoc describing the essential
responsibility of the component