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 063c1a2f2d
commit 058ba3196c
15 changed files with 205 additions and 0 deletions

10
Furniture.Service.slnx Normal file
View File

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

View File

@ -0,0 +1,13 @@
# Furniture Service Use-Case Boundaries
## Orchestration Responsibilities
- Application use cases orchestrate domain workflows.
- Use cases depend on DAL-facing ports, not persistence implementations.
- Transport handlers map to use-case contracts and do not own orchestration logic.
## Current Skeleton
- `IGetFurnitureAvailabilityUseCase`: orchestration boundary contract.
- `GetFurnitureAvailabilityUseCase`: orchestration implementation.
- `IFurnitureAvailabilityReadPort`: DAL-facing port.

View File

@ -0,0 +1,12 @@
# Protocol Selection Policy
## Active Protocol Rule
- This service deployment uses one active protocol at a time.
- Internal default protocol is gRPC.
- Multi-protocol exposure is not allowed in a single deployment.
## Boundary Placement
- Protocol adaptation stays at BFF boundaries.
- Service orchestration remains transport-neutral at application contracts.

View File

@ -0,0 +1,23 @@
@startuml
skinparam packageStyle rectangle
package "furniture-service" {
interface IGetFurnitureAvailabilityUseCase
class GetFurnitureAvailabilityUseCase
interface IFurnitureAvailabilityReadPort
class GetFurnitureAvailabilityRequest
class GetFurnitureAvailabilityResponse
GetFurnitureAvailabilityUseCase ..|> IGetFurnitureAvailabilityUseCase
GetFurnitureAvailabilityUseCase --> IFurnitureAvailabilityReadPort
IGetFurnitureAvailabilityUseCase --> GetFurnitureAvailabilityRequest
IGetFurnitureAvailabilityUseCase --> GetFurnitureAvailabilityResponse
}
package "furniture-bff" as FurnitureBff
package "furniture-dal" as FurnitureDal
FurnitureBff --> IGetFurnitureAvailabilityUseCase
GetFurnitureAvailabilityUseCase --> IFurnitureAvailabilityReadPort
IFurnitureAvailabilityReadPort ..> FurnitureDal
@enduml

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.Service.Contracts\Furniture.Service.Contracts.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,16 @@
using Furniture.Service.Contracts.UseCases;
namespace Furniture.Service.Application.Ports;
/// <summary>
/// Defines DAL-facing read port for furniture availability.
/// </summary>
public interface IFurnitureAvailabilityReadPort
{
/// <summary>
/// Retrieves current availability for a furniture aggregate.
/// </summary>
/// <param name="furnitureId">Furniture aggregate identifier.</param>
/// <returns>Availability response contract.</returns>
Task<GetFurnitureAvailabilityResponse> GetAvailabilityAsync(string furnitureId);
}

View File

@ -0,0 +1,17 @@
using Furniture.Service.Application.Ports;
using Furniture.Service.Contracts.UseCases;
namespace Furniture.Service.Application.UseCases;
/// <summary>
/// Default orchestration implementation for furniture availability lookup.
/// </summary>
public sealed class GetFurnitureAvailabilityUseCase(IFurnitureAvailabilityReadPort readPort)
: IGetFurnitureAvailabilityUseCase
{
/// <inheritdoc />
public Task<GetFurnitureAvailabilityResponse> HandleAsync(GetFurnitureAvailabilityRequest request)
{
return readPort.GetAvailabilityAsync(request.FurnitureId);
}
}

View File

@ -0,0 +1,16 @@
using Furniture.Service.Contracts.UseCases;
namespace Furniture.Service.Application.UseCases;
/// <summary>
/// Defines orchestration boundary for furniture availability lookup.
/// </summary>
public interface IGetFurnitureAvailabilityUseCase
{
/// <summary>
/// Handles the availability lookup use case.
/// </summary>
/// <param name="request">Use-case request contract.</param>
/// <returns>Use-case response contract.</returns>
Task<GetFurnitureAvailabilityResponse> HandleAsync(GetFurnitureAvailabilityRequest request);
}

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,7 @@
namespace Furniture.Service.Contracts.UseCases;
/// <summary>
/// Request contract for furniture availability lookup.
/// </summary>
/// <param name="FurnitureId">Furniture aggregate identifier.</param>
public sealed record GetFurnitureAvailabilityRequest(string FurnitureId);

View File

@ -0,0 +1,8 @@
namespace Furniture.Service.Contracts.UseCases;
/// <summary>
/// Response contract for furniture availability lookup.
/// </summary>
/// <param name="FurnitureId">Furniture aggregate identifier.</param>
/// <param name="QuantityAvailable">Available quantity for the requested furniture.</param>
public sealed record GetFurnitureAvailabilityResponse(string FurnitureId, int QuantityAvailable);

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.Service.Application\Furniture.Service.Application.csproj" />
<ProjectReference Include="..\Furniture.Service.Contracts\Furniture.Service.Contracts.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,6 @@
var builder = WebApplication.CreateBuilder(args);
// Stage 3 skeleton: keep a single active internal protocol policy (gRPC-first).
var app = builder.Build();
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.Service.Application\Furniture.Service.Application.csproj" />
<ProjectReference Include="..\..\src\Furniture.Service.Contracts\Furniture.Service.Contracts.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,28 @@
using Furniture.Service.Application.Ports;
using Furniture.Service.Application.UseCases;
using Furniture.Service.Contracts.UseCases;
namespace Furniture.Service.Application.UnitTests;
public class GetFurnitureAvailabilityUseCaseTests
{
[Fact]
public async Task HandleAsync_WhenCalled_DelegatesToReadPort()
{
var port = new FakeFurnitureAvailabilityReadPort();
var useCase = new GetFurnitureAvailabilityUseCase(port);
var response = await useCase.HandleAsync(new GetFurnitureAvailabilityRequest("FUR-001"));
Assert.Equal("FUR-001", response.FurnitureId);
Assert.Equal(10, response.QuantityAvailable);
}
private sealed class FakeFurnitureAvailabilityReadPort : IFurnitureAvailabilityReadPort
{
public Task<GetFurnitureAvailabilityResponse> GetAvailabilityAsync(string furnitureId)
{
return Task.FromResult(new GetFurnitureAvailabilityResponse(furnitureId, 10));
}
}
}