From 771304eabe3fc156cd65643a0196f35fd74ce0c2 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 11 May 2026 13:10:03 +0800 Subject: [PATCH] docs: add eventual-consistency annotations D-03: document non-transactional boundaries. - Comment in platform_webhook_handler.go explaining that dialog.Process and outbox.InsertPendingBatch are not in a single transaction; 500 is returned on outbox failure for caller retry. - Package-level comment in dialog/service.go noting the lack of a unified transactional outer box and the eventually-consistent nature of storage operations. --- internal/http/handlers/platform_webhook_handler.go | 7 +++++++ internal/service/dialog/service.go | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/internal/http/handlers/platform_webhook_handler.go b/internal/http/handlers/platform_webhook_handler.go index 356efdc..0544050 100644 --- a/internal/http/handlers/platform_webhook_handler.go +++ b/internal/http/handlers/platform_webhook_handler.go @@ -88,6 +88,13 @@ func (h *PlatformWebhookHandler) Handle(w http.ResponseWriter, r *http.Request) writeJSON(w, http.StatusInternalServerError, map[string]any{"error": map[string]any{"code": cserrors.CS_SYS_5001, "message": cserrors.ErrorMsg(cserrors.CS_SYS_5001)}}) return } + // NOTE: Final-consistency boundary. + // dialog.Process and outbox.InsertPendingBatch are NOT enclosed in a single + // transaction. If outbox insertion fails after dialog succeeds, the business + // state (Session/Ticket/Audit) is already persisted but no downstream event + // is emitted. In that case we return HTTP 500 so the platform caller can + // retry; DedupRepository.TryRecord inside dialog.Process guards against + // duplicate business-state mutations on retry. if h.eventWriter == nil { writeJSON(w, http.StatusInternalServerError, map[string]any{"error": map[string]any{"code": cserrors.CS_SYS_5001, "message": cserrors.ErrorMsg(cserrors.CS_SYS_5001)}}) return diff --git a/internal/service/dialog/service.go b/internal/service/dialog/service.go index 7b24dd2..cb6f195 100644 --- a/internal/service/dialog/service.go +++ b/internal/service/dialog/service.go @@ -1,3 +1,14 @@ +// Package dialog implements the core message-processing service. +// +// NOTE on transaction boundaries: the current repository layer does not +// provide a unified transactional outer box (UnitOfWork / sql.Tx). Each +// repository call (SessionRepository, DedupRepository, TicketRepository, +// AuditRepository) executes on its own connection. Therefore the operations +// inside Process are eventually-consistent rather than strictly atomic. +// +// A future refactor introducing UnitOfWork or TxProvider could lift this +// limitation and wrap the entire flow plus outbox insertion in a single +// transaction. package dialog import (