Documentation
¶
Overview ¶
Package commands executes explicitly registered, model-bound Arc commands. Register composes typed adapters without reflective invocation or a required container. Build freezes metadata and extensions; Execute owns resources and completion, while Validate runs authorization and input validation only. Failed results never publish a response. Cleanup is not proof of rollback.
Index ¶
- Constants
- Variables
- func FrameState[T any](ctx context.Context, inv *Invocation, key StateKey[T]) (T, bool, error)
- func ReadModelOrNil[M any](ctx context.Context, inv *Invocation) (*M, error)
- func Register[C any](r Registrar, options ...Option[C]) error
- func RegisterOperation[O ExecutableOperation[D], D any](r *Registry, dependencies Factory[D], keys ...di.Key) error
- func RegisterReadModelProvider[M any](r *Registry, name string, ownership ReadModelOwnership, ...) error
- func RegisterResponseValueHandler[T any](r *Registry, name string, factory Factory[ResponseValueHandler], ...) error
- func ReportCommit(ctx context.Context, inv *Invocation, report CompletionReport) error
- func RequireReadModel[M any](ctx context.Context, inv *Invocation) (M, error)
- func RootState[T any](ctx context.Context, inv *Invocation, key StateKey[T]) (T, bool, error)
- func SetFrameState[T any](ctx context.Context, inv *Invocation, key StateKey[T], value T) error
- func SetRootState[T any](ctx context.Context, inv *Invocation, key StateKey[T], value T) error
- func VerifyDecision(ctx context.Context, inv *Invocation, read *DecisionRead) (err error)
- type AuthorizationFilter
- type BuildView
- type CommandContext
- func (c CommandContext) AllowedSeverity() (validation.Severity, bool)
- func (c CommandContext) Command() any
- func (c CommandContext) CorrelationID() correlation.ID
- func (c CommandContext) Descriptor() metadata.Command
- func (c CommandContext) IsValidationOnly() bool
- func (c CommandContext) Principal() identity.Principal
- func (c CommandContext) ReceivedAt() time.Time
- func (c CommandContext) ResolvedKey() (string, bool)
- func (c CommandContext) Response() (any, bool)
- func (c CommandContext) Tenant() tenancy.ID
- func (c CommandContext) Values() ContextValues
- type CommitDisposition
- type CompensatingOperation
- type CompletionError
- type CompletionReport
- type ContextValues
- type ContextValuesProvider
- type DecisionEvidence
- type DecisionProfile
- type DecisionProvider
- type DecisionRead
- type DecisionSource
- type DecisionTarget
- type DecodeError
- type DeferredCommitParticipant
- type Details
- type ExecutableOperation
- type ExecuteOptions
- type Execution
- type ExecutionScope
- type Factory
- type Filter
- type FilterFunc
- type Handler
- func Handle[C, O any](call func(C, context.Context) (O, error)) Handler[C, O]
- func Invoke[C, O any](call func(context.Context, *Invocation, C) (O, error)) Handler[C, O]
- func Prepare[C, P, O any](provide func(context.Context, *Invocation, C) (Preparation[P], error), ...) Handler[C, O]
- func Scoped[C, O any, S execution.Resources](factory func(context.Context, S) (Handler[C, O], error)) Handler[C, O]
- func Void[C any](call func(C, context.Context) error) Handler[C, NoResponse]
- func WithPreparation[C, P, O any](provide func(C, context.Context) (Preparation[P], error), ...) Handler[C, O]
- func WithProvide[C, P, O any](provide func(C, context.Context) (P, error), ...) Handler[C, O]
- type Invocation
- func (i *Invocation) CommandContext() CommandContext
- func (i *Invocation) Execution() *Execution
- func (i *Invocation) ParentCommandContext(ctx context.Context) (value CommandContext, present bool, err error)
- func (i *Invocation) Pipeline() Pipeline
- func (i *Invocation) Scope() *execution.Scope
- func (i *Invocation) SetValue(name string, value any) error
- type KeyProvider
- type KeyResolver
- type NoResponse
- type Operation
- type OperationCommitParticipant
- type OperationCompensation
- type OperationFailure
- type OperationFailureSource
- type OperationOptions
- type OperationOutcome
- type Operations
- type Option
- func WithAuthorization[C any](a metadata.Authorization) Option[C]
- func WithBlockOnValidationSeverity[C any](s validation.Severity) Option[C]
- func WithDescriptor[C any](d metadata.Command) Option[C]
- func WithExcludeFromDiscovery[C any](exclude bool) Option[C]
- func WithHandlingDependencies[C any](keys ...di.Key) Option[C]
- func WithKey[C any](key func(C) (string, bool)) Option[C]
- func WithName[C any](name string) Option[C]
- func WithNamespace[C any](s string) Option[C]
- func WithNoResponse[C any]() Option[C]
- func WithOperations[C any]() Option[C]
- func WithPath[C any](s string) Option[C]
- func WithPreparationDependencies[C any](keys ...di.Key) Option[C]
- func WithProtectedDecisions[C any]() Option[C]
- func WithResponseType[C, R any]() Option[C]
- func WithScopedValidator[C any](factory Factory[validation.Validator[C]], keys ...di.Key) Option[C]
- func WithUnprotectedDecisions[C any]() Option[C]
- func WithValidator[C any](v validation.Validator[C]) Option[C]
- func WithoutModelValidation[C any]() Option[C]
- type Outcome
- type Pipeline
- type PipelineOptions
- type Preparation
- type ReadModelInstance
- type ReadModelOwnership
- type RecoveryStatus
- type RecoverySummary
- type Registrar
- type Registration
- func (r Registration) CommandType() reflect.Type
- func (r Registration) DecisionProfile() DecisionProfile
- func (r Registration) Decode(body []byte) (any, error)
- func (r Registration) Descriptor() metadata.Command
- func (r Registration) ResponseKind() ResponseKind
- func (r Registration) ResponseType() (reflect.Type, bool)
- func (r Registration) ReturnType() reflect.Type
- type RegistrationError
- type Registry
- func (r *Registry) AddAuthorizationFilter(name string, factory Factory[AuthorizationFilter], keys ...di.Key) error
- func (r *Registry) AddBuildCheck(check func(BuildView) error) error
- func (r *Registry) AddContextValuesProvider(name string, factory Factory[ContextValuesProvider], keys ...di.Key) error
- func (r *Registry) AddDecisionProvider(provider *DecisionProvider) error
- func (r *Registry) AddDeferredCommitParticipant(name string, factory Factory[DeferredCommitParticipant], keys ...di.Key) error
- func (r *Registry) AddExecutionScope(name string, factory Factory[ExecutionScope], keys ...di.Key) error
- func (r *Registry) AddFilter(name string, factory Factory[Filter], keys ...di.Key) error
- func (r *Registry) AddKeyResolver(name string, factory Factory[KeyResolver], keys ...di.Key) error
- func (r *Registry) AddOperationCommitParticipant(name string, factory Factory[OperationCommitParticipant], keys ...di.Key) error
- func (r *Registry) AddOperationExecutionScope(name string, factory Factory[ExecutionScope], keys ...di.Key) error
- func (r *Registry) AddResponseValueHandler(name string, factory Factory[ResponseValueHandler], keys ...di.Key) error
- func (r *Registry) AddReturnAdmission(name string, admission ReturnAdmission) error
- func (r *Registry) Build(options PipelineOptions) (Pipeline, error)
- func (r *Registry) Catalog() metadata.Catalog
- func (r *Registry) ContainsCommandType(typ reflect.Type) bool
- func (r *Registry) RegisterCommand(value Registration) error
- type RegistryOptions
- type ResponseKind
- type ResponseValueContextUpdater
- type ResponseValueHandler
- type Result
- func Execute[R any](ctx context.Context, p Pipeline, command any, options ...ExecuteOptions) (output Result[R], outputErr error)
- func FromError[R any](id correlation.ID, err error) Result[R]
- func InvalidBody(id correlation.ID) Result[NoResponse]
- func Merge[R any](result Result[R], fragments ...Result[NoResponse]) Result[R]
- func NewResult[R any](details Details, response serialization.Optional[R]) Result[R]
- func Success(id concepts.UUID) Result[NoResponse]
- func Unauthorized(id correlation.ID, reason string) Result[NoResponse]
- func WithResponse[R any](id concepts.UUID, response R) Result[R]
- func WithValidationResults(id correlation.ID, findings ...validation.Result) Result[NoResponse]
- func (r Result[R]) Completion() CompletionReport
- func (r Result[R]) Details() Details
- func (r Result[R]) HasExceptions() bool
- func (r Result[R]) IsAuthorized() bool
- func (r Result[R]) IsSuccess() bool
- func (r Result[R]) IsValid() bool
- func (r Result[R]) MarshalJSON() ([]byte, error)
- func (r Result[R]) MarshalJSONWith(encode func(any) ([]byte, error)) (body []byte, err error)
- func (r Result[R]) OperationOutcomes() []OperationOutcome
- func (r Result[R]) Recovery() (RecoverySummary, bool)
- func (r Result[R]) Response() (R, bool)
- func (r Result[R]) StatusCode() int
- type ReturnAdmission
- type ReturnClassification
- type StateKey
Examples ¶
Constants ¶
const ResolvedKey = "resolvedKey"
ResolvedKey is the provider-neutral command key entry.
Variables ¶
var ( // ErrInvalidRegistration identifies invalid configuration or input. ErrInvalidRegistration = errors.New("invalid command registration") // ErrDuplicate identifies duplicate types, identities, options or extensions. ErrDuplicate = errors.New("duplicate command registration") // ErrFrozen identifies mutation after a successful Build. ErrFrozen = errors.New("command registry frozen") // ErrMissingHandler identifies an absent supported handler. ErrMissingHandler = errors.New("command handler missing") // ErrResponseType identifies an incompatible or unknown response contract. ErrResponseType = errors.New("incompatible command response type") // ErrMultipleResponses identifies ambiguous unconsumed return values. ErrMultipleResponses = errors.New("multiple command responses") // ErrCommitNotConfirmed identifies a completion that cannot report successful persistence. ErrCommitNotConfirmed = errors.New("command commit not confirmed") // ErrNilReturn identifies a typed nil claimed as a server-consumed return. ErrNilReturn = errors.New("nil server-consumed command return") // ErrReadModelProvider identifies a command dependency without a registered provider. ErrReadModelProvider = errors.New("command read-model provider missing") // ErrUnhandledEffect identifies an effect without a consumer. ErrUnhandledEffect = errors.New("unhandled command effect") // ErrInvalidPreparation identifies a zero preparation or missing payload. ErrInvalidPreparation = errors.New("invalid command preparation") // ErrNoContext identifies a missing command callback context. ErrNoContext = errors.New("command context unavailable") // ErrExecutionClosed identifies an expired executor or invocation. ErrExecutionClosed = errors.New("command execution closed") // ErrConcurrentExecution identifies overlapping use of a bound executor. ErrConcurrentExecution = errors.New("concurrent command execution") // ErrExecutionFailed identifies a recorded failure in this command or an ancestor. ErrExecutionFailed = errors.New("command execution has recorded failures") // ErrExecutionMismatch identifies an incompatible frame or validation-only use. ErrExecutionMismatch = errors.New("command execution mismatch") )
var ErrDecisionProfile = errors.New("arc: protected decision profile refused")
ErrDecisionProfile identifies a command, registry or invocation that does not satisfy the protected decision-read profile. It is returned by Register, Build and, joined with ErrDecisionRead, by ReadDecision.
var ErrDecisionRead = errors.New("arc: decision read refused")
ErrDecisionRead identifies a refused decision read: an inadmissible profile, an uncertified provider, a failed provider stage, or a read that was not issued to the current invocation. The original cause is retained for errors.Is/As. It is always an infrastructure failure, never an advisory validation finding, so no allowed validation severity can turn a refused read into success.
var ErrInvalidOperation = errors.New("invalid command operation")
ErrInvalidOperation identifies an unsupported declaration or execution boundary.
Functions ¶
func FrameState ¶
FrameState reads a slot shared across one command's callbacks, never siblings or children. Membership ends with that command. Values follow RootState ownership.
func ReadModelOrNil ¶
func ReadModelOrNil[M any](ctx context.Context, inv *Invocation) (*M, error)
ReadModelOrNil returns nil only for a valid key with no instance. The returned pointer owns a shallow value copy; reference-valued model fields remain borrowed.
func Register ¶
Register composes one handler and ordered typed options. Without a handler it supports exactly Handle(context.Context) error through Go's method set.
Example ¶
package main
import (
"context"
"fmt"
"github.com/cratis/arc.go/commands"
)
type ClearLocalCache struct{}
func (ClearLocalCache) Handle(context.Context) error { return nil }
func main() {
var registry commands.Registry
if err := commands.Register[ClearLocalCache](®istry); err != nil {
panic(err)
}
pipeline, err := registry.Build(commands.PipelineOptions{})
if err != nil {
panic(err)
}
result, err := pipeline.Execute(context.Background(), ClearLocalCache{})
if err != nil {
panic(err)
}
_, present := result.Response()
fmt.Println(result.IsSuccess(), present)
}
Output: true false
func RegisterOperation ¶
func RegisterOperation[O ExecutableOperation[D], D any](r *Registry, dependencies Factory[D], keys ...di.Key) error
RegisterOperation registers exact O without activating services. Pointer/value types are distinct. dependencies resolves the COMPLETE execution/compensation bundle before any consumer or operation runs. Nil bundles are invalid. Empty struct bundles support no-dependency operations; a factory is still required. Optional DI keys are copied and checked against the existing catalog at Build. Business methods are invoked through typed calls, never reflection. A present but malformed Compensate method is a registration error, not an absent reversal.
Example ¶
package main
import (
"context"
"fmt"
"github.com/cratis/arc.go/commands"
"github.com/cratis/arc.go/execution"
)
type seatReservations interface {
Reserve(context.Context, string, string) error
CancelOwned(context.Context, string) error
}
type reserveSeat struct{ ReservationID, SeatID string }
func (reserveSeat) CommandOperation() {}
func (o reserveSeat) Execute(ctx context.Context, service seatReservations) error {
return service.Reserve(ctx, o.ReservationID, o.SeatID)
}
func (o reserveSeat) Compensate(ctx context.Context, service seatReservations, _ commands.OperationFailure) error {
return service.CancelOwned(ctx, o.ReservationID)
}
type bookSeat struct{ ReservationID, SeatID string }
func (c bookSeat) Handle(context.Context) (commands.Outcome[string], error) {
return commands.Respond(c.ReservationID, reserveSeat(c)), nil
}
type reservationService struct{ calls int }
func (s *reservationService) Reserve(context.Context, string, string) error { s.calls++; return nil }
func (*reservationService) CancelOwned(context.Context, string) error { return nil }
func main() {
service := &reservationService{} // Application-owned, borrowed by this pipeline.
var registry commands.Registry
if err := commands.RegisterOperation[reserveSeat, seatReservations](®istry,
func(context.Context, *execution.Scope) (seatReservations, error) { return service, nil }); err != nil {
panic(err)
}
if err := commands.Register[bookSeat](®istry, commands.Handle(bookSeat.Handle), commands.WithOperations[bookSeat]()); err != nil {
panic(err)
}
pipeline, err := registry.Build(commands.PipelineOptions{})
if err != nil {
panic(err)
}
command := bookSeat{"reservation-1", "seat-2"}
_, _ = command.Handle(context.Background()) // Declares work only.
fmt.Println("before pipeline:", service.calls)
result, err := commands.Execute[string](context.Background(), pipeline, command)
if err != nil {
panic(err)
}
response, _ := result.Response()
recovery, _ := result.Recovery()
fmt.Println(response, result.IsSuccess(), service.calls, recovery.StartedCount)
}
Output: before pipeline: 0 reservation-1 true 1 1
func RegisterReadModelProvider ¶
func RegisterReadModelProvider[M any](r *Registry, name string, ownership ReadModelOwnership, resolve func(context.Context, *Invocation, string) (ReadModelInstance[M], error)) error
RegisterReadModelProvider binds the exact non-pointer struct M for command dependencies, not public queries. Declared owners beat fallbacks; explicit overrides beat both. Duplicate claims at any one level fail even if overridden. The resolver is borrowed and must support concurrent independent executions. It may use FrameState for provider-specific caches; the core does not cache or infer coordinates/read modes. ResolveReadModel always validates the command key.
Example ¶
package main
import (
"context"
"fmt"
"github.com/cratis/arc.go/commands"
)
type commandModel struct{ Name string }
type keyedCommand struct {
ID string `arc:"key"`
}
func main() {
var registry commands.Registry
if err := commands.RegisterReadModelProvider[commandModel](®istry, "authors", commands.DeclaredReadModel,
func(ctx context.Context, inv *commands.Invocation, key string) (commands.ReadModelInstance[commandModel], error) {
return commands.ReadModelInstance[commandModel]{Value: commandModel{Name: "Ada"}, Exists: key == "ada"}, nil
}); err != nil {
panic(err)
}
if err := commands.Register[keyedCommand](®istry, commands.Invoke(func(ctx context.Context, inv *commands.Invocation, _ keyedCommand) (string, error) {
model, err := commands.RequireReadModel[commandModel](ctx, inv)
return model.Name, err
})); err != nil {
panic(err)
}
pipeline, err := registry.Build(commands.PipelineOptions{})
if err != nil {
panic(err)
}
result, err := commands.Execute[string](context.Background(), pipeline, keyedCommand{ID: "ada"})
if err != nil {
panic(err)
}
name, _ := result.Response()
fmt.Println(name)
}
Output: Ada
func RegisterResponseValueHandler ¶
func RegisterResponseValueHandler[T any](r *Registry, name string, factory Factory[ResponseValueHandler], keys ...di.Key) error
RegisterResponseValueHandler limits a consumer to exact concrete T or values implementing interface T, retaining its dynamic predicate. The marker does not establish unconditional no-response metadata.
func ReportCommit ¶
func ReportCommit(ctx context.Context, inv *Invocation, report CompletionReport) error
ReportCommit retains an early persistence observation on the command owner. It checks callback/security continuity and refuses validation-only execution. Later failures or no-work completion cannot erase confirmed/unknown outcomes. This records facts only; it neither commits nor grants commit authority.
func RequireReadModel ¶
func RequireReadModel[M any](ctx context.Context, inv *Invocation) (M, error)
RequireReadModel resolves M and reports absent instances as dependencyUnavailable. It never manufactures a successful zero-valued dependency after a failed read.
func RootState ¶
RootState reads a slot shared only by this root's bound nested command tree. Values are borrowed, not cloned: their owner must guard mutable contents and recheck Invocation.Execution before using a retained capability. Membership is discarded at execution end. Every access checks callback/security continuity.
func SetFrameState ¶
SetFrameState replaces a frame-local slot after checking callback admission.
func SetRootState ¶
SetRootState replaces a root slot. No initializer or user callback runs under the state lock. It does not confer completion authority on an operation scope.
func VerifyDecision ¶
func VerifyDecision(ctx context.Context, inv *Invocation, read *DecisionRead) (err error)
VerifyDecision refuses a nil, zero, foreign or expired read: one not issued to this command frame, issued to another command or a validation-only run, or whose evidence the provider no longer accepts. It re-checks callback and security continuity before and after the provider check. Checking only a provider token's type or progress does not prove provenance. Every failure wraps ErrDecisionRead.
Types ¶
type AuthorizationFilter ¶
type AuthorizationFilter interface {
Filter
IsAuthorizationFilter()
}
AuthorizationFilter runs ahead of ordinary filters, never instead of declarations.
func AsAuthorizationFilter ¶
func AsAuthorizationFilter(filter Filter) AuthorizationFilter
AsAuthorizationFilter categorizes a filter without activating it.
type BuildView ¶
type BuildView struct {
// contains filtered or unexported fields
}
BuildView is an immutable snapshot of exact registered command types. Its zero value contains no commands; it is safe to retain and read concurrently.
type CommandContext ¶
type CommandContext struct {
// contains filtered or unexported fields
}
CommandContext is an immutable metadata snapshot. Command, response and value objects remain borrowed; callers must not mutate or retain them beyond execution.
func ContextFrom ¶
func ContextFrom(ctx context.Context) (CommandContext, bool)
ContextFrom reads the snapshot installed for the current application callback.
func (CommandContext) AllowedSeverity ¶
func (c CommandContext) AllowedSeverity() (validation.Severity, bool)
AllowedSeverity returns the explicit caller allowance, if supplied.
func (CommandContext) Command ¶
func (c CommandContext) Command() any
Command returns the decoded command receiver.
func (CommandContext) CorrelationID ¶
func (c CommandContext) CorrelationID() correlation.ID
CorrelationID returns the inherited or generated correlation.
func (CommandContext) Descriptor ¶
func (c CommandContext) Descriptor() metadata.Command
Descriptor returns a copy-isolated descriptor.
func (CommandContext) IsValidationOnly ¶
func (c CommandContext) IsValidationOnly() bool
IsValidationOnly distinguishes advisory validation from execution.
func (CommandContext) Principal ¶
func (c CommandContext) Principal() identity.Principal
Principal returns immutable identity metadata.
func (CommandContext) ReceivedAt ¶
func (c CommandContext) ReceivedAt() time.Time
ReceivedAt returns this frame's distinct UTC receipt.
func (CommandContext) ResolvedKey ¶
func (c CommandContext) ResolvedKey() (string, bool)
ResolvedKey preserves an authoritative empty key's presence.
func (CommandContext) Response ¶
func (c CommandContext) Response() (any, bool)
Response returns the selected response and its presence.
func (CommandContext) Tenant ¶
func (c CommandContext) Tenant() tenancy.ID
Tenant returns the selected tenant, not evidence of membership.
func (CommandContext) Values ¶
func (c CommandContext) Values() ContextValues
Values returns copied case-insensitive membership.
type CommitDisposition ¶
type CommitDisposition uint8
CommitDisposition describes observed persistence, not command success. A failed command can have committed. No disposition authorizes an automatic retry.
const ( // NoPersistedWork means no events/writes persisted (including successful checks only). NoPersistedWork CommitDisposition = iota // NotCommitted means the provider established that work did not commit. NotCommitted // Committed means the provider confirmed persistence. Committed // OutcomeUnknown requires reconciliation; persistence may have occurred. OutcomeUnknown // MixedCommit means observations include both confirmed persistence and other outcomes. MixedCommit )
type CompensatingOperation ¶
type CompensatingOperation[D any] interface { Compensate(context.Context, D, OperationFailure) error }
CompensatingOperation optionally reverses attempted work. D must contain every execution AND compensation dependency; registration preflights the whole bundle. Providers own partial-write, lost-acknowledgment, isolation and idempotency safety.
type CompletionError ¶
type CompletionError struct {
// Report records persistence independently of Cause.
Report CompletionReport
// Cause is the original failure, possibly a joined error.
Cause error
}
CompletionError preserves a failed operation's cause and persistence report, including failures after confirmed persistence. Unwrap retains errors.Is/As.
func (*CompletionError) Error ¶
func (e *CompletionError) Error() string
Error describes the failure without claiming a rollback.
func (*CompletionError) Unwrap ¶
func (e *CompletionError) Unwrap() error
Unwrap exposes the original error.
type CompletionReport ¶
type CompletionReport struct {
// Disposition is the provider's observed outcome, never inferred from IsSuccess.
Disposition CommitDisposition
}
CompletionReport is server-side evidence retained independently of the HTTP envelope and command success. Zero means no persisted work. MixedCommit can represent immediate and deferred effects; Arc does not coordinate providers.
type ContextValues ¶
type ContextValues struct {
// contains filtered or unexported fields
}
ContextValues owns copied, case-insensitive membership; contained values remain borrowed.
func NewContextValues ¶
func NewContextValues(entries map[string]any) (ContextValues, error)
NewContextValues copies membership, rejecting empty or case-folded duplicate keys.
func (ContextValues) Entries ¶
func (v ContextValues) Entries() map[string]any
Entries returns copied membership, with normalized lower-case keys.
type ContextValuesProvider ¶
type ContextValuesProvider interface {
Provide(context.Context, *Invocation) (ContextValues, error)
}
ContextValuesProvider supplies trusted preauthorization metadata. Later providers overwrite earlier membership. Factories must be cheap and honor cancellation.
type DecisionEvidence ¶
type DecisionEvidence interface{ DecisionReads() []*DecisionRead }
DecisionEvidence is implemented by values that carry issued decision reads. A protected command's Provide payload that implements it is verified before Handle runs; *DecisionRead implements it. Arc does not inspect other payload shapes, so a provider-typed decision value should implement this interface. A protected command's evidence must carry at least one issued read. An unmarked or unprotected command may return evidence that carries no reads as an advisory snapshot; any read it does carry is refused, because it was never issued.
type DecisionProfile ¶
type DecisionProfile uint8
DecisionProfile is a command's frozen decision-read profile. The zero value is DecisionsUnmarked, so existing commands keep their behavior.
const ( // DecisionsUnmarked is the default profile. ReadDecision refuses; a provider // should also refuse its decision-read API, as C# Arc does for unmarked // commands, while ordinary read-model injection stays advisory. DecisionsUnmarked DecisionProfile = iota // DecisionsProtected admits ReadDecision: reads are invocation-owned, enrolled // with the provider's completion owner and verified before Handle. DecisionsProtected // DecisionsUnprotected acknowledges advisory, unguarded reads. ReadDecision // refuses; a provider serves its detached snapshot instead. DecisionsUnprotected )
func CurrentDecisionProfile ¶
func CurrentDecisionProfile(ctx context.Context, inv *Invocation) (profile DecisionProfile, err error)
CurrentDecisionProfile reports the decision profile of the command that owns this invocation, after checking callback and security continuity. Providers use it to refuse decision reads for unmarked commands and to serve advisory snapshots for unprotected ones.
type DecisionProvider ¶
type DecisionProvider struct {
// contains filtered or unexported fields
}
DecisionProvider is the opaque identity of one decision-read provider, such as one Chronicle client. Construct it with NewDecisionProvider; the zero value and nil are invalid. Identity is by pointer: two providers never share reads even when their store and namespace names match. It carries no capability itself; it is certified only for a registry it was added to.
func NewDecisionProvider ¶
func NewDecisionProvider() *DecisionProvider
NewDecisionProvider allocates an independent provider identity.
type DecisionRead ¶
type DecisionRead struct {
// contains filtered or unexported fields
}
DecisionRead is an issued read. Only ReadDecision issues one; a zero or copied value is never accepted. It is borrowed, invocation-owned evidence: verify it with VerifyDecision before relying on it in another callback.
func ReadDecision ¶
func ReadDecision(ctx context.Context, inv *Invocation, target DecisionTarget, source DecisionSource) (read *DecisionRead, err error)
ReadDecision issues a decision read for target within a protected command's invocation. It refuses, before any provider stage, an unmarked or unprotected command, an uncertified provider or a missing completion owner.
One acquisition is shared per command frame and target, including concurrent callers; the first caller's context owns it and canceled waiters do not cancel it. Results, including failures, are cached for the frame and never retried. Every call re-checks the evidence and, outside validation-only execution, re-enrolls it with source's current owner. Validation-only execution uses a separate cache and never enrolls. Every failure wraps ErrDecisionRead.
func (*DecisionRead) DecisionReads ¶
func (r *DecisionRead) DecisionReads() []*DecisionRead
DecisionReads implements DecisionEvidence. A nil read yields one nil entry, which verification refuses.
func (*DecisionRead) Value ¶
func (r *DecisionRead) Value() any
Value returns the provider's opaque evidence. It is not a proof by itself.
type DecisionSource ¶
type DecisionSource interface {
Admit(context.Context) error
Acquire(context.Context) (any, error)
Check(context.Context, any) error
Enroll(context.Context, any) error
}
DecisionSource is the provider-neutral seam a decision-read provider supplies for one target. Arc calls the stages in order, re-checking callback and security continuity between them, and never holds a lock while calling them. A stage that panics is converted to an error.
Admit refuses unsupported models (for example classified ones) before any acquisition. Acquire returns the provider's opaque read evidence; it must not be nil and must not be manufactured by the application. Check re-validates that evidence (lifetime, target) on every issue and verification. Enroll registers the evidence with the provider's current completion owner so a competing write rejects the whole batch; Arc calls it for every protected issue, including cached ones, and never in validation-only execution.
type DecisionTarget ¶
type DecisionTarget struct {
// Provider must be certified with Registry.AddDecisionProvider.
Provider *DecisionProvider
// Model is the read-model type.
Model reflect.Type
// Store, Namespace and Key select the instance within the provider.
Store, Namespace, Key string
}
DecisionTarget identifies one read. Every field takes part in the invocation cache key, so the same model and key from another provider, store or namespace is a separate read. All fields are required.
type DecodeError ¶
type DecodeError struct{ Cause error }
DecodeError exposes parser causes locally while publishing only safe findings.
func (*DecodeError) Error ¶
func (e *DecodeError) Error() string
Error returns safe malformed-input text.
func (*DecodeError) Unwrap ¶
func (e *DecodeError) Unwrap() error
Unwrap retains inspectable parser/conversion errors.
func (*DecodeError) ValidationResults ¶
func (e *DecodeError) ValidationResults() []validation.Result
ValidationResults returns one safe malformedRequest finding.
type DeferredCommitParticipant ¶
type DeferredCommitParticipant interface {
Begin(context.Context, *Invocation) error
Complete(context.Context, *Invocation, Result[any]) (CompletionReport, error)
}
DeferredCommitParticipant reserves ownership before ordinary ExecutionScope Begin callbacks. Complete runs once, after all ordinary Complete callbacks and before resource disposal. It receives their merged outcome and a live bounded cleanup context, even after a failed Begin. Validate never activates it.
Begin must not connect or activate tenant resources before authorization; use lazy binding in the provider. Complete must roll back open work on failure, report the provider's actual disposition even when returning an error, and never retry an ambiguous commit. Domain rejections use validation-bearing errors. Use ReportCommit for early completion so later callback failures retain evidence.
type Details ¶
type Details struct {
// CorrelationID is echoed unchanged; transport normalization happens separately.
CorrelationID concepts.UUID
// Completion is server-side persistence evidence, never serialized in the envelope.
Completion CompletionReport `json:"-"`
// Authorized states the final authorization decision.
Authorized bool
// ValidationResults are retained findings after severity filtering.
ValidationResults []validation.Result
// ExceptionMessages are client-safe messages; nonempty means exception failure.
ExceptionMessages []string
// ExceptionStackTrace is empty outside explicit development exposure.
ExceptionStackTrace string
// AuthorizationFailureReason is safe client-visible denial feedback.
AuthorizationFailureReason string
// contains filtered or unexported fields
}
Details configures a finalized result. Zero denies authorization. Use Success or WithResponse for success. Arrays are copied; validation State remains borrowed.
type ExecutableOperation ¶
ExecutableOperation declares one typed, synchronous, error-only business call. Dependencies must be usable concurrently across commands. Arc never retries it.
type ExecuteOptions ¶
type ExecuteOptions struct{ AllowedSeverity *validation.Severity }
ExecuteOptions supplies zero or one caller allowance; command floors still apply.
type Execution ¶
type Execution struct {
// contains filtered or unexported fields
}
Execution is a callback-scoped view of synchronous command ownership. It is not a transaction or commit verdict. It launches no goroutines and rejects expiry.
func (*Execution) CheckExplicitCommit ¶
CheckExplicitCommit refuses early persistence in an operation-capable frame. Refusal is sticky even when ignored. It observes existing callback/security ownership; it grants neither authorization nor commit authority otherwise.
func (*Execution) CheckRecordedFailures ¶
CheckRecordedFailures checks callback/security continuity and fails if this frame or an ancestor has already recorded a failure, including an ignored nested Execute result. Advisory nested Validate remains advisory during Execute. This read-only guard is useful before explicit early persistence. It is not an authorization grant or a promise that subsequent callbacks will succeed.
type ExecutionScope ¶
type ExecutionScope interface {
Begin(context.Context, *Invocation) error
Complete(context.Context, *Invocation, Result[any]) (Result[NoResponse], error)
}
ExecutionScope participates once per root Execute. Begin runs in registration order before authorization; Complete runs in reverse order for every entered Begin, including a failing Begin. Completion uses a live bounded cleanup context but observes the already-failed forward result. This is not a rollback contract.
type Filter ¶
type Filter interface {
OnExecution(context.Context, *Invocation) (Result[NoResponse], error)
}
Filter contributes a verdict after mandatory authorization. Zero fragments deny.
type FilterFunc ¶
type FilterFunc func(context.Context, *Invocation) (Result[NoResponse], error)
FilterFunc adapts a synchronous filter callback.
func (FilterFunc) OnExecution ¶
func (f FilterFunc) OnExecution(ctx context.Context, inv *Invocation) (Result[NoResponse], error)
OnExecution calls f.
type Handler ¶
type Handler[C, O any] struct { // contains filtered or unexported fields }
Handler contains immutable, separately staged command callbacks. Callbacks and captured dependencies must support concurrent calls. O is the raw return type.
func Handle ¶
Handle adapts a model method expression or an ordinary typed callback.
Example ¶
package main
import (
"context"
"fmt"
"github.com/cratis/arc.go/commands"
)
type Greet struct {
Name string `json:"name"`
}
func (c Greet) Handle(context.Context) (string, error) { return "Hello, " + c.Name, nil }
func main() {
var registry commands.Registry
if err := commands.Register(®istry, commands.Handle(Greet.Handle)); err != nil {
panic(err)
}
pipeline, err := registry.Build(commands.PipelineOptions{})
if err != nil {
panic(err)
}
result, err := commands.Execute[string](context.Background(), pipeline, Greet{Name: "Ada"})
if err != nil {
panic(err)
}
response, present := result.Response()
fmt.Println(result.IsSuccess(), present, response)
}
Output: true true Hello, Ada
Example (Dependencies) ¶
package main
import (
"context"
"fmt"
"github.com/cratis/arc.go/commands"
)
type Multiplier interface{ Multiply(int) int }
type multiplier int
func (m multiplier) Multiply(value int) int { return int(m) * value }
type Calculate struct {
Number int `json:"number"`
}
func (c Calculate) Handle(_ context.Context, service Multiplier) (int, error) {
return service.Multiply(c.Number), nil
}
func main() {
service := multiplier(2)
var registry commands.Registry
if err := commands.Register(®istry, commands.Handle(func(c Calculate, ctx context.Context) (int, error) { return c.Handle(ctx, service) })); err != nil {
panic(err)
}
pipeline, err := registry.Build(commands.PipelineOptions{})
if err != nil {
panic(err)
}
result, err := commands.Execute[int](context.Background(), pipeline, Calculate{Number: 21})
if err != nil {
panic(err)
}
response, _ := result.Response()
fmt.Println(response)
}
Output: 42
func Prepare ¶
func Prepare[C, P, O any](provide func(context.Context, *Invocation, C) (Preparation[P], error), handle func(context.Context, *Invocation, C, P) (O, error)) Handler[C, O]
Prepare is the generator seam: dependencies resolve independently in each stage.
func Scoped ¶
func Scoped[C, O any, S execution.Resources](factory func(context.Context, S) (Handler[C, O], error)) Handler[C, O]
Scoped lazily constructs an adapter from an application resource holder after authorization and validation. It preserves the adapter's preparation stage.
Example ¶
package main
import (
"context"
"fmt"
"github.com/cratis/arc.go/commands"
"github.com/cratis/arc.go/execution"
)
type Multiplier interface{ Multiply(int) int }
type multiplier int
func (m multiplier) Multiply(value int) int { return int(m) * value }
type Calculate struct {
Number int `json:"number"`
}
func (c Calculate) Handle(_ context.Context, service Multiplier) (int, error) {
return service.Multiply(c.Number), nil
}
type CalculationResources struct {
Service Multiplier
Closed bool
}
func (r *CalculationResources) Close(context.Context) error { r.Closed = true; return nil }
func main() {
resources := &CalculationResources{Service: multiplier(2)}
var registry commands.Registry
if err := commands.Register(®istry, commands.Scoped(func(_ context.Context, r *CalculationResources) (commands.Handler[Calculate, int], error) {
return commands.Handle(func(c Calculate, ctx context.Context) (int, error) { return c.Handle(ctx, r.Service) }), nil
})); err != nil {
panic(err)
}
pipeline, err := registry.Build(commands.PipelineOptions{OpenResources: func(context.Context) (execution.Resources, error) { return resources, nil }})
if err != nil {
panic(err)
}
result, err := commands.Execute[int](context.Background(), pipeline, Calculate{Number: 21})
if err != nil {
panic(err)
}
response, _ := result.Response()
fmt.Println(response, resources.Closed)
}
Output: 42 true
func WithPreparation ¶
func WithPreparation[C, P, O any](provide func(C, context.Context) (Preparation[P], error), handle func(C, context.Context, P) (O, error)) Handler[C, O]
WithPreparation supports explicit preparation controls and early stop.
func WithProvide ¶
func WithProvide[C, P, O any](provide func(C, context.Context) (P, error), handle func(C, context.Context, P) (O, error)) Handler[C, O]
WithProvide separates typed preparation from handling. A failed Provide never fabricates a usable payload. Neither callback is called by Validate.
Example ¶
package main
import (
"context"
"fmt"
"github.com/cratis/arc.go/commands"
)
type Double struct {
Number int `json:"number"`
}
func (c Double) Provide(context.Context) (int, error) { return c.Number * 2, nil }
func (Double) Handle(_ context.Context, value int) (int, error) { return value, nil }
func main() {
var registry commands.Registry
if err := commands.Register(®istry, commands.WithProvide(Double.Provide, Double.Handle)); err != nil {
panic(err)
}
pipeline, err := registry.Build(commands.PipelineOptions{})
if err != nil {
panic(err)
}
result, err := commands.Execute[int](context.Background(), pipeline, Double{Number: 21})
if err != nil {
panic(err)
}
response, _ := result.Response()
fmt.Println(response)
}
Output: 42
type Invocation ¶
type Invocation struct {
// contains filtered or unexported fields
}
Invocation is an expiring callback frame, not a context-stored service locator. Accessors return nil facilities after expiry. Retained executors reject work.
func (*Invocation) CommandContext ¶
func (i *Invocation) CommandContext() CommandContext
CommandContext returns a copied snapshot including values set during this callback.
func (*Invocation) Execution ¶
func (i *Invocation) Execution() *Execution
Execution returns the current owner's callback view, or nil after expiry.
func (*Invocation) ParentCommandContext ¶
func (i *Invocation) ParentCommandContext(ctx context.Context) (value CommandContext, present bool, err error)
ParentCommandContext returns the parent's metadata at nested admission, if present. Membership and descriptors are copied; command/value objects remain borrowed immutable data. It exposes no parent invocation or completion owner.
func (*Invocation) Pipeline ¶
func (i *Invocation) Pipeline() Pipeline
Pipeline returns an executor joining this command owner. Retained use fails.
func (*Invocation) Scope ¶
func (i *Invocation) Scope() *execution.Scope
Scope returns a non-closing callback-scoped operation view, or nil after expiry.
type KeyProvider ¶
type KeyProvider interface{ GetKey() string }
KeyProvider explicitly supplies a provider-neutral command key.
type KeyResolver ¶
KeyResolver supplies trusted keys before filters; empty keys do not win.
type NoResponse ¶
type NoResponse struct{}
NoResponse is the type argument for commands without a client response.
type Operation ¶
type Operation interface{ CommandOperation() }
Operation marks immutable business data describing immediate inline work. Payloads remain borrowed, including pointer declarations; do not mutate them during execution. Services belong in Execute/Compensate parameters, not payloads.
type OperationCommitParticipant ¶
type OperationCommitParticipant interface {
DeferredCommitParticipant
ObserveCommit(context.Context, *Invocation) (CompletionReport, error)
}
OperationCommitParticipant is the existing sole terminal slot plus a read-only observation hook. ObserveCommit reports provider facts before operation entry; it must not connect, commit, retry or activate business services. Pending work can report NotCommitted. Complete owns the authoritative final outcome. Recovery dependencies must remain usable after completion; a completed deferred transaction itself is not a usable recovery resource.
type OperationCompensation ¶
type OperationCompensation uint8
OperationCompensation describes one started invocation's recovery observation.
const ( // CompensationNotNeeded means recovery was not required. CompensationNotNeeded OperationCompensation = iota // CompensationCompleted means the callback returned successfully. CompensationCompleted // CompensationFailed means the callback failed, panicked or exceeded its budget. CompensationFailed // CompensationNotAvailable means no reversal was declared. CompensationNotAvailable // CompensationBudgetExpired means the callback was not entered before expiry. CompensationBudgetExpired // CompensationSuppressed means persistence facts prohibited reversal. CompensationSuppressed )
type OperationFailure ¶
type OperationFailure struct {
InvocationIndex int
InvocationCompleted, IsFailingInvocation bool
Source OperationFailureSource
Completion CompletionReport
Original Result[NoResponse]
Cause error
}
OperationFailure supplies original failure facts to a compensator. Original is an immutable response-free snapshot; Completion is the sole persistence report. Cause preserves the original error identity, never later compensation errors.
type OperationFailureSource ¶
type OperationFailureSource uint8
OperationFailureSource identifies the first failed pipeline phase.
const ( // FailurePlanning is declaration or dependency preflight failure. FailurePlanning OperationFailureSource = iota // FailureResponseHandling is return classification/control/consumer failure. FailureResponseHandling // FailureExecution is a business invocation failure. FailureExecution // FailureCancellation is cancellation of forward execution. FailureCancellation // FailureScopeCompletion is ordinary or terminal completion failure. FailureScopeCompletion )
type OperationOptions ¶
type OperationOptions struct {
// CompensationTimeout defaults to 30 seconds when zero; negative is invalid.
// A callback ignoring cancellation is still synchronously joined. After expiry
// no further compensator starts; Arc never restarts a timed-out callback.
CompensationTimeout time.Duration
}
OperationOptions configures one cooperative, detached budget shared by recovery.
type OperationOutcome ¶
type OperationOutcome struct {
InvocationIndex int
OperationType reflect.Type
ExecutionCompleted bool
Compensation OperationCompensation
CompensationFailure error
}
OperationOutcome is a backend-only observation with no declaration payload. Error objects remain borrowed and must not be mutated after returning.
type Operations ¶
type Operations struct {
// contains filtered or unexported fields
}
Operations is an ordered batch with copied membership and borrowed payloads. Zero is empty. It is safe for concurrent reads if payloads are not mutated.
func NewOperations ¶
func NewOperations(values ...Operation) (Operations, error)
NewOperations copies membership once and rejects nil members, including typed nils.
func (Operations) Values ¶
func (o Operations) Values() []Operation
Values returns isolated ordered membership; declaration payloads remain borrowed.
type Option ¶
type Option[C any] interface { // contains filtered or unexported methods }
Option is a sealed, typed registration option. Singular options cannot repeat.
func WithAuthorization ¶
func WithAuthorization[C any](a metadata.Authorization) Option[C]
WithAuthorization replaces the declaration rather than merging it.
func WithBlockOnValidationSeverity ¶
func WithBlockOnValidationSeverity[C any](s validation.Severity) Option[C]
WithBlockOnValidationSeverity supplies an inclusive, non-loosenable floor.
func WithDescriptor ¶
WithDescriptor pins the complete descriptor before individual overrides.
func WithExcludeFromDiscovery ¶
WithExcludeFromDiscovery hides discovery without changing routes or authorization.
func WithHandlingDependencies ¶
WithHandlingDependencies declares Handle-only DI keys without eager activation.
func WithNamespace ¶
WithNamespace overrides the public namespace, never a Go import path.
func WithNoResponse ¶
WithNoResponse requires every returned value to be consumed.
func WithOperations ¶
WithOperations declares participation when Outcome effects erase operation types. Raw any returns are unsupported. Even empty/absent outputs require a compatible boundary. Validate never prepares, executes or compensates operations.
func WithPreparationDependencies ¶
WithPreparationDependencies declares keys for static catalog checks, not resolution.
func WithProtectedDecisions ¶
WithProtectedDecisions opts a command into protected decision reads, the Go counterpart of C# [ProtectedDecision]. The profile is fixed at registration, before any filter, validator or handler can run.
This initial profile cannot certify arbitrary validation code. Register refuses it unless WithoutModelValidation is also supplied, and it refuses any WithValidator or WithScopedValidator option and any command operation. Build refuses it unless the registry has a decision provider (AddDecisionProvider) and a completion owner (AddDeferredCommitParticipant). It cannot be combined with WithUnprotectedDecisions (ErrDuplicate).
Example ¶
package main
import (
"context"
"fmt"
"reflect"
"github.com/cratis/arc.go/commands"
"github.com/cratis/arc.go/execution"
)
type BookRoom struct{ Room string }
type Room struct{ Booked bool }
// roomSource stands in for a provider integration such as Chronicle's. Real
// evidence is a provider token; Enroll registers it with the completion owner.
type roomSource struct{}
func (roomSource) Admit(context.Context) error { return nil }
func (roomSource) Acquire(context.Context) (any, error) { return &Room{}, nil }
func (roomSource) Check(context.Context, any) error { return nil }
func (roomSource) Enroll(context.Context, any) error { return nil }
// noOwner is a completion owner that persists nothing.
type noOwner struct{}
func (noOwner) Begin(context.Context, *commands.Invocation) error { return nil }
func (noOwner) Complete(context.Context, *commands.Invocation, commands.Result[any]) (commands.CompletionReport, error) {
return commands.CompletionReport{Disposition: commands.NoPersistedWork}, nil
}
func main() {
var registry commands.Registry
provider := commands.NewDecisionProvider()
err := commands.Register(®istry,
commands.WithProtectedDecisions[BookRoom](),
commands.WithoutModelValidation[BookRoom](),
commands.Prepare(func(ctx context.Context, inv *commands.Invocation, c BookRoom) (commands.Preparation[*commands.DecisionRead], error) {
target := commands.DecisionTarget{Provider: provider, Model: reflect.TypeFor[Room](), Store: "hotel", Namespace: "default", Key: c.Room}
read, err := commands.ReadDecision(ctx, inv, target, roomSource{})
if err != nil {
return commands.Preparation[*commands.DecisionRead]{}, err
}
return commands.Provided(read), nil
}, func(_ context.Context, _ *commands.Invocation, c BookRoom, read *commands.DecisionRead) (commands.NoResponse, error) {
// Arc verified read before Handle; decide from it.
fmt.Println("booking", c.Room, "booked:", read.Value().(*Room).Booked)
return commands.NoResponse{}, nil
}))
if err == nil {
err = registry.AddDecisionProvider(provider)
}
if err == nil {
err = registry.AddDeferredCommitParticipant("owner", func(context.Context, *execution.Scope) (commands.DeferredCommitParticipant, error) {
return noOwner{}, nil
})
}
if err != nil {
fmt.Println(err)
return
}
pipeline, err := registry.Build(commands.PipelineOptions{})
if err != nil {
fmt.Println(err)
return
}
result, err := pipeline.Execute(context.Background(), BookRoom{Room: "101"})
fmt.Println(result.IsSuccess(), err)
}
Output: booking 101 booked: false true <nil>
func WithResponseType ¶
WithResponseType declares an enforced client response contract.
func WithScopedValidator ¶
WithScopedValidator constructs a validator only in validation, after authorization.
func WithUnprotectedDecisions ¶
WithUnprotectedDecisions marks a command as using advisory, unguarded reads, the Go counterpart of C# [Unprotected]. It adds no runtime guarantee; it lets a provider serve detached snapshots and records the choice for tooling. It cannot be combined with WithProtectedDecisions (ErrDuplicate).
func WithValidator ¶
func WithValidator[C any](v validation.Validator[C]) Option[C]
WithValidator appends an ordered, shared validator.
func WithoutModelValidation ¶
WithoutModelValidation explicitly disables graph methods, registered rules and tags.
type Outcome ¶
type Outcome[R any] struct { // contains filtered or unexported fields }
Outcome is an explicitly classified return graph. Membership is copied; values remain borrowed. Zero means successful void output. Collections are single leaves.
func Control ¶
func Control[R any](control Result[NoResponse]) Outcome[R]
Control explicitly adopts an infrastructure result rather than serializing it.
func Respond ¶
Respond reserves one response and requires consumption of every effect leaf.
Example ¶
package main
import (
"context"
"fmt"
"github.com/cratis/arc.go/commands"
"github.com/cratis/arc.go/execution"
)
type ItemAdded struct {
Name string `json:"name"`
}
type AddNamedItem struct {
Name string `json:"name"`
}
func (c AddNamedItem) Handle(context.Context) (commands.Outcome[string], error) {
return commands.Respond("receipt", ItemAdded(c)), nil
}
type eventRecorder struct{ Events []ItemAdded }
func (*eventRecorder) CanHandle(_ commands.CommandContext, value any) bool {
_, ok := value.(ItemAdded)
return ok
}
func (r *eventRecorder) Handle(_ context.Context, inv *commands.Invocation, value any) (commands.Result[commands.NoResponse], error) {
r.Events = append(r.Events, value.(ItemAdded))
return commands.Success(inv.CommandContext().CorrelationID()), nil
}
func main() {
// This is an in-memory extension, not Chronicle append or a transaction.
recorder := &eventRecorder{}
var registry commands.Registry
if err := commands.Register(®istry, commands.Handle(AddNamedItem.Handle)); err != nil {
panic(err)
}
if err := commands.RegisterResponseValueHandler[ItemAdded](®istry, "events", func(context.Context, *execution.Scope) (commands.ResponseValueHandler, error) { return recorder, nil }); err != nil {
panic(err)
}
pipeline, err := registry.Build(commands.PipelineOptions{})
if err != nil {
panic(err)
}
result, err := commands.Execute[string](context.Background(), pipeline, AddNamedItem{Name: "Book"})
if err != nil {
panic(err)
}
response, _ := result.Response()
fmt.Println(response, recorder.Events[0].Name)
}
Output: receipt Book
type Pipeline ¶
type Pipeline interface {
Lookup(string) (Registration, bool)
LookupCommand(any) (Registration, error)
Execute(context.Context, any, ...ExecuteOptions) (Result[any], error)
Validate(context.Context, any, ...ExecuteOptions) (Result[NoResponse], error)
ExecuteScoped(context.Context, *execution.Scope, any, ...ExecuteOptions) (Result[any], error)
ValidateScoped(context.Context, *execution.Scope, any, ...ExecuteOptions) (Result[NoResponse], error)
}
Pipeline is the public substitution boundary for backend command execution.
type PipelineOptions ¶
type PipelineOptions struct {
// Diagnostics is borrowed, bounded backend recording; nil disables it.
Diagnostics *observability.Recorder
OpenResources execution.OpenResources
ScopeFactory di.ScopeFactory
DependencyCatalog di.Catalog
Authorization *authorization.Evaluator
Validation *validation.Graph
Membership tenancy.Membership
RequireTenant bool
Clock func() time.Time
CleanupTimeout time.Duration
Operations OperationOptions
ExposeExceptionDetails bool
Logger *slog.Logger
}
PipelineOptions configures borrowed shared collaborators. Nil authorization compiles declarations (never bypasses them); nil validation enables model/tag rules. OpenResources and ScopeFactory are mutually exclusive. Cleanup is cooperative.
type Preparation ¶
type Preparation[P any] struct { // contains filtered or unexported fields }
Preparation distinguishes usable typed payloads from explicit early stop. Zero is invalid; nil payloads supplied by Provided are still usable typed values.
func Provided ¶
func Provided[P any](value P) Preparation[P]
Provided supplies a payload with successful preparation controls.
func ProvidedWith ¶
func ProvidedWith[P any](value P, control Result[NoResponse]) Preparation[P]
ProvidedWith supplies a payload and controls subject to input-stage severity.
func StopProviding ¶
func StopProviding[P any](control Result[NoResponse]) Preparation[P]
StopProviding explicitly skips Handle even when control indicates success.
type ReadModelInstance ¶
type ReadModelInstance[M any] struct { // Value is meaningful only when Exists is true. Value M // Exists distinguishes an absent model from a present zero value. Exists bool }
ReadModelInstance preserves absence independently of a zero-valued model. Value is borrowed from the provider for this frame. Providers own materialization, release, caching and mutable-value isolation; a release failure must be an error.
func ResolveReadModel ¶
func ResolveReadModel[M any](ctx context.Context, inv *Invocation) (ReadModelInstance[M], error)
ResolveReadModel reads M through its selected owner with presence intact. Missing/blank keys are malformedRequest, even for optional reads. A missing provider is ErrReadModelProvider, not absence. Reader/release errors remain inspectable and are never cached or converted into a missing instance.
type ReadModelOwnership ¶
type ReadModelOwnership uint8
ReadModelOwnership controls exact-type provider precedence independently of registration order. Providers at the same level cannot claim the same model.
const ( // FallbackReadModel supplies models with no declared owner or application override. FallbackReadModel ReadModelOwnership = iota // DeclaredReadModel claims models backed by the provider's declared producer. DeclaredReadModel // OverrideReadModel is an explicit application replacement, above declared ownership. OverrideReadModel )
type RecoveryStatus ¶
type RecoveryStatus uint8
RecoveryStatus describes observed callback recovery, never atomic rollback.
const ( // RecoveryNotNeeded means no failed started work requires reversal. RecoveryNotNeeded RecoveryStatus = iota // RecoveryCompleted means every eligible compensator returned successfully. RecoveryCompleted // RecoveryIncomplete means one or more reversals were unavailable or failed. RecoveryIncomplete // RecoverySuppressed means confirmed persistence prohibits automatic reversal. RecoverySuppressed // RecoveryIndeterminate means unknown/mixed persistence prohibits reversal. RecoveryIndeterminate )
type RecoverySummary ¶
type RecoverySummary struct {
Status RecoveryStatus
StartedCount, CompletedCount, CompensatedCount int
FailedCompensationCount, UncompensatedCount int
}
RecoverySummary is a backend-only immutable value snapshot. Completion() on the Result remains the sole persistence report. Completed is not a retry guarantee.
type Registrar ¶
type Registrar interface{ RegisterCommand(Registration) error }
Registrar accepts frozen registrations; generated adapters use this seam.
type Registration ¶
type Registration struct {
// contains filtered or unexported fields
}
Registration is an immutable typed command adapter and descriptor.
func (Registration) CommandType ¶
func (r Registration) CommandType() reflect.Type
CommandType returns the exact pointer or value registration type.
func (Registration) DecisionProfile ¶
func (r Registration) DecisionProfile() DecisionProfile
DecisionProfile returns the frozen decision-read profile.
func (Registration) Decode ¶
func (r Registration) Decode(body []byte) (any, error)
Decode allocates fresh input and uses Arc scalar/field serialization.
func (Registration) Descriptor ¶
func (r Registration) Descriptor() metadata.Command
Descriptor returns a copy-isolated declaration.
func (Registration) ResponseKind ¶
func (r Registration) ResponseKind() ResponseKind
ResponseKind returns the frozen client contract classification.
func (Registration) ResponseType ¶
func (r Registration) ResponseType() (reflect.Type, bool)
ResponseType returns a known response type, never the raw type as a guess.
func (Registration) ReturnType ¶
func (r Registration) ReturnType() reflect.Type
ReturnType returns Handle's raw declared result type.
type RegistrationError ¶
RegistrationError retains the affected identity and inspectable category.
func (*RegistrationError) Error ¶
func (e *RegistrationError) Error() string
Error describes a registration failure.
func (*RegistrationError) Unwrap ¶
func (e *RegistrationError) Unwrap() error
Unwrap exposes the category.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry is a single-owner builder. Zero uses the global namespace. Successful Build freezes it; failed Build leaves it editable. It never activates factories.
func NewRegistry ¶
func NewRegistry(options RegistryOptions) (*Registry, error)
NewRegistry validates namespace configuration without application callbacks.
func (*Registry) AddAuthorizationFilter ¶
func (r *Registry) AddAuthorizationFilter(name string, factory Factory[AuthorizationFilter], keys ...di.Key) error
AddAuthorizationFilter appends a lazy authorization filter.
func (*Registry) AddBuildCheck ¶
AddBuildCheck registers an integration configuration check. Build calls checks in insertion order after declaration validation, before freezing or activating factories, and returns the first error unchanged. Failed builds remain editable and retry all checks on the next Build. Checks may be added before commands; they must not mutate the registry or activate application services. Nil checks or receivers return ErrInvalidRegistration; frozen registries return ErrFrozen.
func (*Registry) AddContextValuesProvider ¶
func (r *Registry) AddContextValuesProvider(name string, factory Factory[ContextValuesProvider], keys ...di.Key) error
AddContextValuesProvider registers an ordered, lazy metadata provider.
func (*Registry) AddDecisionProvider ¶
func (r *Registry) AddDecisionProvider(provider *DecisionProvider) error
AddDecisionProvider certifies a provider identity for protected commands built from this registry. Registration is composition, not a runtime boolean: a read whose target names any other provider is refused. Nil returns ErrInvalidRegistration (as does a zero provider), a repeated provider ErrDuplicate and a frozen registry ErrFrozen. It activates nothing.
func (*Registry) AddDeferredCommitParticipant ¶
func (r *Registry) AddDeferredCommitParticipant(name string, factory Factory[DeferredCommitParticipant], keys ...di.Key) error
AddDeferredCommitParticipant registers the sole terminal participant. A second registration is rejected irrespective of its name; cross-provider atomicity is not supported. The borrowed factory is lazy, and Build performs no I/O.
func (*Registry) AddExecutionScope ¶
func (r *Registry) AddExecutionScope(name string, factory Factory[ExecutionScope], keys ...di.Key) error
AddExecutionScope registers a lazy borrowed root-completion participant.
func (*Registry) AddFilter ¶
AddFilter appends an ordinary lazy filter in stable registration order.
func (*Registry) AddKeyResolver ¶
AddKeyResolver registers a custom resolver before the command's own fallback.
func (*Registry) AddOperationCommitParticipant ¶
func (r *Registry) AddOperationCommitParticipant(name string, factory Factory[OperationCommitParticipant], keys ...di.Key) error
AddOperationCommitParticipant explicitly classifies the sole terminal slot. Unclassified terminal participants remain supported for non-operation commands.
func (*Registry) AddOperationExecutionScope ¶
func (r *Registry) AddOperationExecutionScope(name string, factory Factory[ExecutionScope], keys ...di.Key) error
AddOperationExecutionScope explicitly classifies a noncommitting scope. Begin must not commit; Complete must not persist business changes. Recovery services remain usable until the originating resource owner disposes them. This promise is static: Arc never activates factories merely to discover compatibility.
func (*Registry) AddResponseValueHandler ¶
func (r *Registry) AddResponseValueHandler(name string, factory Factory[ResponseValueHandler], keys ...di.Key) error
AddResponseValueHandler registers a lazy dynamic consumer.
func (*Registry) AddReturnAdmission ¶
func (r *Registry) AddReturnAdmission(name string, admission ReturnAdmission) error
AddReturnAdmission registers a named admission policy. Static claims compile raw return metadata to ResponseNone unless the command explicitly overrides it. Dynamic admission retains unknown response metadata and requires runtime checks.
func (*Registry) Build ¶
func (r *Registry) Build(options PipelineOptions) (Pipeline, error)
Build validates catalogs and shapes, without resolving services, and freezes the registry only on success. Returned pipelines are concurrently callable.
func (*Registry) Catalog ¶
Catalog returns copy-isolated declaration metadata in registration order.
func (*Registry) ContainsCommandType ¶
ContainsCommandType reports exact registration membership without building or freezing the registry. Pointer and value types are distinct; nil receivers and nil types return false. Like registration, access requires a single owner.
func (*Registry) RegisterCommand ¶
func (r *Registry) RegisterCommand(value Registration) error
RegisterCommand accepts only valid immutable registrations and exact ownership.
type RegistryOptions ¶
type RegistryOptions struct{ Namespace string }
RegistryOptions configures public identity defaults.
type ResponseKind ¶
type ResponseKind uint8
ResponseKind distinguishes an unresolved contract from guaranteed absence.
const ( // ResponseUnknown requires an explicit contract for typed execution. ResponseUnknown ResponseKind = iota // ResponseNone guarantees no client response. ResponseNone // ResponseValue permits a response of the declared type, or absence. ResponseValue )
type ResponseValueContextUpdater ¶
type ResponseValueContextUpdater interface {
ResponseValueHandler
UpdateContext(context.Context, *Invocation, any) error
}
ResponseValueContextUpdater participates before response selection. It may set context values, never identity, tenant, correlation, resources or ownership.
type ResponseValueHandler ¶
type ResponseValueHandler interface {
CanHandle(CommandContext, any) bool
Handle(context.Context, *Invocation, any) (Result[NoResponse], error)
}
ResponseValueHandler is an additive server consumer. Every matching handler runs in registration order. CanHandle may depend on the selected response.
type Result ¶
type Result[R any] struct { // contains filtered or unexported fields }
Result is a finalized command outcome with computed flags and explicit response presence. Its zero value denies authorization. It is safe for concurrent reads if borrowed response and State values are not mutated. It is an output envelope, not a JSON decoder; use ordinary DTOs to consume remote responses.
func Execute ¶
func Execute[R any](ctx context.Context, p Pipeline, command any, options ...ExecuteOptions) (output Result[R], outputErr error)
Execute checks a known response contract before application code. Unknown contracts are checked against the actual response after execution; a mismatch returns ErrResponseType and a failed envelope with no response.
func FromError ¶
func FromError[R any](id correlation.ID, err error) Result[R]
FromError classifies joined failures and always redacts production exceptions. It retains no response. The caller still owns and may inspect the original error.
func InvalidBody ¶
func InvalidBody(id correlation.ID) Result[NoResponse]
InvalidBody produces a safe malformedRequest finding.
func Merge ¶
func Merge[R any](result Result[R], fragments ...Result[NoResponse]) Result[R]
Merge ANDs authorization, appends diagnostics and retains the outer correlation and first nonempty denial reason. Any failure removes response presence.
func NewResult ¶
func NewResult[R any](details Details, response serialization.Optional[R]) Result[R]
NewResult finalizes details and copies their arrays. Any failure discards response presence, including scalar zeros. Application response values remain borrowed.
func Success ¶
func Success(id concepts.UUID) Result[NoResponse]
Success returns a successful command without a response.
func Unauthorized ¶
func Unauthorized(id correlation.ID, reason string) Result[NoResponse]
Unauthorized produces a denied fragment with caller-authored safe feedback.
func WithResponse ¶
WithResponse returns success with an explicitly present response, including zero.
func WithValidationResults ¶
func WithValidationResults(id correlation.ID, findings ...validation.Result) Result[NoResponse]
WithValidationResults produces an authorized validation fragment.
func (Result[R]) Completion ¶
func (r Result[R]) Completion() CompletionReport
Completion returns persistence evidence, including after command/cleanup failure.
func (Result[R]) Details ¶
Details returns copy-isolated arrays; application State remains borrowed.
func (Result[R]) HasExceptions ¶
HasExceptions reports whether exceptionMessages is nonempty.
func (Result[R]) IsAuthorized ¶
IsAuthorized reports the final authorization decision.
func (Result[R]) MarshalJSON ¶
MarshalJSON emits the Arc CommandResult envelope with required arrays and strings.
func (Result[R]) MarshalJSONWith ¶
MarshalJSONWith encodes nested values using the supplied Arc traversal. The callback is synchronous and is not retained; callers normally use MarshalJSON.
func (Result[R]) OperationOutcomes ¶
func (r Result[R]) OperationOutcomes() []OperationOutcome
OperationOutcomes returns copied backend-only observations, never payloads.
func (Result[R]) Recovery ¶
func (r Result[R]) Recovery() (RecoverySummary, bool)
Recovery returns backend-only recovery observations, if processing participated.
func (Result[R]) Response ¶
Response distinguishes absence from a legitimate zero value. Nil payloads are omitted on the wire, even if present locally, matching C# null omission.
func (Result[R]) StatusCode ¶
StatusCode selects 200, 403, 400 or 500 in Arc precedence order.
type ReturnAdmission ¶
type ReturnAdmission struct {
// Types is the frozen set of exact server-consumed types; empty means dynamic.
Types []reflect.Type
// Check validates/classifies a borrowed return value, including typed nils.
Check func(context.Context, *Invocation, any) (ReturnClassification, error)
}
ReturnAdmission supplies immutable catalog metadata and optional runtime classification. Types contains exact concrete server-consumed types (pointer and value are distinct); it is copied on registration. A matching typed nil is rejected. With no Types, Check is required and classifies dynamic values.
Check, when supplied, runs before any consumer/updater and may reject a value. For a listed type its OrdinaryReturn cannot undo the static claim. It must not perform external effects. It runs outside locks and must be concurrent-safe. Explicit Respond values and builtin controls bypass this extension entirely. No callback runs during Build. This never classifies a slice by its elements.
type ReturnClassification ¶
type ReturnClassification uint8
ReturnClassification is opt-in admission before nil flattening and effects.
const ( // OrdinaryReturn leaves the existing response/consumer rules unchanged. OrdinaryReturn ReturnClassification = iota // ServerConsumedReturn requires a consumer and never selects a client response. ServerConsumedReturn )
type StateKey ¶
type StateKey[T any] struct { // contains filtered or unexported fields }
StateKey identifies extension-owned state. Construct it with NewStateKey; zero is invalid. Copies identify the same slot. Keep ownership-capability keys private.
func NewStateKey ¶
NewStateKey allocates an independent typed slot, without process-wide storage.
Source Files
¶
- completion.go
- context.go
- decision_profile.go
- decision_reads.go
- decode.go
- diagnostics.go
- errors.go
- execution.go
- execution_scope.go
- filters.go
- handler.go
- keys.go
- operation_execution.go
- operations.go
- options.go
- outcome.go
- pipeline.go
- preparation.go
- read_models.go
- registration.go
- registry.go
- response_handlers.go
- result.go
- result_helpers.go
- return_admission.go
- state.go
- validation.go
- values.go