Initial implementation of HTTP Sender Plugin following TDD methodology with hexagonal architecture. All 313 tests passing (0 failures). This commit adds: - Complete domain model and port interfaces - All adapter implementations (HTTP, gRPC, file logging, config) - Application services (data collection, transmission, backpressure) - Comprehensive test suite with 18 integration tests Test fixes applied during implementation: - Fix base64 encoding validation in DataCollectionServiceIntegrationTest - Fix exception type handling in IConfigurationPortTest - Fix CompletionException unwrapping in IHttpPollingPortTest - Fix sequential batching in DataTransmissionServiceIntegrationTest - Add test adapter failure simulation for reconnection tests - Use adapter counters for gRPC verification Files added: - pom.xml with all dependencies (JUnit 5, Mockito, WireMock, gRPC, Jackson) - src/main/java: Domain model, ports, adapters, application services - src/test/java: Unit tests, integration tests, test utilities
208 lines
8.0 KiB
Markdown
208 lines
8.0 KiB
Markdown
# Architecture Alignment Report - Configuration
|
|
|
|
**Date**: 2025-11-20
|
|
**Issue**: Adapter code doesn't match actual Configuration domain model API
|
|
**Root Cause**: Parallel AI agents generated code with mismatched APIs
|
|
|
|
---
|
|
|
|
## ✅ Correct Architecture (From Domain Model)
|
|
|
|
### Configuration Class Structure
|
|
|
|
**System-Wide Settings** (Configuration.java):
|
|
```java
|
|
public final class Configuration {
|
|
private final List<EndpointConfig> endpoints; // Per-endpoint configs
|
|
private final Duration pollingInterval; // System-wide
|
|
private final int bufferCapacity; // System-wide (300)
|
|
private final String grpcHost; // gRPC target
|
|
private final int grpcPort; // gRPC port
|
|
private final boolean tlsEnabled; // TLS flag
|
|
private final Duration reconnectDelay; // gRPC reconnect (5s)
|
|
private final int healthCheckPort; // Health endpoint (8080)
|
|
}
|
|
```
|
|
|
|
**Per-Endpoint Settings** (EndpointConfig.java):
|
|
```java
|
|
public final class EndpointConfig {
|
|
private final String url; // Endpoint URL
|
|
private final Duration timeout; // Per-endpoint timeout!
|
|
private final Map<String, String> headers; // Per-endpoint headers!
|
|
}
|
|
```
|
|
|
|
### Builder API (Correct Usage)
|
|
|
|
```java
|
|
// ✅ CORRECT: Use static factory method
|
|
Configuration config = Configuration.builder()
|
|
.endpoints(endpointList) // List<EndpointConfig>
|
|
.pollingInterval(Duration.ofSeconds(5))
|
|
.bufferCapacity(300)
|
|
.grpcHost("localhost")
|
|
.grpcPort(50051)
|
|
.tlsEnabled(false)
|
|
.reconnectDelay(Duration.ofSeconds(5))
|
|
.healthCheckPort(8080)
|
|
.build();
|
|
|
|
// ❌ WRONG: Direct constructor access (private!)
|
|
Configuration.Builder builder = new Configuration.Builder(); // Won't compile!
|
|
```
|
|
|
|
---
|
|
|
|
## ❌ What Adapters Are Doing Wrong
|
|
|
|
### ConfigurationFileAdapter Issues
|
|
|
|
**Wrong API Calls**:
|
|
```java
|
|
// ❌ WRONG
|
|
new Configuration.Builder() // Constructor is private!
|
|
builder.grpcServerAddress(String) // Method doesn't exist
|
|
builder.grpcServerPort(int) // Method doesn't exist
|
|
builder.httpEndpoints(List<String>) // Wrong type - needs List<EndpointConfig>
|
|
builder.pollingIntervalSeconds(int) // Wrong type - needs Duration
|
|
builder.bufferSize(int) // Wrong name - should be bufferCapacity
|
|
builder.httpTimeoutSeconds(int) // Doesn't exist - timeout is per-endpoint!
|
|
builder.maxRetries(int) // Doesn't exist - adapter implementation detail
|
|
builder.retryIntervalSeconds(int) // Doesn't exist - adapter implementation detail
|
|
|
|
config.getBufferSize() // Wrong name - should be getBufferCapacity()
|
|
config.getGrpcServerPort() // Wrong name - should be getGrpcPort()
|
|
config.getHttpEndpoints() // Wrong name - should be getEndpoints()
|
|
```
|
|
|
|
**Correct API Calls**:
|
|
```java
|
|
// ✅ CORRECT
|
|
Configuration.builder() // Static factory method
|
|
.grpcHost(String) // Correct method name
|
|
.grpcPort(int) // Correct method name
|
|
.endpoints(List<EndpointConfig>) // Correct type
|
|
.pollingInterval(Duration) // Correct type
|
|
.bufferCapacity(int) // Correct name
|
|
// No timeout here - it's per-endpoint in EndpointConfig!
|
|
// No retry config - it's adapter implementation detail!
|
|
.build()
|
|
|
|
config.getBufferCapacity() // Correct name
|
|
config.getGrpcPort() // Correct name
|
|
config.getEndpoints() // Correct name
|
|
```
|
|
|
|
### HttpPollingAdapter Issues
|
|
|
|
**Wrong Assumptions**:
|
|
```java
|
|
// ❌ WRONG: These methods don't exist in Configuration
|
|
config.getHttpTimeoutSeconds() // Timeout is per-endpoint!
|
|
config.getMaxRetries() // Retry is adapter detail!
|
|
config.getRetryIntervalSeconds() // Retry is adapter detail!
|
|
```
|
|
|
|
**Correct Approach**:
|
|
```java
|
|
// ✅ CORRECT: Get timeout from EndpointConfig
|
|
for (EndpointConfig endpoint : config.getEndpoints()) {
|
|
Duration timeout = endpoint.getTimeout(); // Per-endpoint timeout
|
|
Map<String,String> headers = endpoint.getHeaders(); // Per-endpoint headers
|
|
|
|
// Retry logic is hardcoded in adapter (Req-FR-17)
|
|
int maxRetries = 3; // Hardcoded per requirements
|
|
int retryIntervalSeconds = 5; // Hardcoded per requirements
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 Requirements Traceability
|
|
|
|
### Req-FR-17: HTTP Retry Logic
|
|
- **Requirement**: "If the HTTP GET request fails HSP shall retry up to 3 times with 5-second intervals"
|
|
- **Configuration**: `http.max_retries = 3, http.retry_interval_seconds = 5`
|
|
- **Correct Implementation**: Hardcoded in HttpPollingAdapter (adapter implementation detail)
|
|
- **NOT** in domain model Configuration class
|
|
|
|
### Req-FR-12: HTTP Timeout
|
|
- **Requirement**: "Configurable timeout per endpoint"
|
|
- **Correct Location**: EndpointConfig.timeout (Duration)
|
|
- **NOT** in Configuration class (it's per-endpoint, not system-wide!)
|
|
|
|
### Req-FR-13: HTTP Headers
|
|
- **Requirement**: "Custom headers per endpoint"
|
|
- **Correct Location**: EndpointConfig.headers (Map<String,String>)
|
|
- **NOT** in Configuration class (it's per-endpoint!)
|
|
|
|
---
|
|
|
|
## 🔧 Required Fixes
|
|
|
|
### 1. ConfigurationFileAdapter (16 errors)
|
|
|
|
**Fix Strategy**:
|
|
1. Change `new Configuration.Builder()` → `Configuration.builder()`
|
|
2. Parse JSON to create `List<EndpointConfig>` from endpoint strings
|
|
3. Use correct method names:
|
|
- `grpcHost()` instead of `grpcServerAddress()`
|
|
- `grpcPort()` instead of `grpcServerPort()`
|
|
- `endpoints()` instead of `httpEndpoints()`
|
|
- `pollingInterval(Duration)` instead of `pollingIntervalSeconds(int)`
|
|
- `bufferCapacity()` instead of `bufferSize()`
|
|
4. Remove non-existent methods:
|
|
- `httpTimeoutSeconds()` - use EndpointConfig
|
|
- `maxRetries()` - hardcode in adapter
|
|
- `retryIntervalSeconds()` - hardcode in adapter
|
|
5. Use correct getter names:
|
|
- `getBufferCapacity()` instead of `getBufferSize()`
|
|
- `getGrpcPort()` instead of `getGrpcServerPort()`
|
|
- `getEndpoints()` instead of `getHttpEndpoints()`
|
|
|
|
### 2. HttpPollingAdapter (10 errors)
|
|
|
|
**Fix Strategy**:
|
|
1. Remove calls to non-existent Configuration methods
|
|
2. Hardcode retry logic (Req-FR-17):
|
|
```java
|
|
private static final int MAX_RETRIES = 3;
|
|
private static final int RETRY_INTERVAL_SECONDS = 5;
|
|
```
|
|
3. Get timeout and headers from EndpointConfig:
|
|
```java
|
|
Duration timeout = endpointConfig.getTimeout();
|
|
Map<String,String> headers = endpointConfig.getHeaders();
|
|
```
|
|
4. Implement pollEndpoint(String url, Map<String,String> headers, Duration timeout)
|
|
|
|
### 3. RateLimitedHttpPollingAdapter (3 errors)
|
|
|
|
**Fix Strategy**:
|
|
1. Implement pollEndpoint(String url, Map<String,String> headers, Duration timeout)
|
|
2. Same as HttpPollingAdapter
|
|
|
|
---
|
|
|
|
## 📝 Summary
|
|
|
|
**The Problem**:
|
|
- Adapters were generated assuming Configuration contains per-endpoint settings (timeout, headers, retries)
|
|
- Actual architecture: Configuration contains system-wide settings, EndpointConfig contains per-endpoint settings
|
|
- Retry logic should be hardcoded in adapter (Req-FR-17), not in configuration
|
|
|
|
**The Solution**:
|
|
1. ConfigurationFileAdapter: Use correct Configuration API with Builder pattern
|
|
2. HttpPollingAdapter: Hardcode retry logic, get timeout/headers from EndpointConfig
|
|
3. Follow hexagonal architecture: domain model is simple, adapters handle complexity
|
|
|
|
**Architecture Principle**:
|
|
- **Domain Model** (Configuration, EndpointConfig): Simple, immutable value objects
|
|
- **Adapters** (HttpPollingAdapter): Handle implementation details (retry logic, connection management)
|
|
- **Separation of Concerns**: System-wide config vs per-endpoint config
|
|
|
|
---
|
|
|
|
**Next Step**: Apply these fixes to achieve compilation.
|