feat(kitchen-ops-bff): bridge kitchen state into shared flow

This commit is contained in:
José René White Enciso 2026-03-31 18:53:28 -06:00
parent fcaa7e0d91
commit b6b6c8fcf6
6 changed files with 41 additions and 14 deletions

View File

@ -30,4 +30,6 @@ This BFF exposes kitchen board, claim or release, transition, and priority workf
## Notes ## Notes
- `kitchen-service` currently exposes claim but not a dedicated release contract, so the release route reuses the claim validation path and projects a release-oriented response for the BFF edge. - `kitchen-service` currently exposes claim but not a dedicated release contract, so the release route reuses the claim validation path and projects a release-oriented response for the BFF edge.
- Transition requests now forward `ContextId` to `kitchen-service` so kitchen actions land in the correct shared restaurant lifecycle context.
- The BFF keeps temporary edge-state compatibility for existing web clients by translating `Cooking|Ready|Served` to the canonical kitchen-service states `Preparing|ReadyForPickup|Delivered`.
- Correlation IDs are preserved through Thalos session checks and kitchen-service calls. - Correlation IDs are preserved through Thalos session checks and kitchen-service calls.

View File

@ -9,11 +9,10 @@ kitchen-ops-bff
- Epic 3: Improve observability and operational readiness for demo compose environments. - Epic 3: Improve observability and operational readiness for demo compose environments.
## Domain-Specific Candidate Features ## Domain-Specific Candidate Features
- Order lifecycle consistency and state transitions. - Kitchen board projection over linked restaurant tickets.
- Kitchen queue and dispatch optimization hooks. - Kitchen work-item claim, release, transition, and priority workflows aligned with canonical kitchen-service state transitions.
- Kitchen work-item claim, release, transition, and priority workflows aligned with kitchen-service. - Cross-app state continuity from customer or waiter submission through kitchen execution and POS readiness.
- Operations control-plane policies (flags, service windows, overrides). - Operations control-plane policies (flags, service windows, overrides).
- POS closeout and settlement summary alignment.
## Documentation Contract ## Documentation Contract
Any code change in this repository must include docs updates in the same branch. Any code change in this repository must include docs updates in the same branch.

View File

@ -39,5 +39,6 @@ docker run --rm -p 8080:8080 --name kitchen-ops-bff agilewebs/kitchen-ops-bff:de
- Integration artifact path: `greenfield/demo/restaurant/docker-compose.yml` - Integration artifact path: `greenfield/demo/restaurant/docker-compose.yml`
## Known Limitations ## Known Limitations
- Kitchen-ops now delegates dashboard and work-item actions to `kitchen-service`, but the upstream kitchen workflow adapter still serves deterministic demo data rather than database-backed state. - Kitchen-ops now delegates dashboard and work-item actions to `kitchen-service`, which projects persisted kitchen ticket state and syncs order progression back into the shared restaurant lifecycle.
- Temporary edge-state translation remains in place for existing web clients until the Stage 47 kitchen web task adopts the canonical kitchen-service states directly.
- Demo PostgreSQL seeds validate integration contracts and smoke determinism, but do not yet imply full persistence implementation parity. - Demo PostgreSQL seeds validate integration contracts and smoke determinism, but do not yet imply full persistence implementation parity.

View File

@ -75,7 +75,11 @@ public sealed class KitchenWorkflowServiceClient(HttpClient httpClient) : IKitch
{ {
using var response = await httpClient.PostAsJsonAsync( using var response = await httpClient.PostAsJsonAsync(
"internal/kitchen/orders/transition", "internal/kitchen/orders/transition",
new TransitionKitchenWorkItemPayload(request.OrderId, request.TicketId, request.TargetState), new TransitionKitchenWorkItemPayload(
request.OrderId,
request.TicketId,
MapKitchenServiceState(request.TargetState),
request.ContextId ?? "demo-context"),
cancellationToken); cancellationToken);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
@ -88,8 +92,8 @@ public sealed class KitchenWorkflowServiceClient(HttpClient httpClient) : IKitch
return new TransitionKitchenWorkItemResponse( return new TransitionKitchenWorkItemResponse(
payload.OrderId, payload.OrderId,
payload.TicketId, payload.TicketId,
payload.PreviousState, MapEdgeState(payload.PreviousState),
payload.CurrentState, MapEdgeState(payload.CurrentState),
payload.Transitioned, payload.Transitioned,
payload.Error); payload.Error);
} }
@ -155,7 +159,27 @@ public sealed class KitchenWorkflowServiceClient(HttpClient httpClient) : IKitch
string ClaimedBy, string ClaimedBy,
string Message); string Message);
private sealed record TransitionKitchenWorkItemPayload(string OrderId, string TicketId, string TargetState); private static string MapKitchenServiceState(string edgeState) => edgeState switch
{
"Cooking" => "Preparing",
"Ready" => "ReadyForPickup",
"Served" => "Delivered",
_ => edgeState
};
private static string MapEdgeState(string serviceState) => serviceState switch
{
"Preparing" => "Cooking",
"ReadyForPickup" => "Ready",
"Delivered" => "Served",
_ => serviceState
};
private sealed record TransitionKitchenWorkItemPayload(
string OrderId,
string TicketId,
string TargetState,
string ContextId);
private sealed record TransitionKitchenWorkItemResponsePayload( private sealed record TransitionKitchenWorkItemResponsePayload(
string OrderId, string OrderId,

View File

@ -4,4 +4,5 @@ public sealed record TransitionKitchenWorkItemRequest(
string OrderId, string OrderId,
string TicketId, string TicketId,
string TargetState, string TargetState,
string UpdatedBy); string UpdatedBy,
string? ContextId = null);

View File

@ -65,7 +65,7 @@ public sealed class KitchenWorkflowServiceClientTests
{ {
var adapter = new KitchenWorkflowServiceClient(CreateClient(""" var adapter = new KitchenWorkflowServiceClient(CreateClient("""
{ {
"orderId": "CO-1001", "orderId": "ORD-1001",
"ticketId": "KT-1001", "ticketId": "KT-1001",
"previousState": "Queued", "previousState": "Queued",
"currentState": "Preparing", "currentState": "Preparing",
@ -75,11 +75,11 @@ public sealed class KitchenWorkflowServiceClientTests
""")); """));
var response = await adapter.TransitionWorkItemAsync( var response = await adapter.TransitionWorkItemAsync(
new TransitionKitchenWorkItemRequest("CO-1001", "KT-1001", "Preparing", "chef-maya"), new TransitionKitchenWorkItemRequest("ORD-1001", "KT-1001", "Cooking", "chef-maya", "demo-context"),
CancellationToken.None); CancellationToken.None);
Assert.True(response.Transitioned); Assert.True(response.Transitioned);
Assert.Equal("Preparing", response.CurrentState); Assert.Equal("Cooking", response.CurrentState);
} }
[Fact] [Fact]
@ -131,7 +131,7 @@ public sealed class KitchenWorkflowServiceClientTests
"items": [ "items": [
{ {
"workItemId": "WK-1001", "workItemId": "WK-1001",
"orderId": "CO-1001", "orderId": "ORD-1001",
"ticketId": "KT-1001", "ticketId": "KT-1001",
"tableId": "T-08", "tableId": "T-08",
"station": "hot-line", "station": "hot-line",