Documentation
¶
Overview ¶
Package app holds Hallmark's usecases and the repository ports they depend on. Persistence and transport adapters implement these ports.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AuditEventFilter ¶
type AuditEventFilter struct {
ActorID string
ResourceType string
ResourceID string
Action string
From *time.Time
To *time.Time
Limit int
}
AuditEventFilter narrows a Query over the stream. Zero-value fields are not constrained; From/To bound the timestamp range inclusively.
func (AuditEventFilter) Matches ¶
func (f AuditEventFilter) Matches(e domain.AuditEvent) bool
Matches reports whether an event satisfies the filter. It's the in-memory twin of QueryOptions used by the live broker, so a subscriber and a query with the same filter select the same events. Pure — unit-tested.
func (AuditEventFilter) QueryOptions ¶
func (f AuditEventFilter) QueryOptions() []search.Option
QueryOptions translates a filter into the kit search options the repository applies. Newest-first ordering and a defaulted limit are part of the stream's read contract, so they live here rather than in the db layer.
type AuditEventRepository ¶
type AuditEventRepository interface {
repository.Creator[domain.AuditEvent]
repository.Getter[domain.AuditEvent]
repository.Lister[domain.AuditEvent]
Query(ctx context.Context, f AuditEventFilter) (resource.ListResponse[domain.AuditEvent], error)
}
AuditEventRepository persists and reads the append-only event stream. List backs the JSON:API REST surface (filters parsed from the request); Query backs the gRPC surface (a typed filter). Both read the same rows.
type AuditEventUsecase ¶
type AuditEventUsecase interface {
repository.Getter[domain.AuditEvent]
repository.Lister[domain.AuditEvent]
Append(ctx context.Context, event domain.AuditEvent) (domain.AuditEvent, error)
Query(ctx context.Context, f AuditEventFilter) (resource.ListResponse[domain.AuditEvent], error)
// Subscribe opens a filtered live feed of appended events. It errors when
// the service runs without a broker (live tail disabled).
Subscribe(f AuditEventFilter) (*Subscription, error)
}
AuditEventUsecase is the ingest + read surface for the stream. Append validates and records one event; Query/List read with an applied filter.
func NewAuditEventUsecase ¶
func NewAuditEventUsecase(events AuditEventRepository, opts ...AuditEventUsecaseOption) AuditEventUsecase
type AuditEventUsecaseOption ¶
type AuditEventUsecaseOption func(*auditEventUsecase)
AuditEventUsecaseOption configures the usecase.
func WithBroker ¶
func WithBroker(b *Broker) AuditEventUsecaseOption
WithBroker enables the live tail: Subscribe hands out feeds backed by this broker. The broker should also be registered in the Fanout so appended events reach it.
func WithFanout ¶
func WithFanout(f *Fanout) AuditEventUsecaseOption
WithFanout fans every appended event out to the registered forwarders.
type Broker ¶
type Broker struct {
// contains filtered or unexported fields
}
Broker is the in-process fan-out hub behind the live tail. It implements Forwarder, so registering it in the Fanout makes every appended event available to subscribers; Subscribe hands a caller a filtered channel.
func (*Broker) Forward ¶
Forward publishes an event to every matching subscriber, non-blocking: a full subscriber channel drops the event so one slow consumer never stalls ingest or the other subscribers.
func (*Broker) Subscribe ¶
func (b *Broker) Subscribe(filter AuditEventFilter) *Subscription
Subscribe registers a filtered live feed and returns its Subscription.
type Fanout ¶
type Fanout struct {
// contains filtered or unexported fields
}
Fanout delivers each event to every registered forwarder, best-effort: a forwarder failure is logged and never blocks ingest or the others. (Async queueing is a later refinement.)
type Forwarder ¶
type Forwarder interface {
Name() string
Forward(ctx context.Context, event domain.AuditEvent) error
}
Forwarder fans a stored audit event out to an external sink (Kafka, NATS, S3, a SIEM…). Built-in: LogForwarder. Others satisfy the same port.
type LogForwarder ¶
type LogForwarder struct {
// contains filtered or unexported fields
}
LogForwarder is the zero-config forwarder: it writes events to the log. A useful default and a template for real sinks.
func NewLogForwarder ¶
func NewLogForwarder() *LogForwarder
func (*LogForwarder) Forward ¶
func (f *LogForwarder) Forward(ctx context.Context, e domain.AuditEvent) error
func (LogForwarder) Name ¶
func (LogForwarder) Name() string
type PartitionStore ¶
type PartitionStore interface {
ListMonthlyPartitions(ctx context.Context) ([]string, error)
DropPartition(ctx context.Context, name string) error
}
PartitionStore lists and drops the monthly audit-event partitions.
type RetentionSweeper ¶
RetentionSweeper drops monthly partitions whose entire month precedes the cutoff — TTL enforced by dropping whole partitions, not row deletes (the table is append-only).
func NewRetentionSweeper ¶
func NewRetentionSweeper(partitions PartitionStore) RetentionSweeper
type Subscription ¶
type Subscription struct {
C <-chan domain.AuditEvent
// contains filtered or unexported fields
}
Subscription is a live feed of matching events. Close detaches it and releases the channel; callers must Close when done (e.g. on stream end).
func (*Subscription) Close ¶
func (s *Subscription) Close()
Close detaches the subscription. It's safe to call more than once.