# ADR — Vendor Source of Truth **Status:** Accepted (Fase 0 spike) **Decisores:** Backend + Arquiteto --- ## Contexto O board SHOC exibe colunas VENDOR e APPT na listagem semanal. Hoje vendor existe apenas no agregado Dispatch; a entidade WorkOrder não possui referência ao dispatch primário. --- ## Decisão 1. **WorkOrder.PrimaryDispatchId** (nullable FK → Dispatches) identifica o dispatch canônico para projeção board. 2. Colunas VENDOR/APPT = **projeção read-only** via join no dispatch primário (Vendor.Name, Dispatch.ScheduledDate). 3. Edição inline vendor (Fase 2) muta **Dispatch**, não WorkOrder. Bump `Dispatch.RowVersion`. 4. WO sem dispatch: primeira edição vendor cria dispatch primário e seta `PrimaryDispatchId`. 5. **Proibido** adicionar VendorId, TechName, TechPhone na tabela workOrders. --- ## Query de projeção (spike) ```sql SELECT wo.Id, wo.InternalWONumber, d.Id AS DispatchId, v.Name AS VendorName, d.ScheduledDate AS ApptDate, d.Status AS DispatchStatus FROM workOrders wo LEFT JOIN Dispatches d ON d.Id = wo.PrimaryDispatchId LEFT JOIN Vendors v ON v.Id = d.VendorId WHERE wo.IsDeleted IS NULL OR wo.IsDeleted = 0; ``` Implementação C# em `DispatchDataService.GetPrimaryDispatchProjectionAsync(workOrderId)` (Fase 1). --- ## Seleção do dispatch primário | Cenário | Regra | |---------|-------| | PrimaryDispatchId setado | Usar esse dispatch | | Null + exatamente 1 dispatch | Auto-set PrimaryDispatchId na migration backfill | | Null + N dispatches | Usar dispatch mais recente por DispatchedAt; log para revisão manual | | Zero dispatches | Vendor columns vazias no board | --- ## Consequências - **Positivo:** Zero drift vendor WO vs Dispatch; Portal regression isolada. - **Negativo:** Join extra na listagem board — mitigado por índice em PrimaryDispatchId. - **Fase 0:** Coluna + FK + backfill script no dry-run; endpoint board na Fase 1. --- ## Critérios de aceite spike - [x] ADR documentado - [x] Query prototipada - [ ] PrimaryDispatchId na migration Phase0 - [ ] Backfill documentado no dry-run report