Проект реализует API для озона на подобие хабра. Он позволяет работать с постами и комментариями, используя GraphQL. Система поддерживает создание постов, добавление комментариев, управление включением/выключением комментариев для постов, а также подписку на добавление новых комментариев.
Чтобы запустить проект, выполните следующие шаги:
- Docker и Docker Compose установлены на вашей системе
- Git для клонирования репозитория
-
Клонирование репозитория
git clone https://github.com/nabishec/ozon_habr_api.git cd ozon_habr_api
-
Запуск в Docker-контейнерах
docker-compose up
-
Использование API
После запуска откройте браузер и перейдите по адресу:
http://localhost:8080
По умолчанию проект использует PostgreSQL для хранения данных. Если вы хотите использовать in-memory хранилище для тестирования, измените в Dockerfile строку:
RUN go build -o main ./cmd/main.go
и добавьте флаг -s m
:
RUN go build -o main ./cmd/main.go -s m
Интерактивная GraphQL-playground консоль доступна по адресу:
Получение списка всех постов
query{
posts{
id
title
text
authorID
commentsEnabled
createDate
}
}
Получение конкретного поста с комментариями
query {
post(postID: 1) {
id
title
text
comments(first: 5) {
edges {
node {
id
text
authorID
createDate
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
Создание нового поста
mutation {
addPost(postInput: {
authorID: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
title: "Новый пост"
text: "Содержимое поста"
commentsEnabled: true
}) {
id
title
createDate
}
}
Создание нового комментария
mutation {
addComment(commentInput: {
authorID: "123e4567-e89b-12d3-a456-426614174000",
postID: 1,
parentID: 1, # ID существующего комментария
text: "Это ответ на комментарий 1"
}) {
id
text
parentID
}
}
Подписка на новые комментарии
subscription {
commentAdded(postID: 1) {
id
text
authorID
createDate
}
}
Для тестирования API можно использовать любые GraphQL клиенты, например Insomnia, Postman или GraphiQL.
Показать структуру
ozon_habr_api/
├── cmd/
│ ├── db_connection/
│ │ ├──
cache.go
(Подключение и настройка Redis для кэширования)
│ │ └──
database.go
(Подключение и настройка PostgreSQL)
│ ├── server/
│ │ └──
server.go
(Настройка и запуск GraphQL сервера)
│ └──
main.go
(Основная точка входа, настройка и запуск приложения)
├── graph/
│ ├── model/
│ │ └──
models_gen.go
(Автоматически сгенерированные GraphQL модели)
│ ├──
generated.go
(Сгенерированный код GraphQL (gqlgen))
│ ├──
resolver.go
(Основные резолверы GraphQL)
│ ├──
schema.graphqls
(Определение GraphQL схемы)
│ ├──
schema.resolvers.go
(Реализация резолверов GraphQL)
│ └──
subscription.go
(Реализация структур и методов для управления подписками)
├── internal/
│ ├── handlers/
│ │ ├── comment_mutation/
(Обработчики логики мутаций комментариев)
│ │ │ ├──
interface.go
(Интерфейс для мутаций комментариев)
│ │ │ └──
mutations.go
(Реализация мутаций комментариев)
│ │ ├── comment_query/
(Обработчики логики запросов комментариев)
│ │ │ ├──
interface.go
(Интерфейс для запросов комментариев)
│ │ │ └──
query.go
(Реализация запросов комментариев)
│ │ ├── post_mutation/
(Обработчики логики мутаций постов)
│ │ │ ├──
interface.go
(Интерфейс для мутаций постов)
│ │ │ └──
mutations.go
(Реализация мутаций постов)
│ │ └── post_query/
(Обработчики логики запросов постов)
│ │ ├──
interface.go
(Интерфейс для запросов постов)
│ │ └──
query.go
(Реализация запросов постов)
│ ├── pkg/
│ │ ├── cursor/
│ │ | └──
cursor.go
(Функции для работы с курсорами в пагинации)
│ │ └── errs/
│ │ └──
errors.go
(Хранит ошибки бизнес логики)
│ ├── model/
│ │ └──
model.go
(Внутренние модели данных)
│ └── storage/
│ ├── db/
(Реализация хранилища данных в памяти)
│ │ └──
resolvers.go
(Реализация методов для работы с базой данных PostgreSQL)
│ ├── in-memory/
(Реализация хранилища данных в памяти)
│ │ └──
resolvers.go
(Реализация методов для работы с даннми в памяти)
│ └──
interface.go
(Интерфейс для хранилища данных (PostgreSQL, in-memory))
├── migrations/
│ └──
001_create_tables.up.sql
(SQL скрипт для миграции базы данных (создание таблиц))
├── tools/
│ └──
tools.go
(Инструменты для генерации кода gqlgen)
├──
.env
(Файл с переменными окружения (настройки базы данных, Redis и т.д.))
├──
.gitignore
(Список игнорируемых файлов и директорий для Git)
├──
docker-compose.yml
(Конфигурация Docker Compose для запуска приложения и зависимостей)
├──
Dockerfile
(Инструкции для сборки Docker образа)
├──
go.mod
(Файл зависимостей Go)
├──
go.sum
(Файл с контрольными суммами зависимостей Go)
├──
gqlgen.yml
(Конфигурационный файл для gqlgen)
├──
LICENSE
(Лицензия проекта)
└──
README.md
(Файл с описанием проекта)
Проект использует эффективную иерархическую структуру данных для организации комментариев:
Показать структуру
- ID: Уникальный идентификатор поста (BIGSERIAL)
- AuthorID: UUID автора поста
- Title: Заголовок поста
- Text: Содержимое поста
- CommentsEnabled: Флаг, указывающий, разрешены ли комментарии к посту
- CreateDate: Дата и время создания поста
- ID: Уникальный идентификатор комментария (BIGSERIAL)
- AuthorID: UUID автора комментария
- PostID: Идентификатор поста, к которому относится комментарий
- ParentID: Идентификатор родительского комментария (для вложенных комментариев)
- Path: Материализованный путь в формате LTREE для эффективного поиска и построения иерархии
- Text: Текст комментария
- CreateDate: Дата и время создания комментария
Показать
- Материализованные пути: Использование PostgreSQL LTREE для хранения иерархии комментариев обеспечивает высокую производительность при запросах вложенных структур
- Оптимизированные индексы: Созданы индексы по полям path, create_date и post_id для ускорения запросов
- Эффективная организация комментариев: Иерархическая структура комментариев с возможностью глубокой вложенности до любого уровня
- Двухуровневое кэширование: Использование Redis для кэширования часто запрашиваемых комментариев и веток обсуждений на 30 минут
- Инвалидация кэша: Автоматическое обновление кэша при создании новых комментариев для обеспечения актуальности данных
- Пагинация на основе курсоров: Эффективная пагинация результатов с сохранением контекста для больших наборов данных
- GraphQL оптимизация: Возможность запрашивать только необходимые поля и эффективная организация связанных данных
- Параллельная обработка запросов: Использование горутин для обработки тяжелых задач без блокировки основного потока
- Паттерн Publish-Subscribe: Реализация Pub/Sub для уведомлений о новых комментариях в реальном времени, где компоненты взаимодействуют через центральный механизм каналов
- Потокобезопасное управление подписками: Использование мьютексов для безопасного доступа к списку подписчиков в конкурентной среде
- Автоматическая очистка ресурсов: Корректное закрытие каналов и удаление неактивных подписчиков для предотвращения утечек памяти
- Асинхронность: Используются неблокирующие Go-каналы для передачи данных
- Устойчивость к ошибкам: Защита от паник при отправке данных в закрытые каналы с использованием отложенных функций
- Масштабируемость: Возможность подписки на события по конкретному идентификатору поста, что обеспечивает точечную доставку уведомлений
Примечание о паттернах: В отличие от классического паттерна Observer, где наблюдатели напрямую регистрируются у наблюдаемого объекта, в данном проекте реализован паттерн Publish-Subscribe, который вводит промежуточный слой (брокер сообщений) между издателями и подписчиками. Это обеспечивает более высокую степень декомпозиции: издатели не знают о конкретных подписчиках, а подписчики не знают об издателях. Подписки группируются по идентификатору поста, что позволяет реализовать фильтрацию событий на уровне брокера.
Проект полностью готов к запуску в контейнерах:
- Dockerfile: Оптимизированный многостадийный образ для минимального размера и максимальной производительности
- Docker Compose: Полная конфигурация для запуска всей инфраструктуры (API, PostgreSQL, Redis) одной командой
- Переменные окружения: Настройка всех компонентов через переменные окружения для гибкого развертывания
Это обеспечивает:
- Идентичность сред разработки и производства
- Простоту горизонтального масштабирования
- Минимальное время развертывания
- Легкую интеграцию в любую облачную платформу (AWS, GCP, Azure)
Данный проект стремится соответствовать современным стандартам разработки программного обеспечения.
Показать
- Чистая архитектура: Строгое разделение между слоями данных, бизнес-логики и представления обеспечивает масштабируемость и простоту поддержки
- Dependency Injection: Использование внедрения зависимостей делает код модульным и легко тестируемым
- Repository Pattern: Абстрактный интерфейс хранилища позволяет легко заменять источники данных (PostgreSQL, in-memory)
- Publish-Subscribe Pattern: Применение паттерна Pub/Sub для асинхронной передачи данных между компонентами через централизованного брокера, обеспечивая полную декомпозицию отправителей и получателей
- Готовность к высоким нагрузкам: Архитектура и выбранные технологии обеспечивают хорошую производительность при масштабировании
- Поддержка микросервисной архитектуры: Сервис легко интегрируется в микросервисную экосистему
- Расширяемость: Модульная структура позволяет легко добавлять новые функции и интегрироваться с другими системами
- Code Generation: Автоматическая генерация кода с помощью gqlgen минимизирует ручное написание повторяющегося кода
- Database Migrations: Структурированные миграции базы данных обеспечивают надежное обновление схемы
- Environment Configuration: Гибкая настройка через переменные окружения для различных сред развертывания
- Детальное логирование: Структурированные логи для мониторинга и диагностики системы
- Go 1.24
- GraphQL (gqlgen)
- PostgreSQL 17
- Redis 9
- Docker & Docker Compose
- Gorilla WebSockets
Данный проект распространяется под лицензией Apache License 2.0. Это свободная лицензия с открытым исходным кодом, которая позволяет использовать, модифицировать и распространять код как в коммерческих, так и в некоммерческих целях.
Полный текст лицензии доступен в файле LICENSE.
Copyright 2023 Ozon Habr API
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.