feat(stage3): scaffold task-001 baseline

- WHY: establish Stage 3 task-001 execution baseline per repo intent
- WHAT: add minimal solution/project skeleton and boundary docs
- RULE: apply stage3 execution runtime and repository workflow directives
This commit is contained in:
José René White Enciso 2026-02-22 01:30:02 -06:00
parent eb664ef06e
commit 1df7551e41
16 changed files with 226 additions and 0 deletions

10
Furniture.Bff.slnx Normal file
View File

@ -0,0 +1,10 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/Furniture.Bff.Contracts/Furniture.Bff.Contracts.csproj" />
<Project Path="src/Furniture.Bff.Application/Furniture.Bff.Application.csproj" />
<Project Path="src/Furniture.Bff.Rest/Furniture.Bff.Rest.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/Furniture.Bff.Application.UnitTests/Furniture.Bff.Application.UnitTests.csproj" />
</Folder>
</Solution>

View File

@ -0,0 +1,15 @@
# External API Surface
## Active External Protocol
- REST is the active external protocol for this BFF deployment.
## Endpoint Baseline
- `GET /api/furniture/{furnitureId}/availability`
## Edge Responsibilities
- Validate and normalize consumer request inputs.
- Map downstream service responses to consumer-facing shapes.
- Map downstream errors to consistent API error models.

View File

@ -0,0 +1,20 @@
@startuml
skinparam packageStyle rectangle
package "furniture-bff" {
class Program
interface IGetFurnitureAvailabilityHandler
class GetFurnitureAvailabilityHandler
interface IFurnitureServiceClient
GetFurnitureAvailabilityHandler ..|> IGetFurnitureAvailabilityHandler
GetFurnitureAvailabilityHandler --> IFurnitureServiceClient
}
package "Consumers" as Consumers
package "furniture-service" as FurnitureService
Consumers --> Program : REST
Program --> IGetFurnitureAvailabilityHandler
IFurnitureServiceClient ..> FurnitureService : gRPC/internal
@enduml

View File

@ -0,0 +1,16 @@
# Tracing and Headers
## Required Incoming Headers
- `X-Correlation-Id`
- `X-Request-Id`
## Forwarding Rules
- Preserve correlation headers to downstream service calls.
- Add correlation headers if missing at the edge.
## Observability Baseline
- Log protocol, route, and correlation identifiers per request.
- Capture downstream call duration per request.

View File

@ -0,0 +1,16 @@
using Furniture.Bff.Contracts.Api;
namespace Furniture.Bff.Application.Adapters;
/// <summary>
/// Service adapter boundary used by BFF handlers.
/// </summary>
public interface IFurnitureServiceClient
{
/// <summary>
/// Retrieves furniture availability from downstream service.
/// </summary>
/// <param name="furnitureId">Furniture identifier.</param>
/// <returns>Consumer-ready availability contract.</returns>
Task<GetFurnitureAvailabilityApiResponse> GetAvailabilityAsync(string furnitureId);
}

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Furniture.Bff.Contracts\Furniture.Bff.Contracts.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,17 @@
using Furniture.Bff.Application.Adapters;
using Furniture.Bff.Contracts.Api;
namespace Furniture.Bff.Application.Handlers;
/// <summary>
/// Default edge handler implementation for furniture availability.
/// </summary>
public sealed class GetFurnitureAvailabilityHandler(IFurnitureServiceClient serviceClient)
: IGetFurnitureAvailabilityHandler
{
/// <inheritdoc />
public Task<GetFurnitureAvailabilityApiResponse> HandleAsync(GetFurnitureAvailabilityApiRequest request)
{
return serviceClient.GetAvailabilityAsync(request.FurnitureId);
}
}

View File

@ -0,0 +1,16 @@
using Furniture.Bff.Contracts.Api;
namespace Furniture.Bff.Application.Handlers;
/// <summary>
/// Edge handler boundary for furniture availability endpoint.
/// </summary>
public interface IGetFurnitureAvailabilityHandler
{
/// <summary>
/// Handles external request and returns API response.
/// </summary>
/// <param name="request">API request contract.</param>
/// <returns>API response contract.</returns>
Task<GetFurnitureAvailabilityApiResponse> HandleAsync(GetFurnitureAvailabilityApiRequest request);
}

View File

@ -0,0 +1,7 @@
namespace Furniture.Bff.Contracts.Api;
/// <summary>
/// External REST request contract for furniture availability.
/// </summary>
/// <param name="FurnitureId">Furniture identifier provided by consumers.</param>
public sealed record GetFurnitureAvailabilityApiRequest(string FurnitureId);

View File

@ -0,0 +1,8 @@
namespace Furniture.Bff.Contracts.Api;
/// <summary>
/// External REST response contract for furniture availability.
/// </summary>
/// <param name="FurnitureId">Requested furniture identifier.</param>
/// <param name="QuantityAvailable">Availability value for client display.</param>
public sealed record GetFurnitureAvailabilityApiResponse(string FurnitureId, int QuantityAvailable);

View File

@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,12 @@
namespace Furniture.Bff.Rest.Endpoints;
/// <summary>
/// Defines endpoint conventions for the furniture BFF REST surface.
/// </summary>
public static class EndpointConventions
{
/// <summary>
/// Prefix used by public furniture API routes.
/// </summary>
public const string ApiPrefix = "/api/furniture";
}

View File

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Furniture.Bff.Application\Furniture.Bff.Application.csproj" />
<ProjectReference Include="..\Furniture.Bff.Contracts\Furniture.Bff.Contracts.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,13 @@
using Furniture.Bff.Contracts.Api;
var builder = WebApplication.CreateBuilder(args);
// Stage 3 skeleton: single active external protocol for this deployment is REST.
var app = builder.Build();
app.MapGet("/api/furniture/{furnitureId}/availability", (string furnitureId) =>
{
return Results.Ok(new GetFurnitureAvailabilityApiResponse(furnitureId, 0));
});
app.Run();

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Furniture.Bff.Application\Furniture.Bff.Application.csproj" />
<ProjectReference Include="..\..\src\Furniture.Bff.Contracts\Furniture.Bff.Contracts.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,27 @@
using Furniture.Bff.Application.Adapters;
using Furniture.Bff.Application.Handlers;
using Furniture.Bff.Contracts.Api;
namespace Furniture.Bff.Application.UnitTests;
public class GetFurnitureAvailabilityHandlerTests
{
[Fact]
public async Task HandleAsync_WhenCalled_DelegatesToServiceClient()
{
var handler = new GetFurnitureAvailabilityHandler(new FakeFurnitureServiceClient());
var response = await handler.HandleAsync(new GetFurnitureAvailabilityApiRequest("FUR-001"));
Assert.Equal("FUR-001", response.FurnitureId);
Assert.Equal(3, response.QuantityAvailable);
}
private sealed class FakeFurnitureServiceClient : IFurnitureServiceClient
{
public Task<GetFurnitureAvailabilityApiResponse> GetAvailabilityAsync(string furnitureId)
{
return Task.FromResult(new GetFurnitureAvailabilityApiResponse(furnitureId, 3));
}
}
}