Documentation
¶
Overview ¶
Package usecase implements the sealed Result pattern used by the FlowCatalyst SDK and the platform itself, plus the DomainEvent / ExecutionContext / typed Error primitives the use-case envelope builds on.
The Result[E] type is a sealed sum: only success[E] and failure[E] implement it, both unexported. Failure can be constructed by anyone; Success requires a sealed.Token, which only packages under pkg/fcsdk/ can mint. Therefore the only path to a Success-valued Result outside the SDK is through one of the Commit* free functions in usecasepgx.
This is what guarantees, at compile time, that every domain event is committed atomically with its aggregate state — no code path produces a Success without going through a UnitOfWork.
Index ¶
- func BuildEventType(app, domain, aggregate, action string) string
- func BuildMessageGroup(domain, aggregate, id string) string
- func BuildSubject(domain, aggregate, id string) string
- func ExtractAggregateType(subject string) string
- func ExtractEntityID(subject string) string
- func Into[E any](r Result[E]) (E, error)
- func IsFailure[E any](r Result[E]) bool
- func IsSuccess[E any](r Result[E]) bool
- func RedactAuditJSON(raw []byte, masked []string) ([]byte, error)
- func RedactedAuditCommand(command any) ([]byte, error)
- type AuditMasked
- type DomainEvent
- type Error
- func AsError(err error) *Error
- func Authorization(code, message string) *Error
- func BusinessRule(code, message string) *Error
- func Conflict(code, message string) *Error
- func Internal(code, message string, cause error) *Error
- func NotFound(code, message string) *Error
- func Validation(code, message string) *Error
- type EventMetadata
- type ExecutionContext
- type HasID
- type Kind
- type Result
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BuildEventType ¶
BuildEventType is the convention for forming an event type.
func BuildMessageGroup ¶
BuildMessageGroup is the convention for forming a message group.
func BuildSubject ¶
BuildSubject is the convention for forming a subject from domain/aggregate/id. Mirrors the TS SDK helper.
func ExtractAggregateType ¶
ExtractAggregateType maps "platform.eventtype.123" → "Eventtype". Used by EventSink implementations to populate the aggregate_type column on outbox / audit rows.
func ExtractEntityID ¶
ExtractEntityID maps "platform.eventtype.123" → "123".
func Into ¶
Into converts a Result into the stdlib (T, error) shape. Handlers call this to turn a use case outcome into an HTTP response.
func RedactAuditJSON ¶
RedactAuditJSON applies the audit redaction rule (the Java platform's docs/spec/audit-redaction.md; the shared cases are testdata/audit-redaction-vectors.json) to a serialised command: a key is secret when, lower-cased with '_' and '-' removed, it ends with password, passwordhash, secret, secretref, passphrase or token, or equals apikey, privatekey, authorization or cookie; a secret key's value becomes "***" whatever its type, except null and booleans, which are kept. Objects and arrays are walked; masked names top-level fields masked the same way. Input that is not a JSON object or array is returned unchanged.
func RedactedAuditCommand ¶
RedactedAuditCommand marshals a command for an audit row with the rule applied, including the command's own AuditMaskedFields when it has them.
Types ¶
type AuditMasked ¶
type AuditMasked interface {
AuditMaskedFields() []string
}
AuditMasked is implemented by a command whose audit document must hide a field the name rule would keep (a SECRET platform-config value, say). The fields are top-level JSON keys of the serialised command.
type DomainEvent ¶
type DomainEvent interface {
EventID() string
EventType() string // e.g. "platform:admin:eventtype:created"
SpecVersion() string // CloudEvents spec version
Source() string // e.g. "platform:admin"
Subject() string // e.g. "platform.eventtype.evt_01H..."
Time() time.Time
PrincipalID() string
CorrelationID() string
CausationID() string
ExecutionID() string
MessageGroup() string
// ToDataJSON returns the event-specific data payload as JSON.
// Implementations typically json.Marshal a struct of their fields.
ToDataJSON() ([]byte, error)
}
DomainEvent is the contract every emitted event must satisfy — methods rather than fields so events can embed metadata however suits their JSON shape.
type Error ¶
type Error struct {
Kind Kind
Code string
Message string
Details map[string]any
// Cause is the wrapped error, if any.
Cause error
}
Error is the canonical use case error. Implements the standard error interface; inspect with errors.As(err, &usecase.Error{}).
func Authorization ¶
Authorization builds an authorization error.
func BusinessRule ¶
BusinessRule builds a business rule violation error.
func Validation ¶
Validation builds a validation error. Use for field-level checks.
func (*Error) HTTPStatus ¶
HTTPStatus maps the Kind to an HTTP status code.
type EventMetadata ¶
type EventMetadata struct {
EventID string `json:"eventId"`
SpecVersion string `json:"specVersion"`
Source string `json:"source"`
Type string `json:"type"`
Subject string `json:"subject"`
OccurredAt time.Time `json:"time"`
CorrelationID string `json:"correlationId,omitempty"`
CausationID string `json:"causationId,omitempty"`
PrincipalID string `json:"principalId,omitempty"`
ExecutionID string `json:"executionId,omitempty"`
MessageGroup string `json:"messageGroup,omitempty"`
}
EventMetadata is the CloudEvents-shaped envelope embedded into every domain event. Implementations typically embed this struct and provide a custom MarshalJSON that flattens it alongside the event-specific data fields.
Concrete events still implement the DomainEvent interface explicitly (Go's field/method namespace overlap means you can't have both `EventID string` and `EventID() string` on the same struct). The methods typically delegate to the embedded EventMetadata fields:
func (e EventFoo) EventID() string { return e.Metadata.EventID }
func (e EventFoo) Time() time.Time { return e.Metadata.OccurredAt }
func (e EventFoo) CorrelationID() string { return e.Metadata.CorrelationID }
... etc
func NewEventMetadata ¶
func NewEventMetadata(ec ExecutionContext, eventType, source, subject string) EventMetadata
NewEventMetadata builds an EventMetadata from an ExecutionContext plus the per-event fields. Convenience constructor — events can also construct EventMetadata field-by-field for full control.
type ExecutionContext ¶
type ExecutionContext struct {
// PrincipalID is the acting user or service account.
PrincipalID string
// CorrelationID is propagated from inbound requests; new requests
// generate a fresh ID.
CorrelationID string
// CausationID points to the event that caused this one (for chains).
// Empty for top-level / user-initiated actions.
CausationID string
// ExecutionID is unique per use case invocation.
ExecutionID string
// InitiatedAt is when this context was created.
InitiatedAt time.Time
}
ExecutionContext threads trace identifiers and the acting principal through the use case pipeline.
func FromParentEvent ¶
func FromParentEvent(parent DomainEvent, principalID string) ExecutionContext
FromParentEvent derives a context from a parent event, preserving the correlation chain and recording the parent as the causation.
func NewExecutionContext ¶
func NewExecutionContext(principalID string) ExecutionContext
NewExecutionContext creates a fresh context with the given principal and freshly-generated correlation + execution IDs.
CorrelationID is seeded from ExecutionID for top-level requests. Use WithCorrelation when an inbound request already carries one.
func WithCorrelation ¶
func WithCorrelation(principalID, correlationID string) ExecutionContext
WithCorrelation creates a context using an inbound correlation ID. Use this when handling a request that originated upstream and already carries trace identifiers.
func (ExecutionContext) WithCausation ¶
func (ec ExecutionContext) WithCausation(causationID string) ExecutionContext
WithCausation returns a copy with the causation ID set, for events emitted as a consequence of another event.
type HasID ¶
type HasID interface {
IDStr() string
}
HasID is implemented by every aggregate so the UnitOfWork can identify the row being committed without reflection.
The method is named IDStr (not ID) so it doesn't collide with the typical exported `ID string` field on entity structs. Every entity provides:
type Foo struct { ID string `json:"id"` ... }
func (f *Foo) IDStr() string { return f.ID }
type Kind ¶
type Kind string
Kind classifies a use case error. The Kind drives the HTTP status code mapping in API handlers.
type Result ¶
type Result[E any] interface { // contains filtered or unexported methods }
Result is the outcome of a use case execution. It's a sealed sum with exactly two implementors: success[E] (constructible only via Success) and failure[E] (constructible by anyone via Failure).
func Failure ¶
Failure constructs a Result wrapping an error. Public — anyone can return a Failure for validation errors, business rule violations, authorization failures, etc.