diff --git a/Furniture.Service.slnx b/Furniture.Service.slnx new file mode 100644 index 0000000..ff985fd --- /dev/null +++ b/Furniture.Service.slnx @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/docs/application/use-case-boundaries.md b/docs/application/use-case-boundaries.md new file mode 100644 index 0000000..4d4828e --- /dev/null +++ b/docs/application/use-case-boundaries.md @@ -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. diff --git a/docs/architecture/protocol-selection-policy.md b/docs/architecture/protocol-selection-policy.md new file mode 100644 index 0000000..434eb85 --- /dev/null +++ b/docs/architecture/protocol-selection-policy.md @@ -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. diff --git a/docs/architecture/service-contracts.puml b/docs/architecture/service-contracts.puml new file mode 100644 index 0000000..ec950da --- /dev/null +++ b/docs/architecture/service-contracts.puml @@ -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 diff --git a/src/Furniture.Service.Application/Furniture.Service.Application.csproj b/src/Furniture.Service.Application/Furniture.Service.Application.csproj new file mode 100644 index 0000000..9dbacfc --- /dev/null +++ b/src/Furniture.Service.Application/Furniture.Service.Application.csproj @@ -0,0 +1,10 @@ + + + net10.0 + enable + enable + + + + + diff --git a/src/Furniture.Service.Application/Ports/IFurnitureAvailabilityReadPort.cs b/src/Furniture.Service.Application/Ports/IFurnitureAvailabilityReadPort.cs new file mode 100644 index 0000000..56c321d --- /dev/null +++ b/src/Furniture.Service.Application/Ports/IFurnitureAvailabilityReadPort.cs @@ -0,0 +1,16 @@ +using Furniture.Service.Contracts.UseCases; + +namespace Furniture.Service.Application.Ports; + +/// +/// Defines DAL-facing read port for furniture availability. +/// +public interface IFurnitureAvailabilityReadPort +{ + /// + /// Retrieves current availability for a furniture aggregate. + /// + /// Furniture aggregate identifier. + /// Availability response contract. + Task GetAvailabilityAsync(string furnitureId); +} diff --git a/src/Furniture.Service.Application/UseCases/GetFurnitureAvailabilityUseCase.cs b/src/Furniture.Service.Application/UseCases/GetFurnitureAvailabilityUseCase.cs new file mode 100644 index 0000000..88824b6 --- /dev/null +++ b/src/Furniture.Service.Application/UseCases/GetFurnitureAvailabilityUseCase.cs @@ -0,0 +1,17 @@ +using Furniture.Service.Application.Ports; +using Furniture.Service.Contracts.UseCases; + +namespace Furniture.Service.Application.UseCases; + +/// +/// Default orchestration implementation for furniture availability lookup. +/// +public sealed class GetFurnitureAvailabilityUseCase(IFurnitureAvailabilityReadPort readPort) + : IGetFurnitureAvailabilityUseCase +{ + /// + public Task HandleAsync(GetFurnitureAvailabilityRequest request) + { + return readPort.GetAvailabilityAsync(request.FurnitureId); + } +} diff --git a/src/Furniture.Service.Application/UseCases/IGetFurnitureAvailabilityUseCase.cs b/src/Furniture.Service.Application/UseCases/IGetFurnitureAvailabilityUseCase.cs new file mode 100644 index 0000000..bfbac90 --- /dev/null +++ b/src/Furniture.Service.Application/UseCases/IGetFurnitureAvailabilityUseCase.cs @@ -0,0 +1,16 @@ +using Furniture.Service.Contracts.UseCases; + +namespace Furniture.Service.Application.UseCases; + +/// +/// Defines orchestration boundary for furniture availability lookup. +/// +public interface IGetFurnitureAvailabilityUseCase +{ + /// + /// Handles the availability lookup use case. + /// + /// Use-case request contract. + /// Use-case response contract. + Task HandleAsync(GetFurnitureAvailabilityRequest request); +} diff --git a/src/Furniture.Service.Contracts/Furniture.Service.Contracts.csproj b/src/Furniture.Service.Contracts/Furniture.Service.Contracts.csproj new file mode 100644 index 0000000..6c3a887 --- /dev/null +++ b/src/Furniture.Service.Contracts/Furniture.Service.Contracts.csproj @@ -0,0 +1,7 @@ + + + net10.0 + enable + enable + + diff --git a/src/Furniture.Service.Contracts/UseCases/GetFurnitureAvailabilityRequest.cs b/src/Furniture.Service.Contracts/UseCases/GetFurnitureAvailabilityRequest.cs new file mode 100644 index 0000000..044d73a --- /dev/null +++ b/src/Furniture.Service.Contracts/UseCases/GetFurnitureAvailabilityRequest.cs @@ -0,0 +1,7 @@ +namespace Furniture.Service.Contracts.UseCases; + +/// +/// Request contract for furniture availability lookup. +/// +/// Furniture aggregate identifier. +public sealed record GetFurnitureAvailabilityRequest(string FurnitureId); diff --git a/src/Furniture.Service.Contracts/UseCases/GetFurnitureAvailabilityResponse.cs b/src/Furniture.Service.Contracts/UseCases/GetFurnitureAvailabilityResponse.cs new file mode 100644 index 0000000..abc8ca7 --- /dev/null +++ b/src/Furniture.Service.Contracts/UseCases/GetFurnitureAvailabilityResponse.cs @@ -0,0 +1,8 @@ +namespace Furniture.Service.Contracts.UseCases; + +/// +/// Response contract for furniture availability lookup. +/// +/// Furniture aggregate identifier. +/// Available quantity for the requested furniture. +public sealed record GetFurnitureAvailabilityResponse(string FurnitureId, int QuantityAvailable); diff --git a/src/Furniture.Service.Grpc/Furniture.Service.Grpc.csproj b/src/Furniture.Service.Grpc/Furniture.Service.Grpc.csproj new file mode 100644 index 0000000..d236f97 --- /dev/null +++ b/src/Furniture.Service.Grpc/Furniture.Service.Grpc.csproj @@ -0,0 +1,11 @@ + + + net10.0 + enable + enable + + + + + + diff --git a/src/Furniture.Service.Grpc/Program.cs b/src/Furniture.Service.Grpc/Program.cs new file mode 100644 index 0000000..ef5cbdd --- /dev/null +++ b/src/Furniture.Service.Grpc/Program.cs @@ -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(); diff --git a/tests/Furniture.Service.Application.UnitTests/Furniture.Service.Application.UnitTests.csproj b/tests/Furniture.Service.Application.UnitTests/Furniture.Service.Application.UnitTests.csproj new file mode 100644 index 0000000..f2fe7f7 --- /dev/null +++ b/tests/Furniture.Service.Application.UnitTests/Furniture.Service.Application.UnitTests.csproj @@ -0,0 +1,21 @@ + + + net10.0 + enable + enable + false + + + + + + + + + + + + + + + diff --git a/tests/Furniture.Service.Application.UnitTests/GetFurnitureAvailabilityUseCaseTests.cs b/tests/Furniture.Service.Application.UnitTests/GetFurnitureAvailabilityUseCaseTests.cs new file mode 100644 index 0000000..6ee97f4 --- /dev/null +++ b/tests/Furniture.Service.Application.UnitTests/GetFurnitureAvailabilityUseCaseTests.cs @@ -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 GetAvailabilityAsync(string furnitureId) + { + return Task.FromResult(new GetFurnitureAvailabilityResponse(furnitureId, 10)); + } + } +}