Compare commits
10 Commits
4647306583
...
bff24f3124
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bff24f3124 | ||
|
|
53055650df | ||
|
|
34d93c7e51 | ||
|
|
148bfad9d2 | ||
|
|
ec7c680b30 | ||
|
|
e17023017d | ||
|
|
8d4c3a15dd | ||
|
|
e589338aa5 | ||
|
|
a149394cac | ||
|
|
833813ad13 |
9
.dockerignore
Normal file
9
.dockerignore
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
**/bin/
|
||||||
|
**/obj/
|
||||||
|
.vs/
|
||||||
|
TestResults/
|
||||||
|
.git/
|
||||||
|
.repo-tasks/
|
||||||
|
.repo-context/
|
||||||
|
.tasks/
|
||||||
|
.agile/
|
||||||
63
.gitignore
vendored
63
.gitignore
vendored
@ -1,53 +1,24 @@
|
|||||||
# AgileWebs local orchestration
|
# Repository orchestration folders (local only)
|
||||||
|
.repo-tasks/
|
||||||
|
.repo-context/
|
||||||
.tasks/
|
.tasks/
|
||||||
.agile/
|
.agile/
|
||||||
|
|
||||||
# Build artifacts
|
# .NET build outputs
|
||||||
**/[Bb]in/
|
**/bin/
|
||||||
**/[Oo]bj/
|
**/obj/
|
||||||
/**/out/
|
|
||||||
/**/artifacts/
|
|
||||||
|
|
||||||
# IDE and editor files
|
|
||||||
.vs/
|
.vs/
|
||||||
.idea/
|
TestResults/
|
||||||
.vscode/
|
|
||||||
*.suo
|
|
||||||
*.user
|
|
||||||
*.userosscache
|
|
||||||
*.sln.docstates
|
|
||||||
*.rsuser
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
|
|
||||||
# NuGet
|
|
||||||
*.nupkg
|
|
||||||
*.snupkg
|
|
||||||
**/packages/*
|
|
||||||
!**/packages/build/
|
|
||||||
|
|
||||||
# Test output
|
|
||||||
**/TestResults/
|
**/TestResults/
|
||||||
*.trx
|
*.user
|
||||||
*.coverage
|
*.suo
|
||||||
*.coveragexml
|
*.rsuser
|
||||||
|
|
||||||
# Logs
|
# IDE
|
||||||
*.log
|
.idea/
|
||||||
|
|
||||||
|
# Runtime-local artifacts
|
||||||
logs/
|
logs/
|
||||||
|
*.log
|
||||||
# Local environment files
|
.env.local
|
||||||
.env
|
.env.*.local
|
||||||
.env.*
|
|
||||||
!.env.example
|
|
||||||
|
|
||||||
# Docker
|
|
||||||
.docker/
|
|
||||||
**/.docker/
|
|
||||||
*.pid
|
|
||||||
docker-compose.override.yml
|
|
||||||
docker-compose.*.override.yml
|
|
||||||
|
|
||||||
# OS files
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|||||||
10
Directory.Build.props
Normal file
10
Directory.Build.props
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<Authors>AgileWebs</Authors>
|
||||||
|
<Company>AgileWebs</Company>
|
||||||
|
<RepositoryType>git</RepositoryType>
|
||||||
|
<RepositoryUrl>https://gitea.dream-views.com/AgileWebs/furniture-service</RepositoryUrl>
|
||||||
|
<PackageProjectUrl>https://gitea.dream-views.com/AgileWebs/furniture-service</PackageProjectUrl>
|
||||||
|
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
23
Dockerfile
Normal file
23
Dockerfile
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# syntax=docker/dockerfile:1.7
|
||||||
|
ARG SDK_IMAGE=mcr.microsoft.com/dotnet/sdk:10.0
|
||||||
|
ARG RUNTIME_IMAGE=mcr.microsoft.com/dotnet/aspnet:10.0
|
||||||
|
|
||||||
|
FROM ${SDK_IMAGE} AS build
|
||||||
|
ARG NUGET_FEED_URL=https://gitea.dream-views.com/api/packages/AgileWebs/nuget/index.json
|
||||||
|
ARG NUGET_FEED_USERNAME=
|
||||||
|
ARG NUGET_FEED_TOKEN=
|
||||||
|
WORKDIR /src
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN if [ -n "$NUGET_FEED_USERNAME" ] && [ -n "$NUGET_FEED_TOKEN" ]; then dotnet nuget add source "$NUGET_FEED_URL" --name gitea-org --username "$NUGET_FEED_USERNAME" --password "$NUGET_FEED_TOKEN" --store-password-in-clear-text --allow-insecure-connections --configfile /root/.nuget/NuGet/NuGet.Config; fi
|
||||||
|
|
||||||
|
RUN dotnet restore "src/Furniture.Service.Grpc/Furniture.Service.Grpc.csproj" --configfile /root/.nuget/NuGet/NuGet.Config
|
||||||
|
RUN dotnet publish "src/Furniture.Service.Grpc/Furniture.Service.Grpc.csproj" -c Release -o /app/publish /p:UseAppHost=false --no-restore
|
||||||
|
|
||||||
|
FROM ${RUNTIME_IMAGE} AS runtime
|
||||||
|
WORKDIR /app
|
||||||
|
ENV ASPNETCORE_ENVIRONMENT=Production
|
||||||
|
EXPOSE 8080
|
||||||
|
EXPOSE 8081
|
||||||
|
COPY --from=build /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "Furniture.Service.Grpc.dll"]
|
||||||
@ -6,6 +6,7 @@
|
|||||||
- Use cases depend on DAL-facing ports, not persistence implementations.
|
- Use cases depend on DAL-facing ports, not persistence implementations.
|
||||||
- Use cases consume BuildingBlock capability contracts through adapter and port boundaries.
|
- Use cases consume BuildingBlock capability contracts through adapter and port boundaries.
|
||||||
- Transport handlers map to use-case contracts and do not own orchestration logic.
|
- Transport handlers map to use-case contracts and do not own orchestration logic.
|
||||||
|
- Demo/runtime aliases (`demo-context`, `FURN-*`, `PROD-FURN-*`) are normalized at use-case orchestration before port calls.
|
||||||
|
|
||||||
## Current Skeleton
|
## Current Skeleton
|
||||||
|
|
||||||
|
|||||||
27
docs/architecture/package-publication-baseline.md
Normal file
27
docs/architecture/package-publication-baseline.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Furniture Service Contract Package Baseline
|
||||||
|
|
||||||
|
## Feed
|
||||||
|
|
||||||
|
- Source: `https://gitea.dream-views.com/api/packages/AgileWebs/nuget/index.json`
|
||||||
|
- Authentication: Gitea login + token
|
||||||
|
- HTTP requirement: `allowInsecureConnections="true"` in `nuget.config`
|
||||||
|
|
||||||
|
## Published Baseline
|
||||||
|
|
||||||
|
| Package | Version | Published On |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Furniture.Service.Contracts | 0.2.0 | 2026-02-25 |
|
||||||
|
|
||||||
|
## Dependency Note
|
||||||
|
|
||||||
|
`Furniture.Service.Contracts` depends on:
|
||||||
|
|
||||||
|
- `Core.Blueprint.Common` version `0.2.0`
|
||||||
|
|
||||||
|
## Consumer Validation
|
||||||
|
|
||||||
|
Restore validation passed using:
|
||||||
|
|
||||||
|
- `TargetFramework`: `net10.0`
|
||||||
|
- `PackageReference`: `Furniture.Service.Contracts` `0.2.0`
|
||||||
|
- Restore flags: `--no-cache --force`
|
||||||
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
|
||||||
18
docs/roadmap/feature-epics.md
Normal file
18
docs/roadmap/feature-epics.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Feature Epics
|
||||||
|
|
||||||
|
## Repository
|
||||||
|
furniture-service
|
||||||
|
|
||||||
|
## Core Epics
|
||||||
|
- Epic 1: Expand domain-aligned capabilities for restaurant operations.
|
||||||
|
- Epic 2: Stabilize service contracts for containerized runtime integration.
|
||||||
|
- Epic 3: Improve observability and operational readiness for demo compose environments.
|
||||||
|
|
||||||
|
## Domain-Specific Candidate Features
|
||||||
|
- Order lifecycle consistency and state transitions.
|
||||||
|
- Kitchen queue and dispatch optimization hooks.
|
||||||
|
- Operations control-plane policies (flags, service windows, overrides).
|
||||||
|
- POS closeout and settlement summary alignment.
|
||||||
|
|
||||||
|
## Documentation Contract
|
||||||
|
Any code change in this repository must include docs updates in the same branch.
|
||||||
40
docs/runbooks/containerization.md
Normal file
40
docs/runbooks/containerization.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Containerization Runbook
|
||||||
|
|
||||||
|
## Image Build
|
||||||
|
|
||||||
|
If the repo consumes internal packages from Gitea, pass feed credentials as build args.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build --build-arg NUGET_FEED_USERNAME=<gitea-login> --build-arg NUGET_FEED_TOKEN=<gitea-token> -t agilewebs/furniture-service:dev .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Local Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --rm -p 8080:8080 --name furniture-service agilewebs/furniture-service:dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Health Probe
|
||||||
|
|
||||||
|
- Path: `/health`
|
||||||
|
- Fallback path: `/healthz`
|
||||||
|
- Port: `8080`
|
||||||
|
|
||||||
|
## Runtime Notes
|
||||||
|
|
||||||
|
- Exposes internal service endpoint set plus gRPC runtime surface.
|
||||||
|
|
||||||
|
## Health Endpoint Consistency
|
||||||
|
|
||||||
|
- Canonical probe: `/health`
|
||||||
|
- Compatibility probe: `/healthz`
|
||||||
|
- Container port: `8080`
|
||||||
|
|
||||||
|
## Demo Integration
|
||||||
|
|
||||||
|
- Participates in: **furniture** demo compose stack.
|
||||||
|
- Integration artifact path: `greenfield/demo/furniture/docker-compose.yml`
|
||||||
|
## Known Limitations
|
||||||
|
|
||||||
|
- Current runtime adapters are still predominantly in-memory for deterministic local/demo behavior.
|
||||||
|
- Demo PostgreSQL seeds validate integration contracts and smoke determinism, but do not yet imply full persistence implementation parity.
|
||||||
@ -1,56 +0,0 @@
|
|||||||
using BuildingBlock.Catalog.Contracts.Conventions;
|
|
||||||
using BuildingBlock.Catalog.Contracts.Products;
|
|
||||||
using BuildingBlock.Catalog.Contracts.Responses;
|
|
||||||
using BuildingBlock.Inventory.Contracts.Conventions;
|
|
||||||
using BuildingBlock.Inventory.Contracts.Requests;
|
|
||||||
using BuildingBlock.Inventory.Contracts.Responses;
|
|
||||||
using Furniture.Service.Contracts.UseCases;
|
|
||||||
|
|
||||||
namespace Furniture.Service.Application.Adapters;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Default adapter implementation for furniture service contract composition.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class FurnitureAvailabilityContractAdapter : IFurnitureAvailabilityContractAdapter
|
|
||||||
{
|
|
||||||
private const string ContractVersion = "1.0.0";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public InventoryItemLookupRequest ToInventoryRequest(GetFurnitureAvailabilityRequest request)
|
|
||||||
{
|
|
||||||
return new InventoryItemLookupRequest(
|
|
||||||
new InventoryContractEnvelope(ContractVersion, ResolveCorrelationId(request.CorrelationId)),
|
|
||||||
request.FurnitureId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public ProductContract ToCatalogRequest(GetFurnitureAvailabilityRequest request)
|
|
||||||
{
|
|
||||||
return new ProductContract(
|
|
||||||
new CatalogContractEnvelope(ContractVersion, ResolveCorrelationId(request.CorrelationId)),
|
|
||||||
request.FurnitureId,
|
|
||||||
string.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public GetFurnitureAvailabilityResponse ToServiceResponse(
|
|
||||||
GetFurnitureAvailabilityRequest request,
|
|
||||||
ProductContractResponse catalogResponse,
|
|
||||||
InventoryItemLookupResponse inventoryResponse)
|
|
||||||
{
|
|
||||||
return new GetFurnitureAvailabilityResponse(
|
|
||||||
request.FurnitureId,
|
|
||||||
catalogResponse.DisplayName,
|
|
||||||
inventoryResponse.QuantityAvailable);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
using Core.Blueprint.Common.DependencyInjection;
|
using Core.Blueprint.Common.DependencyInjection;
|
||||||
using Furniture.DAL.DependencyInjection;
|
using Furniture.DAL.DependencyInjection;
|
||||||
|
using Furniture.Domain.Decisions;
|
||||||
using Furniture.Service.Application.Adapters;
|
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;
|
||||||
@ -22,8 +23,8 @@ public static class FurnitureServiceRuntimeServiceCollectionExtensions
|
|||||||
{
|
{
|
||||||
services.AddBlueprintRuntimeCore();
|
services.AddBlueprintRuntimeCore();
|
||||||
services.AddFurnitureDalRuntime();
|
services.AddFurnitureDalRuntime();
|
||||||
|
services.TryAddSingleton<IFurnitureAvailabilityDecisionService, FurnitureAvailabilityDecisionService>();
|
||||||
|
|
||||||
services.TryAddSingleton<IFurnitureAvailabilityContractAdapter, FurnitureAvailabilityContractAdapter>();
|
|
||||||
services.TryAddSingleton<IFurnitureAvailabilityGrpcContractAdapter, FurnitureAvailabilityGrpcContractAdapter>();
|
services.TryAddSingleton<IFurnitureAvailabilityGrpcContractAdapter, FurnitureAvailabilityGrpcContractAdapter>();
|
||||||
services.TryAddSingleton<IFurnitureAvailabilityReadPort, FurnitureAvailabilityReadPortDalAdapter>();
|
services.TryAddSingleton<IFurnitureAvailabilityReadPort, FurnitureAvailabilityReadPortDalAdapter>();
|
||||||
services.TryAddSingleton<ICatalogProductReadPort, CatalogProductReadPortDalAdapter>();
|
services.TryAddSingleton<ICatalogProductReadPort, CatalogProductReadPortDalAdapter>();
|
||||||
|
|||||||
@ -6,9 +6,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
|
||||||
<ProjectReference Include="..\..\..\building-block-catalog\src\BuildingBlock.Catalog.Contracts\BuildingBlock.Catalog.Contracts.csproj" />
|
<PackageReference Include="Furniture.Domain" Version="0.2.0" />
|
||||||
<ProjectReference Include="..\..\..\building-block-inventory\src\BuildingBlock.Inventory.Contracts\BuildingBlock.Inventory.Contracts.csproj" />
|
<PackageReference Include="Furniture.DAL" Version="0.2.0" />
|
||||||
<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>
|
||||||
|
|||||||
@ -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,25 +9,67 @@ 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
|
||||||
{
|
{
|
||||||
|
private static readonly IReadOnlyDictionary<string, (string InventoryItemCode, string CatalogProductId)> LookupAliases =
|
||||||
|
new Dictionary<string, (string InventoryItemCode, string CatalogProductId)>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
["demo-context"] = ("FUR-001", "PRD-001"),
|
||||||
|
["FURN-001"] = ("FUR-001", "PRD-001"),
|
||||||
|
["FURN-002"] = ("FUR-002", "PRD-002"),
|
||||||
|
["FURN-003"] = ("FUR-003", "PRD-003"),
|
||||||
|
["PROD-FURN-001"] = ("FUR-001", "PRD-001"),
|
||||||
|
["PROD-FURN-002"] = ("FUR-002", "PRD-002"),
|
||||||
|
["PROD-FURN-003"] = ("FUR-003", "PRD-003"),
|
||||||
|
["FUR-001"] = ("FUR-001", "PRD-001"),
|
||||||
|
["FUR-002"] = ("FUR-002", "PRD-002"),
|
||||||
|
["FUR-003"] = ("FUR-003", "PRD-003"),
|
||||||
|
["PRD-001"] = ("FUR-001", "PRD-001"),
|
||||||
|
["PRD-002"] = ("FUR-002", "PRD-002"),
|
||||||
|
["PRD-003"] = ("FUR-003", "PRD-003")
|
||||||
|
};
|
||||||
|
|
||||||
/// <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 lookupKeys = ResolveLookupKeys(request.FurnitureId);
|
||||||
|
|
||||||
|
var catalogRequest = decisionService
|
||||||
|
.BuildCatalogRequest(domainRequest)
|
||||||
|
with { ProductId = lookupKeys.CatalogProductId };
|
||||||
|
var inventoryRequest = decisionService
|
||||||
|
.BuildInventoryRequest(domainRequest)
|
||||||
|
with { ItemCode = lookupKeys.InventoryItemCode };
|
||||||
|
|
||||||
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (string InventoryItemCode, string CatalogProductId) ResolveLookupKeys(string furnitureId)
|
||||||
|
{
|
||||||
|
if (LookupAliases.TryGetValue(furnitureId, out var mappedKeys))
|
||||||
|
{
|
||||||
|
return mappedKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (furnitureId, furnitureId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,6 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\..\blueprint-platform\src\Core.Blueprint.Common\Core.Blueprint.Common.csproj" />
|
<PackageReference Include="Core.Blueprint.Common" Version="0.2.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -1,7 +1,16 @@
|
|||||||
using Furniture.Service.Application.DependencyInjection;
|
using Furniture.Service.Application.DependencyInjection;
|
||||||
using Furniture.Service.Grpc.Services;
|
using Furniture.Service.Grpc.Services;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
var httpPort = builder.Configuration.GetValue("FurnitureService:HttpPort", 8080);
|
||||||
|
var grpcPort = builder.Configuration.GetValue("FurnitureService:GrpcPort", 8081);
|
||||||
|
|
||||||
|
builder.WebHost.ConfigureKestrel(options =>
|
||||||
|
{
|
||||||
|
options.ListenAnyIP(httpPort, listenOptions => listenOptions.Protocols = HttpProtocols.Http1);
|
||||||
|
options.ListenAnyIP(grpcPort, listenOptions => listenOptions.Protocols = HttpProtocols.Http2);
|
||||||
|
});
|
||||||
|
|
||||||
builder.Services.AddGrpc();
|
builder.Services.AddGrpc();
|
||||||
builder.Services.AddHealthChecks();
|
builder.Services.AddHealthChecks();
|
||||||
@ -11,5 +20,6 @@ var app = builder.Build();
|
|||||||
|
|
||||||
app.MapGrpcService<FurnitureRuntimeGrpcService>();
|
app.MapGrpcService<FurnitureRuntimeGrpcService>();
|
||||||
app.MapHealthChecks("/healthz");
|
app.MapHealthChecks("/healthz");
|
||||||
|
app.MapHealthChecks("/health");
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -21,6 +21,23 @@ public class RuntimeWiringTests
|
|||||||
var response = await useCase.HandleAsync(new GetFurnitureAvailabilityRequest("FUR-001", "corr-123"));
|
var response = await useCase.HandleAsync(new GetFurnitureAvailabilityRequest("FUR-001", "corr-123"));
|
||||||
|
|
||||||
Assert.Equal("FUR-001", response.FurnitureId);
|
Assert.Equal("FUR-001", response.FurnitureId);
|
||||||
|
Assert.Equal("Contoso Lounge Chair", response.DisplayName);
|
||||||
|
Assert.Equal(8, response.QuantityAvailable);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AddFurnitureServiceRuntime_WhenUsingDemoFurnitureAlias_ResolvesMappedAvailability()
|
||||||
|
{
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddFurnitureServiceRuntime();
|
||||||
|
|
||||||
|
using var provider = services.BuildServiceProvider();
|
||||||
|
var useCase = provider.GetRequiredService<IGetFurnitureAvailabilityUseCase>();
|
||||||
|
|
||||||
|
var response = await useCase.HandleAsync(new GetFurnitureAvailabilityRequest("FURN-001", "corr-123"));
|
||||||
|
|
||||||
|
Assert.Equal("FURN-001", response.FurnitureId);
|
||||||
|
Assert.Equal("Contoso Lounge Chair", response.DisplayName);
|
||||||
Assert.Equal(8, response.QuantityAvailable);
|
Assert.Equal(8, response.QuantityAvailable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user