Skip to content

AlexRodving/user-management-api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🏗️ User Management API - Модульный монолит на Go

Production-ready REST API с Clean Architecture, PostgreSQL и полной документацией

📋 Описание

Полноценное приложение для управления пользователями, демонстрирующее best practices разработки на Go:

  • ✅ Clean Architecture (3 слоя)
  • ✅ PostgreSQL база данных
  • ✅ REST API с CRUD операциями
  • ✅ Dependency Injection
  • ✅ Интерфейсы для тестируемости
  • ✅ Миграции БД

🏛️ Архитектура

┌─────────────────────────────────────┐
│  HTTP Handler (presentation)        │  ← Принимает HTTP запросы
│  /internal/handler/                 │
└────────────┬────────────────────────┘
             ↓
┌─────────────────────────────────────┐
│  Service (business logic)           │  ← Валидация и бизнес-логика
│  /internal/service/                 │
└────────────┬────────────────────────┘
             ↓
┌─────────────────────────────────────┐
│  Repository (data access)           │  ← SQL запросы
│  /internal/repository/              │
└────────────┬────────────────────────┘
             ↓
┌─────────────────────────────────────┐
│  PostgreSQL Database                │  ← Хранение данных
└─────────────────────────────────────┘

Принципы:

  • Разделение ответственности - каждый слой делает своё
  • Dependency Injection - зависимости внедряются через конструкторы
  • Интерфейсы - слои общаются через контракты
  • Тестируемость - легко заменить любой слой моками

📂 Структура проекта

user_management_api/
├── main.go                           # Точка входа приложения
├── go.mod                            # Зависимости
├── go.sum                            # Хеши зависимостей
├── env.example                       # Пример конфигурации
│
├── migrations/                       # SQL миграции
│   └── 001_create_users.sql         # Создание таблицы users
│
└── internal/                         # Внутренние пакеты (не экспортируются)
    │
    ├── config/                       # Конфигурация
    │   └── config.go                # Загрузка настроек из ENV
    │
    ├── domain/                       # Модели данных (Domain Layer)
    │   └── user.go                  # Структуры User, CreateUserRequest, etc.
    │
    ├── repository/                   # Слой работы с БД (Data Layer)
    │   └── user_repository.go       # SQL запросы (CRUD)
    │
    ├── service/                      # Бизнес-логика (Business Layer)
    │   └── user_service.go          # Валидация, обработка данных
    │
    └── handler/                      # HTTP обработчики (Presentation Layer)
        └── user_handler.go          # REST API endpoints

🚀 Быстрый старт

Требования

  • Go 1.21+
  • Docker (для PostgreSQL)
  • curl (для тестирования)

1. Клонируйте репозиторий

git clone <your-repo-url>
cd 07_modular_app

2. Запустите PostgreSQL

# Запуск через Docker
docker run --name postgres \
  -e POSTGRES_PASSWORD=postgres \
  -p 5432:5432 \
  -d postgres

# Создание базы данных
docker exec -it postgres psql -U postgres -c "CREATE DATABASE user_management;"

# Применение миграций
docker exec -i postgres psql -U postgres -d user_management < migrations/001_create_users.sql

3. Настройте конфигурацию

# Скопируйте пример конфигурации
cp env.example .env

# Отредактируйте .env если нужно (опционально)

4. Установите зависимости

go mod tidy

5. Запустите приложение

go run main.go

Сервер запустится на http://localhost:8080


📡 API Endpoints

Создать пользователя

curl -X POST http://localhost:8080/users \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alice@example.com",
    "name": "Alice",
    "password": "secret123"
  }'

Ответ:

{
  "id": 1,
  "email": "alice@example.com",
  "name": "Alice",
  "created_at": "2025-10-15T10:00:00Z",
  "updated_at": "2025-10-15T10:00:00Z"
}

Получить всех пользователей

curl http://localhost:8080/users

Ответ:

[
  {
    "id": 1,
    "email": "alice@example.com",
    "name": "Alice",
    "created_at": "2025-10-15T10:00:00Z",
    "updated_at": "2025-10-15T10:00:00Z"
  }
]

Получить пользователя по ID

curl http://localhost:8080/users/1

Обновить пользователя

curl -X PUT http://localhost:8080/users/1 \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alice.updated@example.com",
    "name": "Alice Updated"
  }'

Удалить пользователя

curl -X DELETE http://localhost:8080/users/1

Ответ:

{
  "message": "User deleted"
}

🔧 Конфигурация

Приложение настраивается через environment variables:

Переменная Описание По умолчанию
DB_HOST Хост PostgreSQL localhost
DB_PORT Порт PostgreSQL 5432
DB_USER Пользователь БД postgres
DB_PASSWORD Пароль БД postgres
DB_NAME Имя базы данных user_management
SERVER_PORT Порт HTTP сервера 8080

Пример использования:

# Запуск на другом порту
SERVER_PORT=3000 go run main.go

# С другими настройками БД
DB_HOST=db.example.com DB_PORT=5433 go run main.go

🗄️ Схема базы данных

Таблица users

Колонка Тип Описание
id SERIAL PRIMARY KEY Уникальный идентификатор
email VARCHAR(255) UNIQUE Email (уникальный)
name VARCHAR(255) Имя пользователя
password VARCHAR(255) Пароль (в production хешируется)
created_at TIMESTAMP Время создания
updated_at TIMESTAMP Время последнего обновления

Индексы:

  • PRIMARY KEY на id
  • UNIQUE INDEX на email
  • INDEX на email для быстрого поиска

🧪 Тестирование

Полный цикл тестирования:

# 1. Создать пользователя
curl -X POST http://localhost:8080/users \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","name":"Test User","password":"password123"}'

# 2. Получить всех пользователей
curl http://localhost:8080/users

# 3. Получить конкретного пользователя
curl http://localhost:8080/users/1

# 4. Обновить пользователя
curl -X PUT http://localhost:8080/users/1 \
  -H "Content-Type: application/json" \
  -d '{"email":"updated@example.com","name":"Updated Name"}'

# 5. Удалить пользователя
curl -X DELETE http://localhost:8080/users/1

# 6. Проверить удаление
curl http://localhost:8080/users

Тест ошибок:

# Создание с невалидными данными
curl -X POST http://localhost:8080/users \
  -H "Content-Type: application/json" \
  -d '{"email":"","name":"","password":"12"}' # Короткий пароль

# Получение несуществующего пользователя
curl http://localhost:8080/users/9999

# Невалидный ID
curl http://localhost:8080/users/abc

💡 Технологии и паттерны

Технологии:

  • Go 1.21 - язык программирования
  • PostgreSQL - реляционная БД
  • net/http - стандартная библиотека для HTTP
  • database/sql - стандартная библиотека для работы с БД
  • lib/pq - PostgreSQL драйвер

Паттерны:

  • Clean Architecture - разделение на слои
  • Repository Pattern - абстракция работы с данными
  • Dependency Injection - внедрение зависимостей
  • Interface Segregation - маленькие интерфейсы

📚 Структура кода

Domain Layer (Модели данных)

// internal/domain/user.go
type User struct {
    ID        int       `json:"id"`
    Email     string    `json:"email"`
    Name      string    `json:"name"`
    Password  string    `json:"-"` // Не показываем в JSON!
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}

Repository Layer (Работа с БД)

// internal/repository/user_repository.go
type UserRepository interface {
    Create(req *domain.CreateUserRequest) (*domain.User, error)
    GetByID(id int) (*domain.User, error)
    GetAll() ([]domain.User, error)
    Update(id int, req *domain.UpdateUserRequest) (*domain.User, error)
    Delete(id int) error
}

Service Layer (Бизнес-логика)

// internal/service/user_service.go
type UserService interface {
    CreateUser(req *domain.CreateUserRequest) (*domain.User, error)
    GetUser(id int) (*domain.User, error)
    GetAllUsers() ([]domain.User, error)
    UpdateUser(id int, req *domain.UpdateUserRequest) (*domain.User, error)
    DeleteUser(id int) error
}

Handler Layer (HTTP)

// internal/handler/user_handler.go
type UserHandler struct {
    service service.UserService
}

func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request)
func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request, id int)
// ... другие методы

🎯 Ключевые особенности

1. Dependency Injection

// main.go
userRepo := repository.NewUserRepository(db)
userService := service.NewUserService(userRepo)  // Внедряем repo
userHandler := handler.NewUserHandler(userService) // Внедряем service

2. Интерфейсы для тестируемости

// Легко заменить на mock для тестов
type UserRepository interface {
    Create(...) error
    GetByID(...) (*User, error)
}

3. Валидация в Service слое

func (s *userService) CreateUser(req *domain.CreateUserRequest) (*domain.User, error) {
    if req.Email == "" {
        return nil, errors.New("email обязателен")
    }
    if len(req.Password) < 6 {
        return nil, errors.New("пароль должен быть минимум 6 символов")
    }
    // ...
}

4. SQL с параметрами (защита от SQL injection)

query := `SELECT * FROM users WHERE id = $1` // $1 - безопасный параметр
db.QueryRow(query, id).Scan(...)

🐛 Troubleshooting

Порт уже занят

# Найти процесс на порту 8080
lsof -i :8080

# Убить процесс
kill -9 <PID>

# Или использовать другой порт
SERVER_PORT=3000 go run main.go

Ошибка подключения к PostgreSQL

# Проверить, запущен ли контейнер
docker ps | grep postgres

# Если не запущен - запустить
docker start postgres

# Проверить логи
docker logs postgres

БД не существует

# Создать БД
docker exec -it postgres psql -U postgres -c "CREATE DATABASE user_management;"

# Применить миграции
docker exec -i postgres psql -U postgres -d user_management < migrations/001_create_users.sql

📖 Обучающие материалы

Этот проект содержит подробные комментарии в каждом файле, объясняющие:

  • Как работает каждая строка кода
  • Почему используется именно этот подход
  • Какие альтернативы существуют

Рекомендуемый порядок изучения:

  1. internal/domain/user.go - начните с моделей данных
  2. internal/repository/user_repository.go - SQL запросы и работа с БД
  3. internal/service/user_service.go - бизнес-логика и валидация
  4. internal/handler/user_handler.go - HTTP обработчики
  5. main.go - сборка всего вместе (Dependency Injection)

🎓 Что можно узнать из этого проекта

Go концепции:

  • ✅ Структуры и методы
  • ✅ Интерфейсы и их реализация
  • ✅ Указатели (value vs pointer receiver)
  • ✅ Error handling
  • ✅ Пакеты и модули

Архитектурные паттерны:

  • ✅ Clean Architecture
  • ✅ Repository Pattern
  • ✅ Dependency Injection
  • ✅ Interface Segregation

Работа с БД:

  • ✅ Подключение к PostgreSQL
  • ✅ SQL миграции
  • ✅ CRUD операции
  • ✅ Параметризованные запросы
  • ✅ QueryRow vs Query vs Exec

HTTP:

  • ✅ REST API design
  • ✅ HTTP методы (GET, POST, PUT, DELETE)
  • ✅ Статус коды (200, 201, 400, 404, 500)
  • ✅ JSON encoding/decoding
  • ✅ Routing

🔐 Security Note

⚠️ Для production:

  1. Хешируйте пароли с помощью bcrypt:
import "golang.org/x/crypto/bcrypt"

hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
  1. Используйте HTTPS вместо HTTP

  2. Добавьте аутентификацию (JWT tokens)

  3. Добавьте rate limiting для защиты от атак

  4. Валидируйте email с помощью regex или библиотек


🚀 Дальнейшее развитие

Что можно добавить:

  1. Аутентификация

    • JWT tokens
    • Login/Logout endpoints
    • Middleware для проверки токенов
  2. Логирование

    • Structured logging (zap, zerolog)
    • Request logging middleware
    • Error logging
  3. Тестирование

    • Unit тесты
    • Integration тесты
    • Моки для repository
  4. Middleware

    • CORS
    • Rate limiting
    • Recovery from panic
  5. Роутер

    • chi, gorilla/mux, или gin
    • Более удобная работа с параметрами
  6. Валидация

    • validator библиотека
    • Custom validation rules
  7. Миграции

    • golang-migrate
    • Автоматическое применение при старте
  8. Docker

    • Dockerfile для приложения
    • docker-compose.yml для всего стека

📝 HTTP Status Codes

Код Название Когда используется
200 OK Успешный GET/PUT
201 Created Успешный POST
400 Bad Request Невалидные данные
404 Not Found Ресурс не найден
405 Method Not Allowed Неподдерживаемый HTTP метод
500 Internal Server Error Ошибка сервера/БД

💡 Best Practices

1. Всегда проверяйте ошибки

if err != nil {
    return nil, err
}

2. Используйте defer для очистки ресурсов

defer db.Close()
defer rows.Close()
defer r.Body.Close()

3. Параметризованные SQL запросы

// ✅ Правильно (безопасно)
query := `SELECT * FROM users WHERE id = $1`
db.QueryRow(query, id)

// ❌ Неправильно (SQL injection!)
query := fmt.Sprintf("SELECT * FROM users WHERE id = %d", id)

4. JSON теги для сериализации

type User struct {
    ID       int    `json:"id"`
    Password string `json:"-"`  // Не показывать в JSON!
}

5. Интерфейсы в пакете потребителя

// Определяем интерфейс там, где используем (service),
// а не там, где реализуем (repository)

📚 Ссылки и ресурсы


👨‍💻 Автор

Создано как учебный проект для изучения Go и Clean Architecture.


📄 Лицензия

MIT License - используйте свободно для обучения и разработки.


🎯 Готовность к собеседованиям

Этот проект демонстрирует понимание:

  • ✅ Go синтаксиса и идиом
  • ✅ Clean Architecture
  • ✅ REST API design
  • ✅ Работы с PostgreSQL
  • ✅ Error handling
  • ✅ Dependency Injection
  • ✅ Интерфейсов и их применения

About

No description, website, or topics provided.

Resources

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages