diff --git a/docs/api/external-api-surface.md b/docs/api/external-api-surface.md
index b0b5433..7dd8ed2 100644
--- a/docs/api/external-api-surface.md
+++ b/docs/api/external-api-surface.md
@@ -11,5 +11,6 @@
## Edge Responsibilities
- Validate and normalize consumer request inputs.
-- Map downstream service responses to consumer-facing shapes.
+- Map edge requests to furniture-service transport-neutral contracts.
+- Map downstream furniture-service responses to consumer-facing shapes.
- Map downstream errors to consistent API error models.
diff --git a/docs/architecture/protocol-adaptation.puml b/docs/architecture/protocol-adaptation.puml
index 7d2790b..a962ebf 100644
--- a/docs/architecture/protocol-adaptation.puml
+++ b/docs/architecture/protocol-adaptation.puml
@@ -5,9 +5,14 @@ package "furniture-bff" {
class Program
interface IGetFurnitureAvailabilityHandler
class GetFurnitureAvailabilityHandler
+ interface IFurnitureAvailabilityEdgeContractAdapter
+ interface IFurnitureAvailabilityEdgeGrpcContractAdapter
+ class GetFurnitureAvailabilityEdgeGrpcContract
interface IFurnitureServiceClient
GetFurnitureAvailabilityHandler ..|> IGetFurnitureAvailabilityHandler
+ GetFurnitureAvailabilityHandler --> IFurnitureAvailabilityEdgeContractAdapter
+ IFurnitureAvailabilityEdgeGrpcContractAdapter --> GetFurnitureAvailabilityEdgeGrpcContract
GetFurnitureAvailabilityHandler --> IFurnitureServiceClient
}
@@ -16,5 +21,5 @@ package "furniture-service" as FurnitureService
Consumers --> Program : REST
Program --> IGetFurnitureAvailabilityHandler
-IFurnitureServiceClient ..> FurnitureService : gRPC/internal
+IFurnitureServiceClient ..> FurnitureService : service contracts
@enduml
diff --git a/docs/operations/tracing-and-headers.md b/docs/operations/tracing-and-headers.md
index b159fa0..1acf164 100644
--- a/docs/operations/tracing-and-headers.md
+++ b/docs/operations/tracing-and-headers.md
@@ -9,6 +9,7 @@
- Preserve correlation headers to downstream service calls.
- Add correlation headers if missing at the edge.
+- Populate furniture-service request contracts with correlation identifiers.
## Observability Baseline
diff --git a/src/Furniture.Bff.Application/Adapters/IFurnitureAvailabilityEdgeContractAdapter.cs b/src/Furniture.Bff.Application/Adapters/IFurnitureAvailabilityEdgeContractAdapter.cs
new file mode 100644
index 0000000..1eb23dd
--- /dev/null
+++ b/src/Furniture.Bff.Application/Adapters/IFurnitureAvailabilityEdgeContractAdapter.cs
@@ -0,0 +1,24 @@
+using Furniture.Bff.Contracts.Api;
+using Furniture.Service.Contracts.UseCases;
+
+namespace Furniture.Bff.Application.Adapters;
+
+///
+/// Defines adapter boundary between furniture BFF edge contracts and furniture service contracts.
+///
+public interface IFurnitureAvailabilityEdgeContractAdapter
+{
+ ///
+ /// Maps edge API request into furniture service request contract.
+ ///
+ /// Edge API request.
+ /// Furniture service request contract.
+ GetFurnitureAvailabilityRequest ToServiceRequest(GetFurnitureAvailabilityApiRequest request);
+
+ ///
+ /// Maps furniture service response contract into edge API response.
+ ///
+ /// Furniture service response contract.
+ /// Edge API response.
+ GetFurnitureAvailabilityApiResponse ToApiResponse(GetFurnitureAvailabilityResponse response);
+}
diff --git a/src/Furniture.Bff.Application/Adapters/IFurnitureAvailabilityEdgeGrpcContractAdapter.cs b/src/Furniture.Bff.Application/Adapters/IFurnitureAvailabilityEdgeGrpcContractAdapter.cs
new file mode 100644
index 0000000..bede2c8
--- /dev/null
+++ b/src/Furniture.Bff.Application/Adapters/IFurnitureAvailabilityEdgeGrpcContractAdapter.cs
@@ -0,0 +1,24 @@
+using Furniture.Bff.Application.Grpc;
+using Furniture.Bff.Contracts.Api;
+
+namespace Furniture.Bff.Application.Adapters;
+
+///
+/// Defines adapter boundary for gRPC translation at the furniture edge.
+///
+public interface IFurnitureAvailabilityEdgeGrpcContractAdapter
+{
+ ///
+ /// Maps edge API request into gRPC contract shape.
+ ///
+ /// Edge API request.
+ /// gRPC request contract shape.
+ GetFurnitureAvailabilityEdgeGrpcContract ToGrpc(GetFurnitureAvailabilityApiRequest request);
+
+ ///
+ /// Maps gRPC request contract shape back into edge API request.
+ ///
+ /// gRPC request contract shape.
+ /// Edge API request.
+ GetFurnitureAvailabilityApiRequest FromGrpc(GetFurnitureAvailabilityEdgeGrpcContract contract);
+}
diff --git a/src/Furniture.Bff.Application/Adapters/IFurnitureServiceClient.cs b/src/Furniture.Bff.Application/Adapters/IFurnitureServiceClient.cs
index 1e118a1..d9ba0e4 100644
--- a/src/Furniture.Bff.Application/Adapters/IFurnitureServiceClient.cs
+++ b/src/Furniture.Bff.Application/Adapters/IFurnitureServiceClient.cs
@@ -1,4 +1,4 @@
-using Furniture.Bff.Contracts.Api;
+using Furniture.Service.Contracts.UseCases;
namespace Furniture.Bff.Application.Adapters;
@@ -10,7 +10,7 @@ public interface IFurnitureServiceClient
///
/// Retrieves furniture availability from downstream service.
///
- /// Furniture identifier.
- /// Consumer-ready availability contract.
- Task GetAvailabilityAsync(string furnitureId);
+ /// Service-layer availability request contract.
+ /// Service-layer availability response contract.
+ Task GetAvailabilityAsync(GetFurnitureAvailabilityRequest request);
}
diff --git a/src/Furniture.Bff.Application/Furniture.Bff.Application.csproj b/src/Furniture.Bff.Application/Furniture.Bff.Application.csproj
index c75bc4d..41b768c 100644
--- a/src/Furniture.Bff.Application/Furniture.Bff.Application.csproj
+++ b/src/Furniture.Bff.Application/Furniture.Bff.Application.csproj
@@ -6,5 +6,6 @@
+
diff --git a/src/Furniture.Bff.Application/Grpc/GetFurnitureAvailabilityEdgeGrpcContract.cs b/src/Furniture.Bff.Application/Grpc/GetFurnitureAvailabilityEdgeGrpcContract.cs
new file mode 100644
index 0000000..4d95b1a
--- /dev/null
+++ b/src/Furniture.Bff.Application/Grpc/GetFurnitureAvailabilityEdgeGrpcContract.cs
@@ -0,0 +1,8 @@
+namespace Furniture.Bff.Application.Grpc;
+
+///
+/// Defines minimal gRPC contract shape for furniture availability edge translation.
+///
+/// Furniture identifier provided by consumers.
+/// Request correlation identifier.
+public sealed record GetFurnitureAvailabilityEdgeGrpcContract(string FurnitureId, string CorrelationId);
diff --git a/src/Furniture.Bff.Application/Handlers/GetFurnitureAvailabilityHandler.cs b/src/Furniture.Bff.Application/Handlers/GetFurnitureAvailabilityHandler.cs
index 7781661..c0da525 100644
--- a/src/Furniture.Bff.Application/Handlers/GetFurnitureAvailabilityHandler.cs
+++ b/src/Furniture.Bff.Application/Handlers/GetFurnitureAvailabilityHandler.cs
@@ -6,12 +6,16 @@ namespace Furniture.Bff.Application.Handlers;
///
/// Default edge handler implementation for furniture availability.
///
-public sealed class GetFurnitureAvailabilityHandler(IFurnitureServiceClient serviceClient)
+public sealed class GetFurnitureAvailabilityHandler(
+ IFurnitureServiceClient serviceClient,
+ IFurnitureAvailabilityEdgeContractAdapter contractAdapter)
: IGetFurnitureAvailabilityHandler
{
///
- public Task HandleAsync(GetFurnitureAvailabilityApiRequest request)
+ public async Task HandleAsync(GetFurnitureAvailabilityApiRequest request)
{
- return serviceClient.GetAvailabilityAsync(request.FurnitureId);
+ var serviceRequest = contractAdapter.ToServiceRequest(request);
+ var serviceResponse = await serviceClient.GetAvailabilityAsync(serviceRequest);
+ return contractAdapter.ToApiResponse(serviceResponse);
}
}
diff --git a/src/Furniture.Bff.Contracts/Api/GetFurnitureAvailabilityApiRequest.cs b/src/Furniture.Bff.Contracts/Api/GetFurnitureAvailabilityApiRequest.cs
index 42b9d38..2de74e4 100644
--- a/src/Furniture.Bff.Contracts/Api/GetFurnitureAvailabilityApiRequest.cs
+++ b/src/Furniture.Bff.Contracts/Api/GetFurnitureAvailabilityApiRequest.cs
@@ -4,4 +4,5 @@ namespace Furniture.Bff.Contracts.Api;
/// External REST request contract for furniture availability.
///
/// Furniture identifier provided by consumers.
-public sealed record GetFurnitureAvailabilityApiRequest(string FurnitureId);
+/// Cross-service correlation identifier.
+public sealed record GetFurnitureAvailabilityApiRequest(string FurnitureId, string CorrelationId = "");
diff --git a/src/Furniture.Bff.Contracts/Api/GetFurnitureAvailabilityApiResponse.cs b/src/Furniture.Bff.Contracts/Api/GetFurnitureAvailabilityApiResponse.cs
index c6068c8..2df63b8 100644
--- a/src/Furniture.Bff.Contracts/Api/GetFurnitureAvailabilityApiResponse.cs
+++ b/src/Furniture.Bff.Contracts/Api/GetFurnitureAvailabilityApiResponse.cs
@@ -4,5 +4,6 @@ namespace Furniture.Bff.Contracts.Api;
/// External REST response contract for furniture availability.
///
/// Requested furniture identifier.
+/// Display name to present to consumer clients.
/// Availability value for client display.
-public sealed record GetFurnitureAvailabilityApiResponse(string FurnitureId, int QuantityAvailable);
+public sealed record GetFurnitureAvailabilityApiResponse(string FurnitureId, string DisplayName, int QuantityAvailable);
diff --git a/tests/Furniture.Bff.Application.UnitTests/ContractShapeTests.cs b/tests/Furniture.Bff.Application.UnitTests/ContractShapeTests.cs
new file mode 100644
index 0000000..23703b8
--- /dev/null
+++ b/tests/Furniture.Bff.Application.UnitTests/ContractShapeTests.cs
@@ -0,0 +1,25 @@
+using Furniture.Bff.Contracts.Api;
+
+namespace Furniture.Bff.Application.UnitTests;
+
+public class ContractShapeTests
+{
+ [Fact]
+ public void GetFurnitureAvailabilityApiRequest_WhenCreated_StoresCorrelationId()
+ {
+ var request = new GetFurnitureAvailabilityApiRequest("FUR-001", "corr-123");
+
+ Assert.Equal("FUR-001", request.FurnitureId);
+ Assert.Equal("corr-123", request.CorrelationId);
+ }
+
+ [Fact]
+ public void GetFurnitureAvailabilityApiResponse_WhenCreated_StoresDisplayName()
+ {
+ var response = new GetFurnitureAvailabilityApiResponse("FUR-001", "Chair", 8);
+
+ Assert.Equal("FUR-001", response.FurnitureId);
+ Assert.Equal("Chair", response.DisplayName);
+ Assert.Equal(8, response.QuantityAvailable);
+ }
+}
diff --git a/tests/Furniture.Bff.Application.UnitTests/GetFurnitureAvailabilityHandlerTests.cs b/tests/Furniture.Bff.Application.UnitTests/GetFurnitureAvailabilityHandlerTests.cs
index 8eabf74..93daf0a 100644
--- a/tests/Furniture.Bff.Application.UnitTests/GetFurnitureAvailabilityHandlerTests.cs
+++ b/tests/Furniture.Bff.Application.UnitTests/GetFurnitureAvailabilityHandlerTests.cs
@@ -1,6 +1,7 @@
using Furniture.Bff.Application.Adapters;
using Furniture.Bff.Application.Handlers;
using Furniture.Bff.Contracts.Api;
+using Furniture.Service.Contracts.UseCases;
namespace Furniture.Bff.Application.UnitTests;
@@ -9,19 +10,38 @@ public class GetFurnitureAvailabilityHandlerTests
[Fact]
public async Task HandleAsync_WhenCalled_DelegatesToServiceClient()
{
- var handler = new GetFurnitureAvailabilityHandler(new FakeFurnitureServiceClient());
+ var handler = new GetFurnitureAvailabilityHandler(
+ new FakeFurnitureServiceClient(),
+ new FakeFurnitureAvailabilityEdgeContractAdapter());
- var response = await handler.HandleAsync(new GetFurnitureAvailabilityApiRequest("FUR-001"));
+ var response = await handler.HandleAsync(new GetFurnitureAvailabilityApiRequest("FUR-001", "corr-123"));
Assert.Equal("FUR-001", response.FurnitureId);
+ Assert.Equal("Chair", response.DisplayName);
Assert.Equal(3, response.QuantityAvailable);
}
private sealed class FakeFurnitureServiceClient : IFurnitureServiceClient
{
- public Task GetAvailabilityAsync(string furnitureId)
+ public Task GetAvailabilityAsync(GetFurnitureAvailabilityRequest request)
{
- return Task.FromResult(new GetFurnitureAvailabilityApiResponse(furnitureId, 3));
+ return Task.FromResult(new GetFurnitureAvailabilityResponse(request.FurnitureId, "Chair", 3));
+ }
+ }
+
+ private sealed class FakeFurnitureAvailabilityEdgeContractAdapter : IFurnitureAvailabilityEdgeContractAdapter
+ {
+ public GetFurnitureAvailabilityRequest ToServiceRequest(GetFurnitureAvailabilityApiRequest request)
+ {
+ return new GetFurnitureAvailabilityRequest(request.FurnitureId, request.CorrelationId);
+ }
+
+ public GetFurnitureAvailabilityApiResponse ToApiResponse(GetFurnitureAvailabilityResponse response)
+ {
+ return new GetFurnitureAvailabilityApiResponse(
+ response.FurnitureId,
+ response.DisplayName,
+ response.QuantityAvailable);
}
}
}