package ports import ( "context" "fmt" "time" "galaxy/user/internal/domain/account" "galaxy/user/internal/domain/common" ) // CreateAccountInput stores the atomic account-create state that must commit // together. type CreateAccountInput struct { // Account stores the durable user-account state. Account account.UserAccount // Reservation stores the canonical race-name reservation linked to Account. Reservation account.RaceNameReservation } // Validate reports whether CreateAccountInput is structurally complete. func (input CreateAccountInput) Validate() error { if err := input.Account.Validate(); err != nil { return fmt.Errorf("create account input account: %w", err) } if err := input.Reservation.Validate(); err != nil { return fmt.Errorf("create account input reservation: %w", err) } if input.Account.UserID != input.Reservation.UserID { return fmt.Errorf("create account input reservation user id must match account user id") } if input.Account.RaceName != input.Reservation.RaceName { return fmt.Errorf("create account input reservation race name must match account race name") } return nil } // RenameRaceNameInput stores the atomic state required to replace one stored // race name and its canonical reservation. type RenameRaceNameInput struct { // UserID identifies the account that must be updated. UserID common.UserID // CurrentCanonicalKey stores the currently owned canonical reservation key. CurrentCanonicalKey account.RaceNameCanonicalKey // NewRaceName stores the replacement exact stored race name. NewRaceName common.RaceName // NewReservation stores the replacement canonical reservation. NewReservation account.RaceNameReservation // UpdatedAt stores the account mutation timestamp. UpdatedAt time.Time } // Validate reports whether RenameRaceNameInput is structurally complete. func (input RenameRaceNameInput) Validate() error { if err := input.UserID.Validate(); err != nil { return fmt.Errorf("rename race name input user id: %w", err) } if err := input.CurrentCanonicalKey.Validate(); err != nil { return fmt.Errorf("rename race name input current canonical key: %w", err) } if err := input.NewRaceName.Validate(); err != nil { return fmt.Errorf("rename race name input race name: %w", err) } if err := input.NewReservation.Validate(); err != nil { return fmt.Errorf("rename race name input reservation: %w", err) } if err := common.ValidateTimestamp("rename race name input updated at", input.UpdatedAt); err != nil { return err } if input.NewReservation.UserID != input.UserID { return fmt.Errorf("rename race name input reservation user id must match user id") } if input.NewReservation.RaceName != input.NewRaceName { return fmt.Errorf("rename race name input reservation race name must match new race name") } return nil } // UserAccountStore persists source-of-truth user-account records and their // exact lookup mappings. type UserAccountStore interface { // Create stores one new account record. Implementations must wrap // ErrConflict when the user id, e-mail, or exact race-name lookup already // exists. Create(ctx context.Context, input CreateAccountInput) error // GetByUserID returns the stored account identified by userID. GetByUserID(ctx context.Context, userID common.UserID) (account.UserAccount, error) // GetByEmail returns the stored account identified by the normalized e-mail // address. GetByEmail(ctx context.Context, email common.Email) (account.UserAccount, error) // GetByRaceName returns the stored account identified by the exact stored // race name. GetByRaceName(ctx context.Context, raceName common.RaceName) (account.UserAccount, error) // ExistsByUserID reports whether userID currently identifies a stored // account. ExistsByUserID(ctx context.Context, userID common.UserID) (bool, error) // RenameRaceName replaces the stored race name of userID and swaps the // exact race-name lookup atomically. Implementations must wrap ErrConflict // when newRaceName is already owned by another account. RenameRaceName(ctx context.Context, input RenameRaceNameInput) error // Update replaces the stored account state for record.UserID. Update(ctx context.Context, record account.UserAccount) error } // RaceNameReservationStore persists source-of-truth race-name reservations. type RaceNameReservationStore interface { // Create stores one new race-name reservation keyed by its canonical // uniqueness key. Implementations must wrap ErrConflict when the canonical // key is already reserved. Create(ctx context.Context, record account.RaceNameReservation) error // GetByCanonicalKey returns the stored reservation identified by key. GetByCanonicalKey(ctx context.Context, key account.RaceNameCanonicalKey) (account.RaceNameReservation, error) // DeleteByCanonicalKey removes the reservation identified by key. DeleteByCanonicalKey(ctx context.Context, key account.RaceNameCanonicalKey) error }