enqueue

package
v0.1.0-develop.2026060... Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 9, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Index

Constants

View Source
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

func DispatchAsync(ctx context.Context, reg *command.Registry, entry kout.Entry) error

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/additionalProperties) ARE validated here against the untrusted entry payload via requestValidator, before decode+handle — the async command-entry value funnel (#1588, ADR §Amendment 2026-06-08). This is the untrusted-boundary counterpart to HTTP request-body validation; the synchronous Dispatch path stays unvalidated as a first-party typed boundary (§D8). entry.Payload() is already the inbound JSON, so validating it adds no marshal round-trip — D4 (sync zero-serialization fast-path) is preserved.

Settle classification (relay)

Deterministic framework errors — reg nil, routing-topic ≠ DispatchID (a mis-wired dispatcher map), request-schema value-validation failure, 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 / 2026-06-08). 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.

func Register

func Register(reg *command.Registry, h Handler) error

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

type Handler interface {
	HandleEnqueue(ctx context.Context, req *Request) (*Response, error)
}

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

func Dispatch(ctx context.Context, reg *command.Registry, req *Request) (*Response, error)

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: async outbox→command is validated in DispatchAsync (the #1588 value funnel, ADR §Amendment 2026-06-08); HTTP→command (when wired) validates the request body in the generated HTTP handler before calling Dispatch.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL