Service Handlers & Transport Adapters
Overview
The service handler architecture in the AF (Application Function) Core is designed to separate business logic from transport details using a composition-based approach. This structure allows service handlers to remain transport-agnostic while utilizing shared helpers to generate consistent responses for different protocols (gRPC, REST, etc.).
Core Components
1. ServiceHandlerHelpers
Location: common/handlers/service_handler_helpers.h
This is the primary utility class for service handlers. It provides static methods to generate standardized responses without requiring handlers to inherit from a base class.
Key Features:
- Standardized Error Handling: creates consistent error responses with codes, messages, and correlation IDs.
- Extension Points: Supports lambda hooks (build_extensions) to inject service-specific fields into generic error payloads.
- Logging Integration: Optional integration with spdlog for debug tracing.
2. Transport Adapters
Location: common/communication/adapters/
Adapters bridge the gap between the internal domain ResponseData and the specific transport format (e.g., gRPC Message, HTTP Response).
-
GrpcResponseAdapter (
grpc_response_adapter.h): Converts internal response structures into theaf::communication::MessagePtrformat used by the gRPC communication layer. It handles:- Mapping HTTP status codes to gRPC status codes.
- Serializing metadata (headers) and payloads.
-
RestResponseAdapter (
rest_response_adapter.h):
Directory Structure
common/
├── handlers/
│ ├── service_handler_helpers.h # Shared helper utilities
│ └── request_context.h # Context context tracking
├── communication/
│ └── adapters/
│ ├── grpc_response_adapter.h # gRPC-specific translation
│ └── rest_response_adapter.h # REST-specific translation
└── response/
├── response_builder.h # Fluent builder for ResponseData
└── response_data.h # Transport-agnostic response model
Usage Examples
Creating an Error Response
Service handlers should use ServiceHandlerHelpers::create_error_response to return errors. This ensures all errors follow the same schema.
#include "handlers/service_handler_helpers.h"
// In your handler method
af::communication::MessagePtr MyHandler::processRequest(const Request& req) {
if (req.isInvalid()) {
return af::common::handlers::ServiceHandlerHelpers::create_error_response(
400, // HTTP Status
"INVALID_REQUEST", // Error Code
"The request ID is missing",// Message
req.correlation_id, // Context
m_logger // Logger
);
}
// ...
}
Extending Response Payloads
You can add custom fields to the error response using the extension lambda:
auto response = ServiceHandlerHelpers::create_error_response(
500, "INTERNAL_ERROR", "Processing failed", corr_id, logger,
[](nlohmann::json& json_payload) {
json_payload["retry_after"] = 30;
json_payload["sub_system"] = "policy_engine";
}
);
Protocol Conversion
The conversion from internal data to the transport format often happens automatically inside the helpers using the adapters.
// Internally, ServiceHandlerHelpers uses:
// auto grpc_msg = adapters::GrpcResponseAdapter::to_grpc_message(response_data);