package events import ( "knowfoolery/backend/shared/types" ) // DomainEvent represents a domain event that occurred in the system type DomainEvent interface { // EventID returns unique identifier for this event occurrence EventID() string // EventType returns the type of event EventType() string // EventVersion returns the version of event schema EventVersion() string // OccurredAt returns when the event occurred OccurredAt() types.Timestamp // AggregateID returns the ID of the aggregate that produced this event AggregateID() string // AggregateType returns the type of aggregate that produced this event AggregateType() string // EventData returns the event-specific data EventData() interface{} } // EventDispatcher handles publishing domain events type EventDispatcher interface { // Publish publishes a domain event Publish(event DomainEvent) error // PublishBatch publishes multiple domain events PublishBatch(events []DomainEvent) error } // EventHandler handles specific types of domain events type EventHandler interface { // Handle processes a domain event Handle(event DomainEvent) error // CanHandle returns true if this handler can process the event type CanHandle(eventType string) bool } // EventStore provides persistence for domain events type EventStore interface { // Store persists a domain event Store(event DomainEvent) error // StoreBatch persists multiple domain events atomically StoreBatch(events []DomainEvent) error // GetEvents retrieves events for an aggregate GetEvents(aggregateID string, aggregateType string, fromVersion int) ([]DomainEvent, error) // GetEventsSince retrieves events since a specific timestamp GetEventsSince(since types.Timestamp) ([]DomainEvent, error) } // Event type constants const ( // User events EventTypeUserRegistered = "user.registered" EventTypeUserUpdated = "user.updated" EventTypeUserDeleted = "user.deleted" // Game session events EventTypeGameSessionStarted = "game_session.started" EventTypeGameSessionCompleted = "game_session.completed" EventTypeGameSessionTimedOut = "game_session.timed_out" EventTypeGameSessionAbandoned = "game_session.abandoned" EventTypeSessionTimerWarning = "game_session.timer_warning" // Question events EventTypeQuestionPresented = "question.presented" EventTypeQuestionAnswered = "question.answered" EventTypeQuestionSkipped = "question.skipped" EventTypeHintRequested = "hint.requested" // Attempt events EventTypeAttemptMade = "attempt.made" EventTypeMaxAttemptsReached = "attempt.max_reached" // Scoring events EventTypeScoreUpdated = "score.updated" EventTypeLeaderboardUpdated = "leaderboard.updated" // Theme events EventTypeThemeCreated = "theme.created" EventTypeThemeUpdated = "theme.updated" EventTypeThemeDeleted = "theme.deleted" ) // Aggregate type constants const ( AggregateTypeUser = "user" AggregateTypeGameSession = "game_session" AggregateTypeQuestion = "question" AggregateTypeTheme = "theme" AggregateTypeLeaderboard = "leaderboard" ) // Base event data structures // UserEventData contains common user event data type UserEventData struct { UserID types.UserID `json:"user_id"` PlayerName string `json:"player_name"` CreatedAt types.Timestamp `json:"created_at,omitempty"` UpdatedAt types.Timestamp `json:"updated_at,omitempty"` } // GameSessionEventData contains common game session event data type GameSessionEventData struct { SessionID types.GameSessionID `json:"session_id"` UserID types.UserID `json:"user_id"` Status types.SessionStatus `json:"status"` Score int `json:"score"` QuestionsAsked int `json:"questions_asked"` QuestionsCorrect int `json:"questions_correct"` HintsUsed int `json:"hints_used"` StartTime types.Timestamp `json:"start_time"` EndTime *types.Timestamp `json:"end_time,omitempty"` Duration *types.Duration `json:"duration,omitempty"` } // QuestionEventData contains common question event data type QuestionEventData struct { QuestionID types.QuestionID `json:"question_id"` SessionID types.GameSessionID `json:"session_id"` ThemeID types.ThemeID `json:"theme_id"` ThemeName string `json:"theme_name"` QuestionText string `json:"question_text"` IsCorrect *bool `json:"is_correct,omitempty"` PointsAwarded *int `json:"points_awarded,omitempty"` AttemptNumber *int `json:"attempt_number,omitempty"` HintUsed *bool `json:"hint_used,omitempty"` PresentedAt types.Timestamp `json:"presented_at"` AnsweredAt *types.Timestamp `json:"answered_at,omitempty"` } // AttemptEventData contains attempt-specific event data type AttemptEventData struct { AttemptID types.AttemptID `json:"attempt_id"` SessionID types.GameSessionID `json:"session_id"` QuestionID types.QuestionID `json:"question_id"` AttemptNumber int `json:"attempt_number"` PlayerAnswer string `json:"player_answer"` CorrectAnswer string `json:"correct_answer"` IsCorrect bool `json:"is_correct"` PointsAwarded int `json:"points_awarded"` HintUsed bool `json:"hint_used"` TimeTaken types.Duration `json:"time_taken"` AttemptedAt types.Timestamp `json:"attempted_at"` } // ScoreEventData contains scoring event data type ScoreEventData struct { SessionID types.GameSessionID `json:"session_id"` UserID types.UserID `json:"user_id"` PreviousScore int `json:"previous_score"` NewScore int `json:"new_score"` PointsAwarded int `json:"points_awarded"` QuestionID types.QuestionID `json:"question_id"` UpdatedAt types.Timestamp `json:"updated_at"` } // LeaderboardEventData contains leaderboard event data type LeaderboardEventData struct { SessionID types.GameSessionID `json:"session_id"` UserID types.UserID `json:"user_id"` PlayerName string `json:"player_name"` FinalScore int `json:"final_score"` QuestionsAsked int `json:"questions_asked"` QuestionsCorrect int `json:"questions_correct"` SuccessRate float64 `json:"success_rate"` Duration types.Duration `json:"duration"` Rank *int `json:"rank,omitempty"` IsTopTen bool `json:"is_top_ten"` CompletedAt types.Timestamp `json:"completed_at"` } // TimerWarningEventData contains timer warning event data type TimerWarningEventData struct { SessionID types.GameSessionID `json:"session_id"` UserID types.UserID `json:"user_id"` TimeRemaining types.Duration `json:"time_remaining"` WarningType string `json:"warning_type"` // "5min", "1min", "10sec" Message string `json:"message"` WarningAt types.Timestamp `json:"warning_at"` } // ThemeEventData contains theme event data type ThemeEventData struct { ThemeID types.ThemeID `json:"theme_id"` Name string `json:"name"` Description string `json:"description,omitempty"` IsActive bool `json:"is_active"` CreatedAt types.Timestamp `json:"created_at,omitempty"` UpdatedAt types.Timestamp `json:"updated_at,omitempty"` } // Repository interfaces for event sourcing // EventSourcedRepository provides event sourcing capabilities for aggregates type EventSourcedRepository interface { // Save saves an aggregate and its events Save(aggregateID string, aggregateType string, expectedVersion int, events []DomainEvent) error // Load loads an aggregate from its events Load(aggregateID string, aggregateType string) ([]DomainEvent, int, error) // GetVersion gets the current version of an aggregate GetVersion(aggregateID string, aggregateType string) (int, error) } // Projection represents a read model projection type Projection interface { // ProjectionName returns the name of this projection ProjectionName() string // Handle handles a domain event and updates the projection Handle(event DomainEvent) error // CanHandle returns true if this projection handles the event type CanHandle(eventType string) bool // Reset resets the projection to initial state Reset() error } // ProjectionManager manages projections type ProjectionManager interface { // RegisterProjection registers a projection RegisterProjection(projection Projection) error // StartProjection starts processing events for a projection StartProjection(projectionName string) error // StopProjection stops processing events for a projection StopProjection(projectionName string) error // RebuildProjection rebuilds a projection from all events RebuildProjection(projectionName string) error }