merge(furniture-service): integrate furniture-service-orchestration-normalization
This commit is contained in:
commit
e589338aa5
14
docs/architecture/service-orchestration-boundary.md
Normal file
14
docs/architecture/service-orchestration-boundary.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Furniture Service Orchestration Boundary
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Constrain furniture-service to orchestration responsibilities after domain extraction.
|
||||||
|
|
||||||
|
## Service Responsibilities
|
||||||
|
- Coordinate use-case flow
|
||||||
|
- Call domain abstractions for decisions
|
||||||
|
- Adapt transport contracts
|
||||||
|
- Depend on Domain and DAL boundaries without direct BuildingBlock ownership
|
||||||
|
|
||||||
|
## Prohibited Responsibilities
|
||||||
|
- Owning business decision rules
|
||||||
|
- Owning persistence decision concerns
|
||||||
10
docs/migration/domain-delegation-plan.md
Normal file
10
docs/migration/domain-delegation-plan.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Domain Delegation Plan
|
||||||
|
|
||||||
|
## Delegation Model
|
||||||
|
- Use cases invoke furniture-domain abstractions for decision logic.
|
||||||
|
- Service adapters retain technical mapping responsibilities.
|
||||||
|
|
||||||
|
## Transition Steps
|
||||||
|
1. Replace in-service decision branches with domain calls.
|
||||||
|
2. Keep contract shapes stable at service boundary.
|
||||||
|
3. Validate orchestration-only responsibilities.
|
||||||
10
docs/migration/orchestration-regression-checks.md
Normal file
10
docs/migration/orchestration-regression-checks.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Orchestration Regression Checks
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
- Service no longer contains domain decision branches.
|
||||||
|
- Service still orchestrates required dependencies.
|
||||||
|
- Transport contract outputs remain stable.
|
||||||
|
|
||||||
|
## Evidence
|
||||||
|
- Updated architecture docs
|
||||||
|
- Migration mapping confirmation
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
using Furniture.Service.Application.Grpc;
|
||||||
|
using Furniture.Service.Contracts.UseCases;
|
||||||
|
|
||||||
|
namespace Furniture.Service.Application.Adapters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default adapter implementation for furniture service gRPC contract translation.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class FurnitureAvailabilityGrpcContractAdapter : IFurnitureAvailabilityGrpcContractAdapter
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public GetFurnitureAvailabilityGrpcContract ToGrpc(GetFurnitureAvailabilityRequest request)
|
||||||
|
{
|
||||||
|
return new GetFurnitureAvailabilityGrpcContract(
|
||||||
|
request.FurnitureId,
|
||||||
|
ResolveCorrelationId(request.CorrelationId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public GetFurnitureAvailabilityRequest FromGrpc(GetFurnitureAvailabilityGrpcContract contract)
|
||||||
|
{
|
||||||
|
return new GetFurnitureAvailabilityRequest(
|
||||||
|
contract.FurnitureId,
|
||||||
|
ResolveCorrelationId(contract.CorrelationId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ResolveCorrelationId(string correlationId)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(correlationId))
|
||||||
|
{
|
||||||
|
return correlationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"corr-{Guid.NewGuid():N}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,39 +0,0 @@
|
|||||||
using BuildingBlock.Catalog.Contracts.Products;
|
|
||||||
using BuildingBlock.Catalog.Contracts.Responses;
|
|
||||||
using BuildingBlock.Inventory.Contracts.Requests;
|
|
||||||
using BuildingBlock.Inventory.Contracts.Responses;
|
|
||||||
using Furniture.Service.Contracts.UseCases;
|
|
||||||
|
|
||||||
namespace Furniture.Service.Application.Adapters;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines adapter boundary for furniture service contract composition.
|
|
||||||
/// </summary>
|
|
||||||
public interface IFurnitureAvailabilityContractAdapter
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Maps service request into an inventory capability request.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request">Furniture availability request.</param>
|
|
||||||
/// <returns>Inventory lookup request.</returns>
|
|
||||||
InventoryItemLookupRequest ToInventoryRequest(GetFurnitureAvailabilityRequest request);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Maps service request into a catalog capability request.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request">Furniture availability request.</param>
|
|
||||||
/// <returns>Catalog product request.</returns>
|
|
||||||
ProductContract ToCatalogRequest(GetFurnitureAvailabilityRequest request);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Maps capability responses back into the service response contract.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request">Furniture availability request.</param>
|
|
||||||
/// <param name="catalogResponse">Catalog product response.</param>
|
|
||||||
/// <param name="inventoryResponse">Inventory lookup response.</param>
|
|
||||||
/// <returns>Furniture availability response.</returns>
|
|
||||||
GetFurnitureAvailabilityResponse ToServiceResponse(
|
|
||||||
GetFurnitureAvailabilityRequest request,
|
|
||||||
ProductContractResponse catalogResponse,
|
|
||||||
InventoryItemLookupResponse inventoryResponse);
|
|
||||||
}
|
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
using Core.Blueprint.Common.DependencyInjection;
|
||||||
|
using Furniture.DAL.DependencyInjection;
|
||||||
|
using Furniture.Domain.Decisions;
|
||||||
|
using Furniture.Service.Application.Adapters;
|
||||||
|
using Furniture.Service.Application.Ports;
|
||||||
|
using Furniture.Service.Application.UseCases;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
|
||||||
|
namespace Furniture.Service.Application.DependencyInjection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers furniture-service runtime orchestration and DAL adapters.
|
||||||
|
/// </summary>
|
||||||
|
public static class FurnitureServiceRuntimeServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Adds furniture-service runtime wiring aligned with blueprint runtime and furniture-dal runtime.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">Service collection.</param>
|
||||||
|
/// <returns>Service collection for fluent chaining.</returns>
|
||||||
|
public static IServiceCollection AddFurnitureServiceRuntime(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddBlueprintRuntimeCore();
|
||||||
|
services.AddFurnitureDalRuntime();
|
||||||
|
services.TryAddSingleton<IFurnitureAvailabilityDecisionService, FurnitureAvailabilityDecisionService>();
|
||||||
|
|
||||||
|
services.TryAddSingleton<IFurnitureAvailabilityGrpcContractAdapter, FurnitureAvailabilityGrpcContractAdapter>();
|
||||||
|
services.TryAddSingleton<IFurnitureAvailabilityReadPort, FurnitureAvailabilityReadPortDalAdapter>();
|
||||||
|
services.TryAddSingleton<ICatalogProductReadPort, CatalogProductReadPortDalAdapter>();
|
||||||
|
services.TryAddSingleton<IGetFurnitureAvailabilityUseCase, GetFurnitureAvailabilityUseCase>();
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,8 +5,9 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\..\building-block-catalog\src\BuildingBlock.Catalog.Contracts\BuildingBlock.Catalog.Contracts.csproj" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
|
||||||
<ProjectReference Include="..\..\..\building-block-inventory\src\BuildingBlock.Inventory.Contracts\BuildingBlock.Inventory.Contracts.csproj" />
|
<ProjectReference Include="..\..\..\furniture-domain\src\Furniture.Domain\Furniture.Domain.csproj" />
|
||||||
|
<ProjectReference Include="..\..\..\furniture-dal\src\Furniture.DAL\Furniture.DAL.csproj" />
|
||||||
<ProjectReference Include="..\Furniture.Service.Contracts\Furniture.Service.Contracts.csproj" />
|
<ProjectReference Include="..\Furniture.Service.Contracts\Furniture.Service.Contracts.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
using BuildingBlock.Catalog.Contracts.Conventions;
|
||||||
|
using BuildingBlock.Catalog.Contracts.Products;
|
||||||
|
using BuildingBlock.Catalog.Contracts.Responses;
|
||||||
|
using Furniture.DAL.Contracts;
|
||||||
|
using Furniture.DAL.Repositories;
|
||||||
|
|
||||||
|
namespace Furniture.Service.Application.Ports;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default DAL adapter for catalog product read port.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CatalogProductReadPortDalAdapter(ICatalogRepository catalogRepository) : ICatalogProductReadPort
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<ProductContractResponse> ReadProductAsync(ProductContract request)
|
||||||
|
{
|
||||||
|
var lookupRequest = new CatalogProductLookupRequest(
|
||||||
|
new FurnitureDalContractEnvelope(
|
||||||
|
request.Envelope.ContractVersion,
|
||||||
|
request.Envelope.CorrelationId),
|
||||||
|
request.ProductId);
|
||||||
|
|
||||||
|
var projectionRecord = await catalogRepository.ReadProductAsync(lookupRequest);
|
||||||
|
if (projectionRecord is null)
|
||||||
|
{
|
||||||
|
return new ProductContractResponse(request.Envelope, request.ProductId, string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
var responseEnvelope = new CatalogContractEnvelope(
|
||||||
|
projectionRecord.Envelope.ContractVersion,
|
||||||
|
projectionRecord.Envelope.CorrelationId);
|
||||||
|
|
||||||
|
return new ProductContractResponse(
|
||||||
|
responseEnvelope,
|
||||||
|
projectionRecord.ProductId,
|
||||||
|
projectionRecord.DisplayName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
using BuildingBlock.Inventory.Contracts.Conventions;
|
||||||
|
using BuildingBlock.Inventory.Contracts.Requests;
|
||||||
|
using BuildingBlock.Inventory.Contracts.Responses;
|
||||||
|
using Furniture.DAL.Contracts;
|
||||||
|
using Furniture.DAL.Repositories;
|
||||||
|
|
||||||
|
namespace Furniture.Service.Application.Ports;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default DAL adapter for furniture availability read port.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class FurnitureAvailabilityReadPortDalAdapter(
|
||||||
|
IFurnitureRepository furnitureRepository) : IFurnitureAvailabilityReadPort
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<InventoryItemLookupResponse> ReadAvailabilityAsync(InventoryItemLookupRequest request)
|
||||||
|
{
|
||||||
|
var lookupRequest = new FurnitureAvailabilityLookupRequest(
|
||||||
|
new FurnitureDalContractEnvelope(
|
||||||
|
request.Envelope.ContractVersion,
|
||||||
|
request.Envelope.CorrelationId),
|
||||||
|
request.ItemCode);
|
||||||
|
|
||||||
|
var availabilityRecord = await furnitureRepository.ReadAvailabilityAsync(lookupRequest);
|
||||||
|
if (availabilityRecord is null)
|
||||||
|
{
|
||||||
|
return new InventoryItemLookupResponse(request.Envelope, request.ItemCode, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var responseEnvelope = new InventoryContractEnvelope(
|
||||||
|
availabilityRecord.Envelope.ContractVersion,
|
||||||
|
availabilityRecord.Envelope.CorrelationId);
|
||||||
|
|
||||||
|
return new InventoryItemLookupResponse(
|
||||||
|
responseEnvelope,
|
||||||
|
availabilityRecord.FurnitureId,
|
||||||
|
availabilityRecord.QuantityAvailable);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
using Furniture.Service.Application.Adapters;
|
|
||||||
using Furniture.Service.Application.Ports;
|
using Furniture.Service.Application.Ports;
|
||||||
using Furniture.Service.Contracts.UseCases;
|
using Furniture.Service.Contracts.UseCases;
|
||||||
|
using Furniture.Domain.Contracts;
|
||||||
|
using Furniture.Domain.Decisions;
|
||||||
|
|
||||||
namespace Furniture.Service.Application.UseCases;
|
namespace Furniture.Service.Application.UseCases;
|
||||||
|
|
||||||
@ -8,7 +9,7 @@ namespace Furniture.Service.Application.UseCases;
|
|||||||
/// Default orchestration implementation for furniture availability lookup.
|
/// Default orchestration implementation for furniture availability lookup.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class GetFurnitureAvailabilityUseCase(
|
public sealed class GetFurnitureAvailabilityUseCase(
|
||||||
IFurnitureAvailabilityContractAdapter contractAdapter,
|
IFurnitureAvailabilityDecisionService decisionService,
|
||||||
ICatalogProductReadPort catalogReadPort,
|
ICatalogProductReadPort catalogReadPort,
|
||||||
IFurnitureAvailabilityReadPort inventoryReadPort)
|
IFurnitureAvailabilityReadPort inventoryReadPort)
|
||||||
: IGetFurnitureAvailabilityUseCase
|
: IGetFurnitureAvailabilityUseCase
|
||||||
@ -16,17 +17,25 @@ public sealed class GetFurnitureAvailabilityUseCase(
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<GetFurnitureAvailabilityResponse> HandleAsync(GetFurnitureAvailabilityRequest request)
|
public async Task<GetFurnitureAvailabilityResponse> HandleAsync(GetFurnitureAvailabilityRequest request)
|
||||||
{
|
{
|
||||||
var catalogRequest = contractAdapter.ToCatalogRequest(request);
|
var domainRequest = new FurnitureAvailabilityDecisionRequest(
|
||||||
var inventoryRequest = contractAdapter.ToInventoryRequest(request);
|
request.FurnitureId,
|
||||||
|
request.CorrelationId);
|
||||||
|
var catalogRequest = decisionService.BuildCatalogRequest(domainRequest);
|
||||||
|
var inventoryRequest = decisionService.BuildInventoryRequest(domainRequest);
|
||||||
|
|
||||||
var catalogTask = catalogReadPort.ReadProductAsync(catalogRequest);
|
var catalogTask = catalogReadPort.ReadProductAsync(catalogRequest);
|
||||||
var inventoryTask = inventoryReadPort.ReadAvailabilityAsync(inventoryRequest);
|
var inventoryTask = inventoryReadPort.ReadAvailabilityAsync(inventoryRequest);
|
||||||
|
|
||||||
await Task.WhenAll(catalogTask, inventoryTask);
|
await Task.WhenAll(catalogTask, inventoryTask);
|
||||||
|
|
||||||
return contractAdapter.ToServiceResponse(
|
var domainResponse = decisionService.ComposeResponse(
|
||||||
request,
|
domainRequest,
|
||||||
await catalogTask,
|
await catalogTask,
|
||||||
await inventoryTask);
|
await inventoryTask);
|
||||||
|
|
||||||
|
return new GetFurnitureAvailabilityResponse(
|
||||||
|
domainResponse.FurnitureId,
|
||||||
|
domainResponse.DisplayName,
|
||||||
|
domainResponse.QuantityAvailable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,16 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Grpc.AspNetCore" Version="2.71.0" />
|
||||||
|
<PackageReference Include="Grpc.Tools" Version="2.71.0">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Protobuf Include="Protos\furniture_runtime.proto" GrpcServices="Server" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Furniture.Service.Application\Furniture.Service.Application.csproj" />
|
<ProjectReference Include="..\Furniture.Service.Application\Furniture.Service.Application.csproj" />
|
||||||
<ProjectReference Include="..\Furniture.Service.Contracts\Furniture.Service.Contracts.csproj" />
|
<ProjectReference Include="..\Furniture.Service.Contracts\Furniture.Service.Contracts.csproj" />
|
||||||
|
|||||||
@ -1,6 +1,15 @@
|
|||||||
|
using Furniture.Service.Application.DependencyInjection;
|
||||||
|
using Furniture.Service.Grpc.Services;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Stage 3 skeleton: keep a single active internal protocol policy (gRPC-first).
|
builder.Services.AddGrpc();
|
||||||
|
builder.Services.AddHealthChecks();
|
||||||
|
builder.Services.AddFurnitureServiceRuntime();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
|
app.MapGrpcService<FurnitureRuntimeGrpcService>();
|
||||||
|
app.MapHealthChecks("/healthz");
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|||||||
20
src/Furniture.Service.Grpc/Protos/furniture_runtime.proto
Normal file
20
src/Furniture.Service.Grpc/Protos/furniture_runtime.proto
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
option csharp_namespace = "Furniture.Service.Grpc";
|
||||||
|
|
||||||
|
package furniture.service.grpc;
|
||||||
|
|
||||||
|
service FurnitureRuntime {
|
||||||
|
rpc GetFurnitureAvailability (GetFurnitureAvailabilityGrpcRequest) returns (GetFurnitureAvailabilityGrpcResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetFurnitureAvailabilityGrpcRequest {
|
||||||
|
string furniture_id = 1;
|
||||||
|
string correlation_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetFurnitureAvailabilityGrpcResponse {
|
||||||
|
string furniture_id = 1;
|
||||||
|
string display_name = 2;
|
||||||
|
int32 quantity_available = 3;
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
using Grpc.Core;
|
||||||
|
using Furniture.Service.Application.Adapters;
|
||||||
|
using Furniture.Service.Application.Grpc;
|
||||||
|
using Furniture.Service.Application.UseCases;
|
||||||
|
|
||||||
|
namespace Furniture.Service.Grpc.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Internal gRPC endpoint implementation for furniture runtime operations.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class FurnitureRuntimeGrpcService(
|
||||||
|
IGetFurnitureAvailabilityUseCase getFurnitureAvailabilityUseCase,
|
||||||
|
IFurnitureAvailabilityGrpcContractAdapter grpcContractAdapter) : FurnitureRuntime.FurnitureRuntimeBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Executes furniture availability lookup through service use-case orchestration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">gRPC availability request.</param>
|
||||||
|
/// <param name="context">gRPC server call context.</param>
|
||||||
|
/// <returns>gRPC availability response.</returns>
|
||||||
|
public override async Task<GetFurnitureAvailabilityGrpcResponse> GetFurnitureAvailability(
|
||||||
|
GetFurnitureAvailabilityGrpcRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
var grpcContract = new GetFurnitureAvailabilityGrpcContract(
|
||||||
|
request.FurnitureId,
|
||||||
|
request.CorrelationId);
|
||||||
|
|
||||||
|
var useCaseRequest = grpcContractAdapter.FromGrpc(grpcContract);
|
||||||
|
var useCaseResponse = await getFurnitureAvailabilityUseCase.HandleAsync(useCaseRequest);
|
||||||
|
|
||||||
|
return new GetFurnitureAvailabilityGrpcResponse
|
||||||
|
{
|
||||||
|
FurnitureId = useCaseResponse.FurnitureId,
|
||||||
|
DisplayName = useCaseResponse.DisplayName,
|
||||||
|
QuantityAvailable = useCaseResponse.QuantityAvailable
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,6 +7,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.4" />
|
<PackageReference Include="coverlet.collector" Version="6.0.4" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||||
<PackageReference Include="xunit" Version="2.9.3" />
|
<PackageReference Include="xunit" Version="2.9.3" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
|
||||||
|
|||||||
@ -4,10 +4,11 @@ using BuildingBlock.Catalog.Contracts.Responses;
|
|||||||
using BuildingBlock.Inventory.Contracts.Conventions;
|
using BuildingBlock.Inventory.Contracts.Conventions;
|
||||||
using BuildingBlock.Inventory.Contracts.Requests;
|
using BuildingBlock.Inventory.Contracts.Requests;
|
||||||
using BuildingBlock.Inventory.Contracts.Responses;
|
using BuildingBlock.Inventory.Contracts.Responses;
|
||||||
using Furniture.Service.Application.Adapters;
|
|
||||||
using Furniture.Service.Application.Ports;
|
using Furniture.Service.Application.Ports;
|
||||||
using Furniture.Service.Application.UseCases;
|
using Furniture.Service.Application.UseCases;
|
||||||
using Furniture.Service.Contracts.UseCases;
|
using Furniture.Service.Contracts.UseCases;
|
||||||
|
using Furniture.Domain.Contracts;
|
||||||
|
using Furniture.Domain.Decisions;
|
||||||
|
|
||||||
namespace Furniture.Service.Application.UnitTests;
|
namespace Furniture.Service.Application.UnitTests;
|
||||||
|
|
||||||
@ -16,10 +17,10 @@ public class GetFurnitureAvailabilityUseCaseTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task HandleAsync_WhenCalled_DelegatesToReadPort()
|
public async Task HandleAsync_WhenCalled_DelegatesToReadPort()
|
||||||
{
|
{
|
||||||
var adapter = new FakeFurnitureAvailabilityContractAdapter();
|
var decisionService = new FakeFurnitureAvailabilityDecisionService();
|
||||||
var catalogPort = new FakeCatalogProductReadPort();
|
var catalogPort = new FakeCatalogProductReadPort();
|
||||||
var inventoryPort = new FakeFurnitureAvailabilityReadPort();
|
var inventoryPort = new FakeFurnitureAvailabilityReadPort();
|
||||||
var useCase = new GetFurnitureAvailabilityUseCase(adapter, catalogPort, inventoryPort);
|
var useCase = new GetFurnitureAvailabilityUseCase(decisionService, catalogPort, inventoryPort);
|
||||||
|
|
||||||
var response = await useCase.HandleAsync(new GetFurnitureAvailabilityRequest("FUR-001", "corr-123"));
|
var response = await useCase.HandleAsync(new GetFurnitureAvailabilityRequest("FUR-001", "corr-123"));
|
||||||
|
|
||||||
@ -28,9 +29,9 @@ public class GetFurnitureAvailabilityUseCaseTests
|
|||||||
Assert.Equal(10, response.QuantityAvailable);
|
Assert.Equal(10, response.QuantityAvailable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class FakeFurnitureAvailabilityContractAdapter : IFurnitureAvailabilityContractAdapter
|
private sealed class FakeFurnitureAvailabilityDecisionService : IFurnitureAvailabilityDecisionService
|
||||||
{
|
{
|
||||||
public ProductContract ToCatalogRequest(GetFurnitureAvailabilityRequest request)
|
public ProductContract BuildCatalogRequest(FurnitureAvailabilityDecisionRequest request)
|
||||||
{
|
{
|
||||||
return new ProductContract(
|
return new ProductContract(
|
||||||
new CatalogContractEnvelope("1.0.0", request.CorrelationId),
|
new CatalogContractEnvelope("1.0.0", request.CorrelationId),
|
||||||
@ -38,19 +39,19 @@ public class GetFurnitureAvailabilityUseCaseTests
|
|||||||
string.Empty);
|
string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public InventoryItemLookupRequest ToInventoryRequest(GetFurnitureAvailabilityRequest request)
|
public InventoryItemLookupRequest BuildInventoryRequest(FurnitureAvailabilityDecisionRequest request)
|
||||||
{
|
{
|
||||||
return new InventoryItemLookupRequest(
|
return new InventoryItemLookupRequest(
|
||||||
new InventoryContractEnvelope("1.0.0", request.CorrelationId),
|
new InventoryContractEnvelope("1.0.0", request.CorrelationId),
|
||||||
request.FurnitureId);
|
request.FurnitureId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetFurnitureAvailabilityResponse ToServiceResponse(
|
public FurnitureAvailabilityDecisionResponse ComposeResponse(
|
||||||
GetFurnitureAvailabilityRequest request,
|
FurnitureAvailabilityDecisionRequest request,
|
||||||
ProductContractResponse catalogResponse,
|
ProductContractResponse catalogResponse,
|
||||||
InventoryItemLookupResponse inventoryResponse)
|
InventoryItemLookupResponse inventoryResponse)
|
||||||
{
|
{
|
||||||
return new GetFurnitureAvailabilityResponse(
|
return new FurnitureAvailabilityDecisionResponse(
|
||||||
request.FurnitureId,
|
request.FurnitureId,
|
||||||
catalogResponse.DisplayName,
|
catalogResponse.DisplayName,
|
||||||
inventoryResponse.QuantityAvailable);
|
inventoryResponse.QuantityAvailable);
|
||||||
|
|||||||
@ -0,0 +1,51 @@
|
|||||||
|
using Furniture.Service.Application.Adapters;
|
||||||
|
using Furniture.Service.Application.DependencyInjection;
|
||||||
|
using Furniture.Service.Application.Grpc;
|
||||||
|
using Furniture.Service.Application.UseCases;
|
||||||
|
using Furniture.Service.Contracts.UseCases;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Furniture.Service.Application.UnitTests;
|
||||||
|
|
||||||
|
public class RuntimeWiringTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task AddFurnitureServiceRuntime_WhenInvoked_ResolvesUseCase()
|
||||||
|
{
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddFurnitureServiceRuntime();
|
||||||
|
|
||||||
|
using var provider = services.BuildServiceProvider();
|
||||||
|
var useCase = provider.GetRequiredService<IGetFurnitureAvailabilityUseCase>();
|
||||||
|
|
||||||
|
var response = await useCase.HandleAsync(new GetFurnitureAvailabilityRequest("FUR-001", "corr-123"));
|
||||||
|
|
||||||
|
Assert.Equal("FUR-001", response.FurnitureId);
|
||||||
|
Assert.Equal(8, response.QuantityAvailable);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FurnitureAvailabilityGrpcContractAdapter_WhenMapped_PreservesValues()
|
||||||
|
{
|
||||||
|
var adapter = new FurnitureAvailabilityGrpcContractAdapter();
|
||||||
|
var request = new GetFurnitureAvailabilityRequest("FUR-002", "corr-456");
|
||||||
|
|
||||||
|
var grpcContract = adapter.ToGrpc(request);
|
||||||
|
var roundtrip = adapter.FromGrpc(grpcContract);
|
||||||
|
|
||||||
|
Assert.Equal("FUR-002", roundtrip.FurnitureId);
|
||||||
|
Assert.Equal("corr-456", roundtrip.CorrelationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FurnitureAvailabilityGrpcContractAdapter_WhenCorrelationMissing_GeneratesCorrelation()
|
||||||
|
{
|
||||||
|
var adapter = new FurnitureAvailabilityGrpcContractAdapter();
|
||||||
|
var grpcContract = new GetFurnitureAvailabilityGrpcContract("FUR-003", string.Empty);
|
||||||
|
|
||||||
|
var mapped = adapter.FromGrpc(grpcContract);
|
||||||
|
|
||||||
|
Assert.Equal("FUR-003", mapped.FurnitureId);
|
||||||
|
Assert.NotEmpty(mapped.CorrelationId);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user