Documentation
¶
Index ¶
Constants ¶
const DispatchID idutil.SafeID = "command.devicecommand.enqueue.v1"
DispatchID is the command.Registry key for command.devicecommand.enqueue.v1. Enroll handler implementations under this id via Register; Dispatch uses it to route calls to the registered Handler.
Variables ¶
This section is empty.
Functions ¶
func DispatchAsync ¶
DispatchAsync decodes a claimed command outbox.Entry, looks up the Handler registered under DispatchID, and invokes it. It mirrors Dispatch but sources the typed *Request from entry.Payload() (JSON) rather than receiving it directly — the async outbox boundary is where a command crosses from wire bytes back to a typed request (ADR §5 ④; JSON marshal happens at the outbox boundary, leaving the synchronous D4 type-assert path unchanged).
The relay drives this via Relay.WithCommandDispatch for entries whose RoutingTopic equals DispatchID; it is not called from business code. The entry's principal + observability identity is restored into ctx (entry.RestoreContext) before the handler runs, mirroring the consumer delivery path so the async handler sees the enqueuer's identity.
The handler's *Response is intentionally discarded: an async command has no in-process caller awaiting a reply, so only the error is returned for the relay to settle (ack) or retry the entry.
Request value-constraints declared in the contract request schema (minLength/maxLength/required) are NOT validated here — the typed *Request is the structural contract; untrusted-payload value validation at the async command-entry boundary is the command-entry validation funnel tracked as #1588 (ADR §5 / Amendment 2026-06-04).
Settle classification (relay) ¶
Deterministic framework errors — reg nil, routing-topic ≠ DispatchID (a mis-wired dispatcher map), payload decode failure, no handler registered, or a wrong-typed registered value — are wrapped with kout.NewPermanentError. None can recover by retry, so the relay's handleFailedEntry routes them straight to MarkDead instead of burning the retry budget (ADR §Amendment 2026-06-06 r2). The handler's own business error is returned UNWRAPPED: it is transient by default (relay → MarkRetry until the attempt budget is exhausted); a handler that knows a failure is unrecoverable can itself return a kout.PermanentError. Untrusted-payload value-validation classification stays deferred to #1588.
func Register ¶
Register wires h under DispatchID in reg. It is the sole sanctioned registration path for command.devicecommand.enqueue.v1.
Returns KindInvalid / ErrValidationFailed when reg is nil, or h is nil or typed-nil. Returns KindConflict / ErrConflict when a handler is already registered (enforces the one-to-one command→handler mapping; mirrors Watermill DuplicateCommandHandlerError and the saga registry duplicate check).
Types ¶
type Handler ¶
Handler is the typed business interface the handling cell implements for command.devicecommand.enqueue.v1. It is the sole sanctioned target type for Register and the sole type asserted by Dispatch. A hand-written typed command handler is unexpressible outside this generated package — mirroring saga's Impl / BuildDefinition pattern.
Only Register is the sanctioned registration path; calling command.Registry.RegisterHandler directly is intercepted by archtest COMMAND-DISPATCH-REGISTER-CALLER-01.
type Request ¶
type Request struct {
DeviceID string `json:"deviceId"`
CommandType string `json:"commandType,omitempty"`
Payload string `json:"payload"`
}
Request — command.devicecommand.enqueue.v1.request
type Response ¶
type Response struct {
Data *ResponseData `json:"data"`
}
Response — command.devicecommand.enqueue.v1.response
func Dispatch ¶
Dispatch looks up the Handler registered under DispatchID and invokes it.
Request value-constraints declared in the contract request schema (minLength/maxLength/required/additionalProperties) are NOT runtime-validated here. Per ADR §D4/§D6 + Amendment 2026-06-04, the request schema's role at the in-process sync boundary is to SOURCE the typed *Request signature, not to gate values at runtime: Go enforces field types, additionalProperties:false is structurally unviolatable on a typed struct, and the caller is first-party in-process code. JSON-schema value-validation belongs at the untrusted command-entry boundaries (HTTP→command, async outbox→command), tracked as a #1044 sub-issue.
Returns KindInvalid / ErrValidationFailed when reg or req is nil (miswiring / caller bug — surfaced as a structured error rather than a nil-pointer panic). Returns KindNotFound / ErrCommandNotFound when no handler has been registered. Returns KindInternal / ErrInternal when the registered value has an unexpected type (invariant violation: Register is the sole writer and always stores a Handler, so this branch is a programmer error that should never fire in production).
type ResponseData ¶
type ResponseData struct {
ID string `json:"id"`
DeviceID string `json:"deviceId"`
CommandType string `json:"commandType"`
Payload string `json:"payload"`
Status string `json:"status"`
Attempt int64 `json:"attempt"`
// format: date-time
CreatedAt string `json:"createdAt"`
// format: date-time
SentAt string `json:"sentAt,omitempty"`
// format: date-time
DeliveredAt string `json:"deliveredAt,omitempty"`
// format: date-time
CompletedAt string `json:"completedAt,omitempty"`
}
ResponseData is a generated DTO for contract command.devicecommand.enqueue.v1.