behavior

package
v0.38.0 Latest Latest
Warning

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

Go to latest
Published: Jul 11, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrMissingCollectorBehavior = errors.New("approval: required collector behavior missing from cqrs:behaviors group")

ErrMissingCollectorBehavior is returned by the boot-time self-check when the ActionLog or EventPublish behavior failed to register. Surfaced via fx.Invoke so misconfigured hosts cannot start a process that would silently drop audit rows / domain events.

View Source
var Module = fx.Module(
	"vef:approval:behavior",

	fx.Provide(
		fx.Annotate(
			NewTransactionBehavior,
			fx.ResultTags(`group:"vef:cqrs:behaviors"`),
		),
		fx.Annotate(
			NewActionLogBehavior,
			fx.ResultTags(`group:"vef:cqrs:behaviors"`),
		),
		fx.Annotate(
			NewEventPublishBehavior,
			fx.ResultTags(`group:"vef:cqrs:behaviors"`),
		),
	),

	fx.Invoke(
		fx.Annotate(
			assertCollectorBehaviorsRegistered,
			fx.ParamTags(`group:"vef:cqrs:behaviors"`),
		),
	),
)

Module provides all CQRS behavior middlewares for the approval module. Behaviors are aggregated via FX group `group:"vef:cqrs:behaviors"`; the CQRS bus sorts them by their Order() method so wrapping order is independent of FX's group-resolution timing.

Order assignments (see cqrs.Ordered). Lower Order wraps outer, and each collector flushes AFTER the wrapped handler returns, so the innermost behavior flushes first:

  • Transaction (Order 0) wraps every inner behavior and the handler; commits once they all succeed.
  • ActionLog (Order 100) wraps EventPublish; its audit rows therefore flush AFTER EventPublish has published.
  • EventPublish (Order 200) is the innermost behavior, so it flushes (publishes events) FIRST — before the ActionLog rows are inserted.

All three flushes run inside the single Transaction tx, so the relative order is invisible outside the commit: the events become visible iff the transaction (audit rows included) commits.

The fx.Invoke hook runs a boot-time self-check that fails fast if either the ActionLog or EventPublish behavior is missing — the alternative is silent audit / event loss in production, which is far worse than refusing to start.

Functions

func NewActionLogBehavior added in v0.24.0

func NewActionLogBehavior(db orm.DB) cqrs.Behavior

NewActionLogBehavior buffers ActionLog entries produced by a command handler and inserts them in a single batch after the handler succeeds. Failed handlers short-circuit so audit rows never describe a non-event.

Order positions the behavior between Transaction (outermost) and EventPublish (innermost). Because each collector flushes after the wrapped handler returns, EventPublish (inner) flushes first and these audit rows persist AFTER the events publish — all inside the same Transaction tx, so the commit is still atomic.

func NewEventPublishBehavior added in v0.24.0

func NewEventPublishBehavior(db orm.DB, bus event.Bus) cqrs.Behavior

NewEventPublishBehavior buffers domain events produced by a command handler and publishes them, in registration order, after the handler succeeds. Publishing runs inside the surrounding transaction so the framework's event Bus can enroll via event.WithTx(db); each event also projects its payload OccurredTime onto Envelope.OccurredAt so downstream consumers see business time rather than publish time.

Order 200 makes this the innermost approval behavior, so among the collectors it flushes FIRST — events publish before the outer ActionLog inserts its audit rows. Both flushes run inside the same Transaction tx, so the events are visible iff that transaction commits.

func NewTransactionBehavior

func NewTransactionBehavior(db orm.DB) cqrs.Behavior

NewTransactionBehavior creates a new TransactionBehavior.

func PublishEventsTx added in v0.29.0

func PublishEventsTx(ctx context.Context, bus event.Bus, db orm.DB, events ...approval.DomainEvent) error

PublishEventsTx publishes domain events through the bus enrolled in the caller's transaction (event.WithTx(db)), projecting each payload's OccurredTime onto Envelope.OccurredAt so downstream consumers see business time rather than publish time. A failed publish is wrapped with the offending event's type for context. Returns nil when bus is nil or events is empty.

This is the shared publish primitive for the approval module: the CQRS EventPublishBehavior flush delegates here, and the engine's PublishEventsTx (used by sites outside the CQRS pipeline) wraps it too, so the option-building and publish loop live in exactly one place.

Types

type ActionLogCollector added in v0.24.0

type ActionLogCollector = Collector[*approval.ActionLog]

ActionLogCollector is the request-scoped buffer for approval ActionLog entries. Alias of Collector[*approval.ActionLog] for call-site clarity.

func ActionLogCollectorFromContext added in v0.24.0

func ActionLogCollectorFromContext(ctx context.Context) *ActionLogCollector

ActionLogCollectorFromContext returns the request-scoped collector or a detached no-op collector (with a warning) when called outside the CQRS pipeline.

func TryActionLogCollectorFromContext added in v0.29.0

func TryActionLogCollectorFromContext(ctx context.Context) (*ActionLogCollector, bool)

TryActionLogCollectorFromContext returns the collector silently when missing, for engine paths that may legitimately run outside the CQRS pipeline (e.g. the timeout scanner) and fall back to event-only auditing.

type Collector added in v0.24.0

type Collector[T any] struct {
	// contains filtered or unexported fields
}

Collector buffers items of type T produced by a command handler while it runs inside the CQRS pipeline. Handlers append items to the collector instead of touching the outside world directly; a collectorBehavior[T] flushes the buffer in one batch after the handler returns successfully.

Concrete uses live in event_publish.go (Collector[approval.DomainEvent]) and action_log.go (Collector[*approval.ActionLog]).

func TryCollectorFromContext added in v0.24.0

func TryCollectorFromContext[T any](ctx context.Context) (*Collector[T], bool)

TryCollectorFromContext returns the request-scoped Collector[T] if one is installed, else (nil, false). Use this when the absence of the collector should not produce a warning — for example, engine helpers that must fall back to direct bus.Publish when invoked from a cron or saga outside the CQRS pipeline.

func (*Collector[T]) Add added in v0.24.0

func (c *Collector[T]) Add(items ...T)

Add appends items to the collector. Nil entries — including typed-nil pointers / nil interface values — are silently dropped so call sites can pass best-effort outputs without guarding each one.

func (*Collector[T]) Items added in v0.24.0

func (c *Collector[T]) Items() []T

Items returns the buffered slice. Callers should not mutate it; the returned slice is shared with the behavior that will eventually flush it.

type EventCollector added in v0.24.0

type EventCollector = Collector[approval.DomainEvent]

EventCollector is the request-scoped buffer for approval domain events. It's just Collector[approval.DomainEvent]; the alias gives call sites a stable name even if the generic plumbing is reshaped later.

func EventCollectorFromContext added in v0.24.0

func EventCollectorFromContext(ctx context.Context) *EventCollector

EventCollectorFromContext returns the request-scoped event collector or a detached no-op collector (with a warning) when called outside the CQRS pipeline so unit tests that bypass the bus don't crash on nil receivers.

func TryEventCollectorFromContext added in v0.24.0

func TryEventCollectorFromContext(ctx context.Context) (*EventCollector, bool)

TryEventCollectorFromContext returns the collector silently when missing, for callers (engine helpers, saga drivers) that have a sensible fallback.

type TransactionBehavior

type TransactionBehavior struct {
	// contains filtered or unexported fields
}

TransactionBehavior wraps command handlers in a database transaction. Query handlers bypass the transaction.

func (*TransactionBehavior) Handle

func (b *TransactionBehavior) Handle(ctx context.Context, action cqrs.Action, next func(context.Context) (any, error)) (any, error)

Handle wraps command actions in a database transaction. Query actions pass through unchanged. If a parent transaction is already attached to ctx (e.g. when a Saga or event subscriber re-dispatches a command from within an existing transaction), the inner pipeline reuses that transaction rather than opening a nested one — concurrent nested transactions on the same connection are driver-specific and the runtime cost of savepoints outweighs the rare benefit here.

func (*TransactionBehavior) Order added in v0.24.0

func (*TransactionBehavior) Order() int

Order places TransactionBehavior at the outermost slot so every inner behavior (ActionLog / EventPublish) sees the same tx in context.

Jump to

Keyboard shortcuts

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