Skip to main content

Overview

The User Service provides user account management functionality, including user creation, retrieval, and deletion. It implements a repository pattern to abstract database operations. Package: internal/core/user/service.go

UserServiceApi Interface

Defines the public contract for user service implementations. Source: internal/core/user/interfaces.go
type UserServiceApi interface {
    GetUser(string) (*User, error)
    AddUser(*User) (*User, error)
    //DeleteUser(uuid.UUID) error
}

Interface Methods

GetUser
method
Retrieves a user by username
AddUser
method
Creates a new user account
The DeleteUser method is currently commented out in the public interface but is available in the service implementation.

UserService Implementation

Structure

type UserService struct {
    repo repository
}
The service wraps a repository implementation that handles data persistence.

Constructor

func NewUserService(repo repository) *UserService
repo
repository
required
A repository implementing the repository interface for user data persistence
Returns: Initialized UserService instance Example:
import "github.com/Lafetz/showdown-trivia-game/internal/core/user"

// Assuming you have a repository implementation
userRepo := NewUserRepository(db)
userService := user.NewUserService(userRepo)
Source: internal/core/user/service.go:20

Methods

GetUser

Retrieves a user by their username.
func (srv *UserService) GetUser(username string) (*User, error)
username
string
required
The username to search for
Returns:
  • *User: Pointer to the User object if found
  • error: Returns ErrUserNotFound if user doesn’t exist, or other repository errors
Example:
user, err := userService.GetUser("john_doe")
if err != nil {
    if errors.Is(err, user.ErrUserNotFound) {
        fmt.Println("User not found")
        return
    }
    return err
}

fmt.Printf("Found user: %s (ID: %s)\n", user.Username, user.Id)
Source: internal/core/user/service.go:26

AddUser

Creates a new user account.
func (srv *UserService) AddUser(user *User) (*User, error)
user
*User
required
Pointer to a User object with username, email, and hashed password
Returns:
  • *User: The created user with generated ID and timestamp
  • error: Returns ErrUsernameUnique or ErrEmailUnique if username/email already exists
Example:
import (
    "golang.org/x/crypto/bcrypt"
    "github.com/Lafetz/showdown-trivia-game/internal/core/user"
)

// Hash the password
hashedPassword, err := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
if err != nil {
    return err
}

// Create new user
newUser := user.NewUser("john_doe", "[email protected]", hashedPassword)

// Add to database
createdUser, err := userService.AddUser(newUser)
if err != nil {
    if errors.Is(err, user.ErrUsernameUnique) {
        fmt.Println("Username already taken")
        return
    }
    if errors.Is(err, user.ErrEmailUnique) {
        fmt.Println("Email already registered")
        return
    }
    return err
}

fmt.Printf("User created with ID: %s\n", createdUser.Id)
Source: internal/core/user/service.go:29

DeleteUser

Deletes a user by their unique ID.
func (srv *UserService) DeleteUser(id uuid.UUID) error
id
uuid.UUID
required
The unique identifier of the user to delete
Returns:
  • error: Returns ErrDelete if deletion fails, or repository-specific errors
Example:
import "github.com/google/uuid"

userID := uuid.MustParse("550e8400-e29b-41d4-a716-446655440000")
err := userService.DeleteUser(userID)
if err != nil {
    if errors.Is(err, user.ErrDelete) {
        fmt.Println("Failed to delete user")
        return
    }
    return err
}

fmt.Println("User deleted successfully")
Source: internal/core/user/service.go:33
This method is not exposed in the UserServiceApi interface. It’s available in the concrete implementation but not part of the public API contract.

Data Structures

User Entity

Represents a user account. Source: internal/core/user/domain.go
type User struct {
    Id        uuid.UUID
    Username  string
    Email     string
    Password  []byte
    CreatedAt time.Time
}
Id
uuid.UUID
Unique identifier for the user (auto-generated)
Username
string
Unique username for authentication
Email
string
User’s email address (must be unique)
Password
[]byte
Hashed password (should never store plain text passwords)
CreatedAt
time.Time
Timestamp of account creation

User Constructor

func NewUser(username string, email string, password []byte) *User
username
string
required
Desired username
email
string
required
User’s email address
password
[]byte
required
Hashed password bytes (use bcrypt or similar)
Returns: Pointer to a new User with auto-generated ID and CreatedAt timestamp Example:
hashedPwd, _ := bcrypt.GenerateFromPassword([]byte("secret"), bcrypt.DefaultCost)
user := user.NewUser("alice", "[email protected]", hashedPwd)
Source: internal/core/user/domain.go:17

Repository Interface

Defines the contract for user data persistence. Source: internal/core/user/interfaces.go
type repository interface {
    GetUser(username string) (*User, error)
    AddUser(*User) (*User, error)
    DeleteUser(uuid.UUID) error
}
The repository interface must be implemented by the persistence layer (e.g., PostgreSQL, MongoDB, in-memory store).

Error Types

The user package defines several error types for common failure scenarios. Source: internal/core/user/service.go:9-14
var (
    ErrUserNotFound   = errors.New("user not found")
    ErrUsernameUnique = errors.New("an account with this username exists")
    ErrDelete         = errors.New("failed to Delete user")
    ErrEmailUnique    = errors.New("an account with this email exists")
)

Error Descriptions

ErrUserNotFound
error
Returned when GetUser cannot find a user with the specified username
ErrUsernameUnique
error
Returned when AddUser attempts to create a user with an existing username
ErrEmailUnique
error
Returned when AddUser attempts to create a user with an existing email
ErrDelete
error
Returned when DeleteUser fails to remove a user from the database

Error Handling Pattern

import "errors"

user, err := userService.GetUser("john_doe")
if err != nil {
    switch {
    case errors.Is(err, user.ErrUserNotFound):
        // Handle user not found (404)
        return fmt.Errorf("no user found with that username")
    default:
        // Handle other errors (500)
        return fmt.Errorf("database error: %w", err)
    }
}

Security Considerations

Always hash passwords before storing them. Never store plain text passwords in the database.
Recommended Password Hashing:
import "golang.org/x/crypto/bcrypt"

// When creating a user
hashedPassword, err := bcrypt.GenerateFromPassword(
    []byte(plainPassword),
    bcrypt.DefaultCost,
)
if err != nil {
    return err
}

user := user.NewUser(username, email, hashedPassword)
Password Verification:
// When authenticating
user, err := userService.GetUser(username)
if err != nil {
    return err
}

err = bcrypt.CompareHashAndPassword(user.Password, []byte(providedPassword))
if err != nil {
    return errors.New("invalid password")
}

Usage in Application

Typical user registration flow:
func RegisterUser(service user.UserServiceApi, username, email, password string) error {
    // Hash password
    hashedPwd, err := bcrypt.GenerateFromPassword(
        []byte(password),
        bcrypt.DefaultCost,
    )
    if err != nil {
        return fmt.Errorf("failed to hash password: %w", err)
    }
    
    // Create user entity
    newUser := user.NewUser(username, email, hashedPwd)
    
    // Add to database
    _, err = service.AddUser(newUser)
    if err != nil {
        if errors.Is(err, user.ErrUsernameUnique) {
            return fmt.Errorf("username '%s' is already taken", username)
        }
        if errors.Is(err, user.ErrEmailUnique) {
            return fmt.Errorf("email '%s' is already registered", email)
        }
        return fmt.Errorf("registration failed: %w", err)
    }
    
    return nil
}
Typical user authentication flow:
func AuthenticateUser(service user.UserServiceApi, username, password string) (*user.User, error) {
    // Retrieve user
    u, err := service.GetUser(username)
    if err != nil {
        if errors.Is(err, user.ErrUserNotFound) {
            return nil, errors.New("invalid credentials")
        }
        return nil, err
    }
    
    // Verify password
    err = bcrypt.CompareHashAndPassword(u.Password, []byte(password))
    if err != nil {
        return nil, errors.New("invalid credentials")
    }
    
    return u, nil
}

Build docs developers (and LLMs) love