Documentation
¶
Overview ¶
Package readmodels provides explicit model declarations and namespace-bound, presence-aware reads. Models describe kernel-owned state, not local projections.
Index ¶
- Constants
- Variables
- func ValidateWatchOptions(options ...WatchOption) error
- type Catalog
- type Change
- type ChangeType
- type CodecPanicError
- type Collection
- type DecisionCodecPanicError
- type DecisionRead
- type DecisionReadAdmission
- type DecisionReadRefusalReason
- type DecisionReadRefused
- type DecisionReader
- type Descriptor
- func (d Descriptor) ContainerName() string
- func (d Descriptor) DisplayName() string
- func (d Descriptor) EventSequence() events.SequenceID
- func (d Descriptor) Fields() []serialization.Field
- func (d Descriptor) Generation() Generation
- func (d Descriptor) GoType() reflect.Type
- func (d Descriptor) HasExplicitEventSequence() bool
- func (d Descriptor) Identifier() Identifier
- func (d Descriptor) Indexes() []string
- func (d Descriptor) KeyProperty() string
- func (d Descriptor) Marshal(value any) ([]byte, error)
- func (d Descriptor) Observer() (ObserverType, string)
- func (d Descriptor) RebindJSON(data []byte, original Descriptor) ([]byte, error)
- func (d Descriptor) Schema() string
- func (d Descriptor) Sink() Sink
- func (d Descriptor) Unmarshal(data json.RawMessage) (any, error)
- func (d Descriptor) ValidateProducer() error
- func (d Descriptor) WithDefaultSinkType(kind SinkType) (Descriptor, error)
- func (d Descriptor) WithNamingPolicy(policy serialization.NamingPolicy) (Descriptor, error)
- type Generation
- type Identifier
- type Instance
- type Key
- type MaterializedReader
- type MaterializedService
- type Model
- type ModelOption
- func Passive() ModelOption
- func WithCodecs(codecs *serialization.Codecs) ModelOption
- func WithContainerName(name string) ModelOption
- func WithDisplayName(name string) ModelOption
- func WithEventSequence(sequence events.SequenceID) ModelOption
- func WithGeneration(generation Generation) ModelOption
- func WithIdentifier(id Identifier) ModelOption
- func WithIndexes(paths ...string) ModelOption
- func WithObserver(kind ObserverType, id string) ModelOption
- func WithPII(paths ...string) ModelOption
- func WithProtection(declarations ...compliance.Declaration) ModelOption
- func WithSink(sink Sink) ModelOption
- func WithSubjectProperty(path string) ModelOption
- type ObserverType
- type PassiveReader
- type ProjectionReplayValidator
- type Reader
- func (r *Reader[T]) Get(ctx context.Context, key Key) (Instance[T], error)
- func (r *Reader[T]) GetAll(ctx context.Context, count *events.Count) (Collection[T], error)
- func (r *Reader[T]) GetSnapshots(ctx context.Context, key Key) ([]Snapshot[T], error)
- func (r *Reader[T]) Materialized() *MaterializedReader[T]
- func (r *Reader[T]) NewSession(key Key) (*Session[T], error)
- func (r *Reader[T]) Release(ctx context.Context, value T) (T, error)
- func (r *Reader[T]) ReleaseMany(ctx context.Context, values []T) ([]T, error)
- func (r *Reader[T]) Watch(ctx context.Context, options ...WatchOption) (*Subscription[Change[T]], error)
- type ReducerCollectionReader
- type ReductionChanges
- type ReleaseError
- type Service
- func (s *Service) Catalog() *Catalog
- func (s *Service) Get(ctx context.Context, model Identifier, key Key) (Instance[json.RawMessage], error)
- func (s *Service) GetAll(ctx context.Context, model Identifier, count *events.Count) (Collection[json.RawMessage], error)
- func (s *Service) GetSnapshots(ctx context.Context, model Identifier, key Key) ([]Snapshot[json.RawMessage], error)
- func (s *Service) GetValue(ctx context.Context, typ reflect.Type, key Key) (any, error)
- func (s *Service) Materialized() *MaterializedService
- func (s *Service) Release(ctx context.Context, model Identifier, document json.RawMessage) (json.RawMessage, error)
- func (s *Service) ReplayProjection(ctx context.Context, model Identifier, eventCount uint64) ([]json.RawMessage, error)
- func (s *Service) Watch(ctx context.Context, model Identifier, options ...WatchOption) (*Subscription[Change[json.RawMessage]], error)
- type ServiceOption
- func WithPassiveReader(reader PassiveReader) ServiceOption
- func WithProjectionReplayValidator(validator ProjectionReplayValidator) ServiceOption
- func WithReducerCollectionReader(reader ReducerCollectionReader) ServiceOption
- func WithReductionChanges(source *ReductionChanges) ServiceOption
- func WithReleasedPassiveReader(reader PassiveReader) ServiceOption
- func WithSnapshotEventCatalog(catalog *events.Catalog) ServiceOption
- type Session
- type Sink
- type SinkType
- type Snapshot
- type Subscription
- type WatchOption
- type Window
- type WindowDiffer
Examples ¶
Constants ¶
const UnlimitedInstances int32 = -1
UnlimitedInstances selects all instances after Skip (up to the kernel int32 limit).
Variables ¶
var ErrDecisionReadRefused = errors.New("chronicle: decision read refused")
ErrDecisionReadRefused identifies a decision that cannot safely issue a token.
var ErrDecisionRequiresUnitOfWork = errors.New("chronicle: decision read requires an existing unit of work")
ErrDecisionRequiresUnitOfWork means Get has no existing participant. Use GetDetached when the outer orchestrator will explicitly enroll later.
var ErrInterrupted = errors.New("chronicle: read-model subscription interrupted")
ErrInterrupted means a best-effort feed ended. There is no durable cursor: re-watch and re-fetch state; neither operation promises gap-free recovery.
var ErrOverloaded = errors.New("chronicle: read-model subscription overloaded")
ErrOverloaded means the subscription's bounded queue filled. No changes are silently dropped: the stream terminates and requires a new watch and snapshot.
var ErrRelease = errors.New("chronicle: read-model release failed")
ErrRelease identifies a failed protected-value release. No original ciphertext or partially released document is returned on this error.
Functions ¶
func ValidateWatchOptions ¶
func ValidateWatchOptions(options ...WatchOption) error
ValidateWatchOptions validates options without opening a stream. Registration adapters use it to reject invalid queue configuration before client startup.
Types ¶
type Catalog ¶
type Catalog struct {
// contains filtered or unexported fields
}
Catalog is an immutable, concurrency-safe collection. Use NewCatalog; the zero value is an empty catalog. Descriptors never retain caller-mutable collections.
func NewCatalog ¶
func NewCatalog(descriptors ...Descriptor) (*Catalog, error)
NewCatalog validates and snapshots declarations, rejecting duplicate types and identifiers. A model has one current generation; this is not a migration catalog.
func (*Catalog) Descriptors ¶
func (c *Catalog) Descriptors() []Descriptor
Descriptors returns a defensive copy in registration order.
func (*Catalog) Lookup ¶
func (c *Catalog) Lookup(value any) (Descriptor, bool)
Lookup resolves a value or non-nil pointer to a registered model.
func (*Catalog) LookupIdentifier ¶
func (c *Catalog) LookupIdentifier(id Identifier) (Descriptor, bool)
LookupIdentifier resolves the persisted model identity.
func (*Catalog) LookupType ¶
func (c *Catalog) LookupType(typ reflect.Type) (Descriptor, bool)
LookupType resolves a Go struct type or pointer to it, without constructing a value.
type Change ¶
type Change[T any] struct { // Type distinguishes addition, modification and removal. Type ChangeType // Key identifies the changed model, including when Value is absent. Key Key // Value is owned by the consumer and usable only when HasValue is true. Value T // HasValue is false when a removal carries no document. HasValue bool // Context carries available metadata; unspecified fields remain unknown. Context events.Context }
Change owns a change's key, optional model, and available causing-event metadata. Value is usable only with HasValue. Removed is always explicit in Type; a deletion may carry the old model, or no value. Context.EventType is unknown: the watch protocol does not identify it or provide full event audit metadata.
type ChangeType ¶
type ChangeType uint8
ChangeType classifies a read-model change, not an event-log delivery.
const ( // Added is a new model (or a model entering a materialized window). Added ChangeType = iota // Modified is a changed model already present. Modified // Removed is a deleted model, or one leaving a materialized window. Removed )
type CodecPanicError ¶
type CodecPanicError struct{}
CodecPanicError means an application decoder panicked. It matches ErrProtocol and never retains the panic value, which may contain protected content.
func (*CodecPanicError) Error ¶
func (*CodecPanicError) Error() string
Error returns a fixed, payload-free description.
func (*CodecPanicError) Unwrap ¶
func (*CodecPanicError) Unwrap() error
Unwrap preserves the protocol category without exposing the panic payload.
type Collection ¶
type Collection[T any] struct { // Instances is non-nil on success, including an empty collection. Instances []Instance[T] // ProcessedEventsCount is the actual reported or locally folded event count. ProcessedEventsCount events.Count }
Collection owns present instances and the number of events reported by the operation. Deleted instances are omitted; present zero-valued models remain. ProcessedEventsCount is not a sequence position, sink watermark or freshness guarantee. Materialized retrieval reports zero even when instances exist.
type DecisionCodecPanicError ¶
type DecisionCodecPanicError struct{}
DecisionCodecPanicError means an application model decoder panicked during a decision read. It matches ErrProtocol and never retains or formats the panic value. The read returns no model or token; cleanup already completed.
func (*DecisionCodecPanicError) Error ¶
func (*DecisionCodecPanicError) Error() string
Error returns a fixed, payload-free description.
func (*DecisionCodecPanicError) Unwrap ¶
func (*DecisionCodecPanicError) Unwrap() error
Unwrap preserves the protocol failure category without exposing a panic value.
type DecisionRead ¶
type DecisionRead[T any] struct { Instance Instance[T] // Instance preserves removal progress even when absent. Token transactions.DecisionToken // Token can be enrolled only by one unit/owner. }
DecisionRead contains explicitly present/absent model state and an opaque optimistic guard. Every failure returns its zero value, never usable evidence.
type DecisionReadAdmission ¶
type DecisionReadAdmission struct {
IsAdmitted bool // IsAdmitted means the frozen local shape is supported.
Reason DecisionReadRefusalReason // Reason is empty on admission.
}
DecisionReadAdmission reports local shape admission without network I/O. It does not establish server agreement, capabilities or a protected read.
type DecisionReadRefusalReason ¶
type DecisionReadRefusalReason string
DecisionReadRefusalReason is a stable admission or read refusal category.
const ( DecisionReducer DecisionReadRefusalReason = "reducer" // DecisionReducer excludes reducer-backed models. DecisionAmbiguousProjection DecisionReadRefusalReason = "ambiguous-projection" // DecisionAmbiguousProjection requires exactly one projection. DecisionNotEventLog DecisionReadRefusalReason = "not-event-log" // DecisionNotEventLog excludes other sequences. DecisionJoin DecisionReadRefusalReason = "join" // DecisionJoin excludes joins and removal joins. DecisionHierarchy DecisionReadRefusalReason = "hierarchy" // DecisionHierarchy excludes children and nested projections. DecisionOpenEndedEventTypes DecisionReadRefusalReason = "open-ended-event-types" // DecisionOpenEndedEventTypes excludes all-event subscriptions. DecisionDerivatives DecisionReadRefusalReason = "derivatives" // DecisionDerivatives excludes FromEvery derivatives. DecisionFromEventProperty DecisionReadRefusalReason = "from-event-property" // DecisionFromEventProperty excludes property-derived input. DecisionNotEventSourceKeyed DecisionReadRefusalReason = "not-event-source-keyed" // DecisionNotEventSourceKeyed requires source keys, including parents and All. DecisionNoEventTypes DecisionReadRefusalReason = "no-event-types" // DecisionNoEventTypes requires a finite nonempty dependency set. DecisionUnsupportedEventType DecisionReadRefusalReason = "unsupported-event-type" // DecisionUnsupportedEventType rejects unresolved or comma-containing IDs. DecisionKeyConversion DecisionReadRefusalReason = "key-conversion" // DecisionKeyConversion requires a string or UUID schema key. DecisionInvalidKey DecisionReadRefusalReason = "invalid-key" // DecisionInvalidKey rejects noncanonical and wildcard keys. DecisionDefinitionMismatch DecisionReadRefusalReason = "definition-mismatch" // DecisionDefinitionMismatch means the server disagrees with admission. DecisionFoldIncomplete DecisionReadRefusalReason = "fold-incomplete" // DecisionFoldIncomplete exhausts three fresh fold attempts. DecisionFoldAhead DecisionReadRefusalReason = "fold-ahead" // DecisionFoldAhead exhausts three fresh pre-fold boundaries. )
const DecisionProtectedModel DecisionReadRefusalReason = "protected-model"
DecisionProtectedModel formerly excluded every classified model from session-based decisions.
Deprecated: no longer produced. Kernel 19.32.2 and later release decision session folds with each value's original subject (Chronicle#4561), so classified models are admitted like C#.
const DecisionProtectionMetadata DecisionReadRefusalReason = "protection-metadata"
DecisionProtectionMetadata formerly meant model protection could not be determined.
Deprecated: no longer produced; admission does not inspect protection metadata.
type DecisionReadRefused ¶
type DecisionReadRefused struct {
Model Identifier // Model identifies the refused model.
Reason DecisionReadRefusalReason // Reason describes admission/agreement/fold failure.
}
DecisionReadRefused identifies the model and stable reason; it never contains the potentially sensitive instance key or model content.
func (*DecisionReadRefused) Error ¶
func (e *DecisionReadRefused) Error() string
func (*DecisionReadRefused) Unwrap ¶
func (e *DecisionReadRefused) Unwrap() error
type DecisionReader ¶
type DecisionReader[T any] struct { // contains filtered or unexported fields }
DecisionReader is a concurrency-safe admitted optimistic reader. Construct it with DecisionsFor; its zero value is invalid. It owns no client resources.
func DecisionsFor ¶
func DecisionsFor[T any](service *Service, model Model[T]) *DecisionReader[T]
DecisionsFor selects a typed decision reader without I/O. Only EventStore's frozen projection catalogs support issuance; low-level/test substitutes do not.
func (*DecisionReader[T]) Admit ¶
func (r *DecisionReader[T]) Admit() DecisionReadAdmission
Admit inspects the frozen catalog only. Refused declarations never issue RPCs.
func (*DecisionReader[T]) Get ¶
func (r *DecisionReader[T]) Get(ctx context.Context, key Key) (read DecisionRead[T], err error)
Get reads and enrolls into the existing participant in ctx. It never creates an implicit unit or exposes its owner. Failed enrollment returns no read/token.
func (*DecisionReader[T]) GetDetached ¶
func (r *DecisionReader[T]) GetDetached(ctx context.Context, key Key) (read DecisionRead[T], err error)
GetDetached uses at most three fresh projection sessions, without enrolling or creating a unit. Classified models are admitted; the kernel releases their folds. It checks server agreement before and after each fold, captures an unfiltered pre-fold log boundary, and probes the source's dependency types separately. LastHandled is progress, never a proof or replacement boundary. Cleanup is awaited, with a five-second cancellation-detached metadata-preserving budget; cleanup/cancellation/generation/epoch failure returns no token. Application codecs run only after cleanup, agreement checks and release of counted RPC work; cancellation, connection generation and catalog epoch are rechecked afterward. The pinned protocol cannot atomically bind definitions or in-place history changes. Codec panics return DecisionCodecPanicError without retaining the panic value. Errors have payload-free messages; underlying returned causes remain deliberately inspectable through errors.Is/As and may contain sensitive transport diagnostics.
type Descriptor ¶
type Descriptor struct {
// contains filtered or unexported fields
}
Descriptor is immutable model metadata. Its zero value is invalid.
func BindProjection ¶
func BindProjection(d Descriptor, id string, sequence events.SequenceID, passive bool) (Descriptor, error)
BindProjection returns a new descriptor associated with a compiled producer. It is a registry-composition hook, not runtime registration. Explicit conflicting observer/sequence settings fail; passive projections use None rather than a materialized sink. Existing handles still refer to the same model declaration.
func BindReducer ¶
func BindReducer(d Descriptor, id string, sequence events.SequenceID, passive bool) (Descriptor, error)
BindReducer associates a model with exactly one reducer at registry freeze. Existing projection ownership and explicit conflicting sequences are errors. A passive model uses no sink; inactive materialized reducers retain their sink.
func (Descriptor) ContainerName ¶
func (d Descriptor) ContainerName() string
ContainerName returns the sink container, not the model identifier.
func (Descriptor) DisplayName ¶
func (d Descriptor) DisplayName() string
DisplayName returns the human-facing name.
func (Descriptor) EventSequence ¶
func (d Descriptor) EventSequence() events.SequenceID
EventSequence returns the sequence used by immediate reads and session cleanup.
func (Descriptor) Fields ¶
func (d Descriptor) Fields() []serialization.Field
Fields returns detached metadata from the same plan used by the model schema.
func (Descriptor) Generation ¶
func (d Descriptor) Generation() Generation
Generation returns the schema generation.
func (Descriptor) GoType ¶
func (d Descriptor) GoType() reflect.Type
GoType returns the declared, non-pointer struct type.
func (Descriptor) HasExplicitEventSequence ¶
func (d Descriptor) HasExplicitEventSequence() bool
HasExplicitEventSequence reports whether the model overrides source inference.
func (Descriptor) Identifier ¶
func (d Descriptor) Identifier() Identifier
Identifier returns the stable persisted identity.
func (Descriptor) Indexes ¶
func (d Descriptor) Indexes() []string
Indexes returns copied serialized property paths, in declaration order.
func (Descriptor) KeyProperty ¶
func (d Descriptor) KeyProperty() string
KeyProperty returns the serialized root ID property, or empty when undeclared.
func (Descriptor) Marshal ¶
func (d Descriptor) Marshal(value any) ([]byte, error)
Marshal serializes a value or non-nil pointer of this model's type. The caller must not mutate it during the call. This does not apply encryption.
func (Descriptor) Observer ¶
func (d Descriptor) Observer() (ObserverType, string)
Observer returns the producer kind and identifier. An empty identifier means no producer was declared.
func (Descriptor) RebindJSON ¶
func (d Descriptor) RebindJSON(data []byte, original Descriptor) ([]byte, error)
RebindJSON returns a detached serialized snapshot with this descriptor's names. The original descriptor must describe the same Go type. Explicit nulls and omissions survive; no user callbacks or encryption are invoked.
func (Descriptor) Schema ¶
func (d Descriptor) Schema() string
Schema returns the shared serialization plan's schema with explicit read-model metadata.
func (Descriptor) Unmarshal ¶
func (d Descriptor) Unmarshal(data json.RawMessage) (any, error)
Unmarshal decodes a present model document into a pointer to its registered Go type, normalizing declared collections like Reader.Get. The result is owned. Decoder panics return CodecPanicError without retaining the panic value. Runtime adapters can use this with a catalog; ordinary callers should use Reader.
func (Descriptor) ValidateProducer ¶
func (d Descriptor) ValidateProducer() error
ValidateProducer checks deferred passive-model requirements at registry freeze.
func (Descriptor) WithDefaultSinkType ¶
func (d Descriptor) WithDefaultSinkType(kind SinkType) (Descriptor, error)
WithDefaultSinkType returns detached metadata with a materialized client default. Explicit WithSink selections and passive producer bindings win. This registry-composition hook preserves declaration identity, schema and codec snapshots; it performs no I/O and invokes no application callbacks.
func (Descriptor) WithNamingPolicy ¶
func (d Descriptor) WithNamingPolicy(policy serialization.NamingPolicy) (Descriptor, error)
WithNamingPolicy compiles detached property metadata for registry composition. Container names and declaration identity are unchanged; existing typed handles remain valid when used with the resulting client catalog. Provider classifications are frozen by Go member identity for subsequent naming and producer binding.
type Generation ¶
type Generation uint32
Generation identifies a schema generation, starting at one.
type Identifier ¶
type Identifier string
Identifier is the persisted, store-wide model identity, independent of its sink container.
type Instance ¶
type Instance[T any] struct { // Value is usable only when Exists is true. Value T // Exists distinguishes absent state from a legitimate zero-valued model. Exists bool // LastHandled is the reported position, or nil when unavailable. LastHandled *events.SequenceNumber }
Instance distinguishes absence from a valid zero-valued model. Value is usable only when Exists is true. LastHandled is nil only for the unavailable sentinel; an absent (removed) model can still have a last-handled position.
type MaterializedReader ¶
type MaterializedReader[T any] struct { // contains filtered or unexported fields }
MaterializedReader is the typed materialized-window API. Construct through Reader.Materialized.
func (*MaterializedReader[T]) GetInstances ¶
func (m *MaterializedReader[T]) GetInstances(ctx context.Context, window *Window) ([]T, error)
GetInstances is the typed equivalent of MaterializedService.GetInstances.
func (*MaterializedReader[T]) ObserveInstances ¶
func (m *MaterializedReader[T]) ObserveInstances(ctx context.Context, window *Window, options ...WatchOption) (*Subscription[[]T], error)
ObserveInstances is the typed equivalent of MaterializedService.ObserveInstances.
type MaterializedService ¶
type MaterializedService struct {
// contains filtered or unexported fields
}
MaterializedService reads sink windows. It does not execute passive projections or reducers, and leaving a window does not prove that a document was deleted.
func (*MaterializedService) GetInstances ¶
func (m *MaterializedService) GetInstances(ctx context.Context, model Identifier, window *Window) ([]json.RawMessage, error)
GetInstances reads the exact requested window. Nil means 0/50. Results own their JSON. The kernel releases stored values; the SDK validates the final representation without decrypting again. Protected windows are limited to MongoDB root string PII and namespace-encrypted properties excluding sink identity aliases; other classified profiles fail with ErrUnsupported before RPC.
func (*MaterializedService) ObserveInstances ¶
func (m *MaterializedService) ObserveInstances(ctx context.Context, model Identifier, window *Window, options ...WatchOption) (*Subscription[[]json.RawMessage], error)
ObserveInstances returns after the first snapshot has been decoded. Unlike Watch this RPC has no Subscribed marker; the first Recv returns that snapshot. Each subsequent value replaces the entire window. Server-side coalescing can omit intermediate states. No durable changes, cursor or gap-free resume exist. Cancellation, overload, terminal errors and Close follow Subscription's contract.
type Model ¶
type Model[T any] struct { // contains filtered or unexported fields }
Model is a typed, immutable declaration, normally returned by chronicle.RegisterReadModel.
func Define ¶
func Define[T any](options ...ModelOption) (Model[T], error)
Define compiles T without registration or I/O. T must be a named, non-pointer struct accepted by serialization.Compile. Generic instantiations require explicit WithIdentifier and WithContainerName options. Most callers use RegisterReadModel.
func (Model[T]) Descriptor ¶
func (m Model[T]) Descriptor() Descriptor
Descriptor returns the immutable untyped model metadata.
func (Model[T]) Identifier ¶
func (m Model[T]) Identifier() Identifier
Identifier returns the persisted model identity.
type ModelOption ¶
type ModelOption func(*modelConfig)
ModelOption configures a declaration. Scalars are last-wins; property path options accumulate and reject duplicates. Slices are copied. Nil options and invalid final configurations fail before registry admission.
func Passive ¶
func Passive() ModelOption
Passive declares an on-demand reducer model. Register its reducer before NewClient; for projections use projections.Passive on the producer instead.
func WithCodecs ¶
func WithCodecs(codecs *serialization.Codecs) ModelOption
WithCodecs selects an immutable explicit derived codec set. The last option wins; nil clears it. Classification providers are frozen at declaration.
func WithContainerName ¶
func WithContainerName(name string) ModelOption
WithContainerName overrides the case-preserving pluralized simple type name, corresponding to C# ReadModelNameAttribute. It does not change the identifier.
func WithDisplayName ¶
func WithDisplayName(name string) ModelOption
WithDisplayName overrides the simple type name used for display.
func WithEventSequence ¶
func WithEventSequence(sequence events.SequenceID) ModelOption
WithEventSequence selects the sequence for immediate reads (default event-log).
func WithGeneration ¶
func WithGeneration(generation Generation) ModelOption
WithGeneration selects a positive schema generation (default one).
func WithIdentifier ¶
func WithIdentifier(id Identifier) ModelOption
WithIdentifier overrides the default full Go import path plus type name. Use an explicit C# full name to share a persisted model across languages.
func WithIndexes ¶
func WithIndexes(paths ...string) ModelOption
WithIndexes declares nested serialized property paths, including paths through collection items. Calls accumulate paths in declaration order. Unknown paths and duplicates within or across calls fail declaration with ErrInvalidConfiguration.
func WithObserver ¶
func WithObserver(kind ObserverType, id string) ModelOption
WithObserver associates a registered producer. It does not register that producer.
func WithPII ¶
func WithPII(paths ...string) ModelOption
WithPII marks properties as personal data in the model schema. Paths use serialized names and accumulate across calls. Duplicate paths within or across calls fail declaration with ErrInvalidConfiguration. Composite values propagate to leaves; explicitly classified collections retain their container classification. Tags and WithProtection use the same pipeline.
func WithProtection ¶
func WithProtection(declarations ...compliance.Declaration) ModelOption
WithProtection adds copied type/property classifications and providers. Tags need no option. Invalid or conflicting metadata fails before any connection I/O.
func WithSink ¶
func WithSink(sink Sink) ModelOption
WithSink replaces the default MongoDB/default-configuration sink.
func WithSubjectProperty ¶
func WithSubjectProperty(path string) ModelOption
WithSubjectProperty selects a top-level serialized scalar property for Release. Empty/unset values fall back to the model's ID field, as in C#.
type ObserverType ¶
type ObserverType uint8
ObserverType identifies the definition that produces this model.
const ( // Projection is the C# default observer type. Projection ObserverType = 2 // Reducer identifies a reducer-backed model. Passive instances fold locally; // reducer sessions are not supported. Reducer ObserverType = 1 )
type PassiveReader ¶
type PassiveReader func(context.Context, Descriptor, Key) (Instance[json.RawMessage], error)
PassiveReader is the store's in-process reducer seam. It returns raw state; Service performs protected-value release before returning it to the caller.
type ProjectionReplayValidator ¶
type ProjectionReplayValidator func(context.Context, Descriptor) (known bool, err error)
ProjectionReplayValidator admits a model only when its producer's defaults and key/relationship shape are faithfully supported by kernel collection/history replay. It returns known=false, err=nil when no local producer is known. New collection/history APIs refuse unknown producers; legacy ReplayProjection preserves its caller-owned admission contract in that case.
type Reader ¶
type Reader[T any] struct { // contains filtered or unexported fields }
Reader is a typed, concurrency-safe reader for one immutable model declaration. Its zero value is invalid; construct it with For.
func For ¶
For creates a lazy typed reader. A nil service, empty or foreign declaration is rejected by operations with ErrNotRegistered; no network I/O occurs here.
func (*Reader[T]) Get ¶
Get reads one instance. Missing models remain Exists=false; declared slice properties in present models normalize missing/null to empty (pointer-to-slice declarations retain nullable presence). LastHandled is never folded into T.
func (*Reader[T]) GetAll ¶
GetAll decodes the entire collection with the registered serialization plan. No partial collection or progress is returned after decoding or cancellation fails. Codecs run outside transport leases and may close their client.
Example ¶
package main
import (
"context"
"errors"
"fmt"
contracts "github.com/cratis/chronicle.go/contracts/readmodels"
"github.com/cratis/chronicle.go/events"
"github.com/cratis/chronicle.go/readmodels"
"google.golang.org/grpc"
)
// collectionExampleTransport is an offline response fixture, not kernel replay
// or storage. The real-kernel specs establish the routing and state semantics.
type collectionExampleTransport struct{}
func (collectionExampleTransport) Invoke(_ context.Context, method string, request, response any, _ ...grpc.CallOption) error {
if method != contracts.ReadModels_GetAllInstances_FullMethodName {
return errors.New("unexpected RPC")
}
r := response.(*contracts.GetAllInstancesResponse)
r.Instances = []string{`{"id":"account-1","name":"Open"}`}
if request.(*contracts.GetAllInstancesRequest).EventCount != uint64(events.UnlimitedCount) {
r.ProcessedEventsCount = 2
}
return nil
}
func (collectionExampleTransport) NewStream(context.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) {
return nil, errors.New("unexpected stream")
}
func main() {
type Account struct {
ID string `json:"id"`
Name string `json:"name"`
}
model, err := readmodels.Define[Account](readmodels.WithObserver(readmodels.Projection, "accounts"))
if err != nil {
panic(err)
}
catalog, err := readmodels.NewCatalog(model.Descriptor())
if err != nil {
panic(err)
}
// Production uses store.ReadModels(). This offline adapter owns the fixture's
// simple source-key/empty-default producer evidence.
service, err := readmodels.New("accounts", "Default", catalog, collectionExampleTransport{}, readmodels.WithProjectionReplayValidator(allowReplay))
if err != nil {
panic(err)
}
reader := readmodels.For(service, model)
ctx := context.Background()
current, err := reader.GetAll(ctx, nil)
if err != nil {
panic(err)
}
fmt.Println(current.Instances[0].Value.ID, current.ProcessedEventsCount)
count := events.Count(2)
historical, err := reader.GetAll(ctx, &count)
if err != nil {
panic(err)
}
fmt.Println(historical.ProcessedEventsCount, historical.Instances[0].LastHandled == nil)
// Count two does not invent last-handled position one.
Output:
func (*Reader[T]) GetSnapshots ¶
GetSnapshots decodes each state through the frozen model plan; contributions remain raw and can be decoded using events.Decode and the event catalog. No partial snapshot or metadata is returned on late codec failure/cancellation.
func (*Reader[T]) Materialized ¶
func (r *Reader[T]) Materialized() *MaterializedReader[T]
Materialized selects sink pages rather than projection changes or passive reads.
func (*Reader[T]) NewSession ¶
NewSession creates a lazy hydration session with an internally generated UUID. The first Get hydrates; each Get uses that same session. Reducers and models without a declared projection fail with ErrUnsupported. Classified values are released by the kernel with the subject each value was written under (kernel 19.32.2 or later, Chronicle#4561); against an older or unverified kernel, Get of a classified model fails with ErrUnsupported before RPC. Call Close even after failed Get, because the kernel may already have allocated hydration state.
func (*Reader[T]) Release ¶
Release decrypts a value fetched directly from a sink. No value is returned on failure. T must carry its subject; use Service.Release for raw stored lineage.
func (*Reader[T]) ReleaseMany ¶
ReleaseMany releases unreleased sink values in input order. It is not idempotent: never pass server-released read results, since legitimate plaintext can resemble ciphertext. Input values are never modified. Any failure returns a nil slice, never partially released data. Nil input remains nil.
func (*Reader[T]) Watch ¶
func (r *Reader[T]) Watch(ctx context.Context, options ...WatchOption) (*Subscription[Change[T]], error)
Watch is the typed equivalent of Service.Watch, including its ready barrier, bounded queue, terminal errors, namespace isolation and Close obligation.
type ReducerCollectionReader ¶
type ReducerCollectionReader func(context.Context, Descriptor, events.Count) (Collection[json.RawMessage], error)
ReducerCollectionReader folds released matching history locally. It returns owned plaintext, actual progress and present instances only. The callback must honor cancellation and support concurrent calls. It must not publish changes or acknowledge an observer. No counted transport lease spans this callback.
type ReductionChanges ¶
type ReductionChanges struct {
// contains filtered or unexported fields
}
ReductionChanges is a store/namespace-local notification source for successful in-process folds, not a durable kernel change feed. The client/store coordinate owns it; adapters may construct its zero value, bind a generation, and publish folds. It is concurrency-safe and invokes no callbacks while holding its lock.
func (*ReductionChanges) BindGeneration ¶
func (h *ReductionChanges) BindGeneration(ctx context.Context)
BindGeneration binds future local watches to a client generation. Rebinding terminates old feeds; already returned watches never silently switch generation. The context must be a client lifetime, not a short registration/request context.
func (*ReductionChanges) Publish ¶
func (h *ReductionChanges) Publish(generation context.Context, model Identifier, key Key, value json.RawMessage)
Publish reports a successful local fold in its final plaintext representation, not an encrypted sink document. Nil means removed. Documents are snapshotted; the source never waits for slow watchers. Each overloaded watcher terminates independently and no failure is attributed to the durable reducer. generation must be the exact context passed to BindGeneration; late folds from retired generations are ignored. Notifications do not prove that the kernel persisted the returned fold yet.
type ReleaseError ¶
type ReleaseError struct {
// Cause is the inspectable underlying error. Do not log transport diagnostics
// without considering whether the server included sensitive data.
Cause error
}
ReleaseError preserves the underlying transport/protocol cause without including model data, subject values or potentially sensitive kernel error messages.
func (*ReleaseError) Error ¶
func (e *ReleaseError) Error() string
Error returns a payload-free description.
func (*ReleaseError) Unwrap ¶
func (e *ReleaseError) Unwrap() []error
Unwrap exposes ErrRelease and the underlying failure to errors.Is/As.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is a concurrency-safe namespace-bound reader. It borrows its transport and catalog. Use EventStore.ReadModels; New is for adapters and test transports.
func New ¶
func New(store metadata.StoreName, namespace metadata.Namespace, catalog *Catalog, conn grpc.ClientConnInterface, options ...ServiceOption) (*Service, error)
New constructs a service without I/O. The caller owns the channel and any registration/readiness barriers; the store facade supplies those automatically.
func (*Service) Get ¶
func (s *Service) Get(ctx context.Context, model Identifier, key Key) (Instance[json.RawMessage], error)
Get reads raw JSON by registered model identity and key. JSON null means absent; an empty/malformed/non-object response fails with ErrProtocol. Raw JSON is owned by the caller and retains kernel metadata except the root ID alias, which is normalized to the model's serialized property name. Materialized, projection immediate and session reads are server-released and validated without another decrypt. Classified projection immediate/session reads need Chronicle 19.32.2 or later (Chronicle#4561) and fail with ErrUnsupported before RPC unless the connection reports such a kernel.
func (*Service) GetAll ¶
func (s *Service) GetAll(ctx context.Context, model Identifier, count *events.Count) (Collection[json.RawMessage], error)
GetAll reads a collection. A nil count selects materialized state unless the model is passive. Every explicit reducer count folds locally, even Unlimited. Finite counts select the first N matching events globally, not per instance. Zero returns empty without I/O or reducer activation; finite counts above MaxInt32 fail rather than clamp. Local folds fetch complete matching history: the count bounds computation, not transport volume. Failures return no partial instances or progress. This operation allocates no hydration session.
func (*Service) GetSnapshots ¶
func (s *Service) GetSnapshots(ctx context.Context, model Identifier, key Key) ([]Snapshot[json.RawMessage], error)
GetSnapshots reads correlation-grouped projection history for a nonblank key. Reducers fail with ErrUnsupported before any RPC: kernel 19.29.4 returns an uninformative empty history for every non-projection. Replay fidelity admission also refuses unknown/defaulted/relationship producers. Classified states are released by the kernel with their original subjects from 19.32.2; older or unverified kernels are refused with ErrUnsupported. Success returns a non-nil slice. Any error or cancellation discards all results. No sessions are created.
func (*Service) GetValue ¶
GetValue reads a registered model for a runtime-discovered Go parameter type. It uses the same collection normalization as Reader.Get. typ must name the model struct or *model. Missing models return a typed nil pointer; a value-type request fails with ErrNotRegistered rather than manufacturing present state. Most applications should prefer the statically typed Reader.Get API.
func (*Service) Materialized ¶
func (s *Service) Materialized() *MaterializedService
Materialized returns the raw materialized-window API for this namespace.
func (*Service) Release ¶
func (s *Service) Release(ctx context.Context, model Identifier, document json.RawMessage) (json.RawMessage, error)
Release resolves protected values in a raw sink document using its registered schema. Top-level properties are released in groups selected by __subjects, falling back to the resolved default subject. Persisted lineage is preserved. Missing subjects, kernel release errors and invalid replies fail closed. Unprotected documents are copied without a release RPC. This API accepts unreleased sink documents, not results from server-released reads. Release is not idempotent: legitimate plaintext can resemble ciphertext and must not be released twice. Namespace/global confidentiality does not require a subject. Subject-dependent values without an owner fail closed, even in mixed-scope models.
func (*Service) ReplayProjection ¶
func (s *Service) ReplayProjection(ctx context.Context, model Identifier, eventCount uint64) ([]json.RawMessage, error)
ReplayProjection computes all instances by replaying at most eventCount events through the kernel projection. This does not wait for or prove sink/observer catch-up. Counts above math.MaxInt32 are invalid; zero returns an empty non-nil result without an RPC. Reducers are explicitly unsupported here. The kernel releases classified values with the subject each was written under (19.32.2 or later, Chronicle#4561); the SDK never decrypts them again, and refuses classified models with ErrUnsupported unless the connection reports such a kernel. Returned JSON is owned and ID aliases are normalized by the same path as Get. No local projection engine is used.
func (*Service) Watch ¶
func (s *Service) Watch(ctx context.Context, model Identifier, options ...WatchOption) (*Subscription[Change[json.RawMessage]], error)
Watch opens a projection change feed and returns only after Subscribed. This barrier is NOT a current model snapshot. Changes are ordered as received; an interruption is terminal, never resumed. The context owns the full lifetime. Watches cannot join hydration sessions: the wire has no session selector. Classified models fail with ErrUnsupported before opening a stream: the pinned kernel cannot reliably release projection changes, and local reducer changes have no delivery-time erasure fence. For unclassified reducers, Watch attaches to this client's plaintext fold notifications (local readiness, no server Subscribed or sink-write guarantee). Local changes are Modified/Removed; reactors infer first-seen Added.
type ServiceOption ¶
ServiceOption configures an immutable read service at construction.
func WithPassiveReader ¶
func WithPassiveReader(reader PassiveReader) ServiceOption
WithPassiveReader installs the store's reducer reader. The callback is borrowed and must support concurrent calls and cancellation. Repeated options fail.
func WithProjectionReplayValidator ¶
func WithProjectionReplayValidator(validator ProjectionReplayValidator) ServiceOption
WithProjectionReplayValidator installs immutable producer admission for new collection/history reads. Repeated options fail. The callback must support concurrent calls and cancellation; it must not evaluate projections locally.
func WithReducerCollectionReader ¶
func WithReducerCollectionReader(reader ReducerCollectionReader) ServiceOption
WithReducerCollectionReader installs the store's local collection fold. Unlike the legacy PassiveReader seam, results are already plaintext. Repeats fail.
func WithReductionChanges ¶
func WithReductionChanges(source *ReductionChanges) ServiceOption
WithReductionChanges installs the store's local reducer notification source. It is borrowed, and must be bound to a generation before Watch. Each watch has two bounded queues: raw local notifications and decoded consumer deliveries.
func WithReleasedPassiveReader ¶
func WithReleasedPassiveReader(reader PassiveReader) ServiceOption
WithReleasedPassiveReader installs a passive reader that folds already released events into plaintext. It preserves the legacy PassiveReader signature without decrypting its result again. Do not combine with WithPassiveReader.
func WithSnapshotEventCatalog ¶
func WithSnapshotEventCatalog(catalog *events.Catalog) ServiceOption
WithSnapshotEventCatalog installs frozen event schemas for protected snapshot contribution validation. Known protected generations validate their released representation before delivery; unknown types stay raw. The store supplies this automatically. Nil/repeated catalogs fail; no application codec runs here.
type Session ¶
type Session[T any] struct { // contains filtered or unexported fields }
Session owns one projection hydration session, bound to one reader/key. Get and Close serialize with cancellable admission. It must not be copied. Close before closing the client; the kernel session is not a protected decision token or a snapshot guarantee, and is not automatically replayed after connection loss.
func (*Session[T]) Close ¶
Close dehydrates using the exact model, key, namespace, sequence and session ID. It accepts a cleanup context rather than hiding I/O behind an unbounded Close. Success is idempotent; failed cleanup is reported and may be retried with a fresh context. No further Get is admitted once cleanup starts.
type Sink ¶
type Sink struct {
// Type selects one of the supported kernel sink providers.
Type SinkType
// ConfigurationID selects a server-managed connection configuration.
ConfigurationID string
}
Sink selects kernel-owned storage. ConfigurationID is a canonical UUID string; empty means the all-zero default configuration. No database driver is required.
type Snapshot ¶
type Snapshot[T any] struct { // Instance is the projected state, not a presence-bearing Instance[T]. Instance T // Events contains raw, server-released contributions with their full context. // Event IDs, original content, revisions and alternate generations are absent // from this RPC and are not fabricated. Unknown event types remain raw. Events []events.Appended // Occurred is the first contributing event's timestamp, not commit time. Occurred time.Time // CorrelationID belongs to this group, not the response envelope. CorrelationID metadata.CorrelationID }
Snapshot owns a correlation-grouped projection state and its contributions. The kernel groups correlation globally: A/B/A produces groups A then B, not three contiguous groups. Neither existence nor last-handled progress is present on this wire; an empty object after removal is indistinguishable from a present empty model. Snapshots are not optimistic-concurrency or decision-read evidence.
type Subscription ¶
type Subscription[T any] struct { // contains filtered or unexported fields }
Subscription owns one best-effort stream and its receive worker. One consumer may call Recv or iterate Values. Close is concurrency-safe and joins the worker; always close after early consumption. Buffered items precede terminal errors. Its zero value is invalid. It never reconnects or implies a current snapshot.
func (*Subscription[T]) Close ¶
func (s *Subscription[T]) Close() error
Close cancels and joins the receive worker. Repeated calls are safe. It returns nil for successful cleanup; inspect Err or Recv for the terminal stream outcome.
func (*Subscription[T]) Done ¶
func (s *Subscription[T]) Done() <-chan struct{}
Done closes when the receive worker has exited (buffered items may remain).
func (*Subscription[T]) Err ¶
func (s *Subscription[T]) Err() error
Err returns nil while running and the retained error after termination.
func (*Subscription[T]) Recv ¶
func (s *Subscription[T]) Recv() (T, error)
Recv returns the next owned value, or the retained terminal error. Cancellation is controlled by the context supplied when opening the subscription.
type WatchOption ¶
type WatchOption func(*watchConfig)
WatchOption configures a subscription. Scalar options are last-wins.
func WithWatchBuffer ¶
func WithWatchBuffer(messages, bytes int) WatchOption
WithWatchBuffer bounds queued messages and their serialized bytes (defaults: 64 messages, 8 MiB). Both must be positive. A single oversized message also terminates with ErrOverloaded. Decoded Go values and gRPC's own buffers are extra.
type Window ¶
type Window struct {
// Skip is the number of instances to skip; negative values mean zero.
Skip int32
// Take is the maximum count; -1 means unlimited, other nonpositive values empty.
Take int32
}
Window selects a skip/take range, not an event-count replay. A nil *Window uses Skip=0, Take=50. Negative Skip becomes zero; Take<=0 is empty except -1 (unlimited). Non-aligned ranges fetch a covering page from the start, capped at MaxInt32.
type WindowDiffer ¶
type WindowDiffer struct {
// contains filtered or unexported fields
}
WindowDiffer compares released, normalized model JSON by key. It is single- consumer state, scoped to one subscription. A removed item has no Value and only means it left this window, not that it was deleted from the sink. Unlike C#'s silent skip/overwrite, missing or duplicate keys fail explicitly. The zero value is usable; Reset is achieved by constructing a new differ.
func (*WindowDiffer) Diff ¶
func (d *WindowDiffer) Diff(model Descriptor, window []json.RawMessage) ([]Change[json.RawMessage], error)
Diff emits additions/modifications in current window order, then removals in previous order. Invalid windows leave the prior state intact. The registered key property takes precedence over C# id/_id/Id fallbacks.
Source Files
¶
- catalog.go
- codec.go
- collections.go
- decision_admission.go
- decision_agreement.go
- decision_decode.go
- decision_error.go
- decision_reader.go
- document.go
- indexes.go
- local_changes.go
- materialized.go
- model.go
- naming.go
- projection.go
- projection_release.go
- property_naming.go
- read_options.go
- reducer.go
- release.go
- release_many.go
- release_validation.go
- replay.go
- schema.go
- service.go
- session.go
- sink_defaults.go
- snapshot_decode.go
- snapshots.go
- subject.go
- subscription.go
- typed_value.go
- watch.go
- window_differ.go