Skip to content

Сервис динамического сегментирования пользователей

Notifications You must be signed in to change notification settings

Icerzack/Dynamic-user-segmentation-service

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

codecov

Dynamic user segmentation service

Сервис динамического сегментирования пользователей

Содержание
  1. Основные сведения
  2. Установка и запуск
  3. Тесты и CI

Основные сведения

Описание

Данная программа содержит реализацию сервиса динамического сегментирования пользователей.

Ниже приведены основные моменты данной реализации:

  1. Реализовано на Golang;

  2. Есть возможность экспорта истории добавления\удаления сегментов у пользователей;

  3. Есть поддержка назначения TTL определенному сегменту у пользователя;

  4. Используются следующие библиотеки:

    2.1. https://github.com/georgysavva/scany -- Работа с БД;

    2.2. https://github.com/jackc/pgconn -- Работа с БД;

    2.3. https://github.com/jackc/pgx/ -- Работа с БД;

    2.4. https://pkg.go.dev/go.uber.org/mock -- Создание stub'ов и mock'ов для тестов.

  5. docker-compose для поднятия и развертывания среды;

  6. CI на GitHub Actions и тесты для проверки главного функционала.

Структура проекта выглядит следующим образом:

├── .github
|   └── workflows
|       └── tests.yml                   - Файл конфигурации CI
├── build
|   └── Dockerfile                      - Dockerfile для создания образа приложения
├── docs
|   └── history.csv                     - CSV файл, содержащий в себе историю по операциям
├── cmd
|   └── main.go                         - Запускающий файл
├── internal
|   ├── app
|   |   ├── config.go                   - Конфигурация сервера
|   |   ├── server.go                   - Методы для запуска сервера и обработки запросов
|   |   └── server_test.go              - Тесты для проверки работоспособности
|   └── pkg
|       ├── db
|       |   ├── mocks
|       |   |   └── mock.go             - Авто-генерация моков для интерфейса DBops
|       |   ├── sql
|       |   |   └── init.sql            - Файл для создания таблиц в базе данных
|       |   ├── client.go               - Создание экземпляра PostgreSQL
|       |   └── database.go             - Методы по работе с базой данных
|       ├── model
|       |   ├── segment.go              - Структуры для работы с JSON
|       |   └── user_segment.go         - Структуры для работы с JSON
|       ├── repository
|       |   ├── postgresql
|       |   |   ├── segments.go         - Реализация паттерна репозиторий для PostgreSQL
|       |   |   └── user_segment.go     - Реализация паттерна репозиторий для PostgreSQL
|       |   ├── repository.go           - Паттерн репозиторий
|       |   └── structs.go              - Структуры для базы данных
|       └── service
|           ├── db
|           |   ├── mocks
|           |   |   └── mock.go         - Авто-генерация моков для интерфейса Service
|           |   ├── postgres.go         - Реализация бизнес-логики для PostgreSQL
|           |   └── service.go          - Интерфес для работы бизнес-логики
|           └── history.go
|               ├── csv.go              - Реализация модуля ведения истории для формата CSV
|               └── service.go          - Интерфес для работы c модулем истории
├── .gitignore
├── docker-compose.yml                  - Для поднятия в Docker'е
├── go.mod
├── Makefile
└── README.md

База данных

Подразумевается, что ID пользователя нам известен заранее и подается из вне как запрос на этот сервис.

image

Таблица segments хранит в себе в качестве ключа название сегмента и его описание. Описание может быть пустым.

Таблица user_segments хранит в себе в качестве ключа id записи в этой таблице, user_id хранит в себе уникальный идентификатор пользователя (который мы знаем заранее), seg_title хранит в себе название сегмента, в котором состоит пользователь. seg_title не является FK! Данная зависимость реализована напрямую из кода, дабы избежать ссылок в самих таблицах.

Таким образом, чтение, добавление и удаление сегментов у пользователя реализовано по следующему алгоритму:

  1. Если данный ID пользователя имеется в таблице user_segments, то будут выведены все сегменты из данной таблицы, где поле user_id равняется нашему запрашиваемому;
  2. Если данный ID пользователя в таблице НЕ имеется, то соответственно количество найденных строк будет 0. Поэтому, в качестве ответа, вернется сообщение о том, что данный пользователь не состоит ни в одном сегменте;
  3. Если при добавлении пользователю перечислены несуществующие в таблице segments названия сегментов, то записи не будут созданы в таблице user_segments, а в качестве ответа вернется массив элементов, которые не были добавлены из-за этого несоответсвия. В остальном в таблицу будут добавлены новые записи, содержащие в себе ID пользователя и сегмент (и так для каждого из перечисленных существующих сегментов);
  4. Если при удалении у пользователя перечислены несуществующие в таблице user_segments названия сегментов, то подходящие записи не найдутся, а следовательно, в качестве ответа, будет возвращен массив элементов, которые удалить не удалось. В остальном у данного пользователя будут убраны соотвествующие сегменты из данной таблицы.

Ведение истории операций

При выполнении операции добавления\удаления в файле (docs/history.csv) автоматически будут появляться записи в формате CSV. Чтобы посмотреть данный файл, нужно перейти по адресу: localhost:3001/docs/history.csv (если запущено в Docker).

Пример вывода:

Снимок экрана 2023-08-31 в 22 26 43

Взаимодействие

Для взаимодействия с сервером есть 4 способа:

  1. /create-segment -- создать сегмент. Принимает JSON в качестве тела запроса;

  2. /delete-segment -- удалить сегмент. Принимает JSON в качестве тела запроса;

  3. /user-in-segment -- выполнить операции создания и\или удаления для определенного пользователя. Принимает JSON в качестве тела запроса;

    3.1. Поле "ttl" передается как массив int-ов, Так, элемент на позиции i будет назначен элементу на позиции i в массиве "seg_titles_to_add". Если значение 0, тогда назначение TTL игнорируется. Передается в секундах.

  4. /get-user-segments -- получить все сегменты, в которых состоит пользователь. Принимает JSON в качестве тела запроса.

В качестве ответа возвращается JSON, который содержит всегда поле "status" и дополнительные.

image

Сами структуры запросов и ответов в этих файлах:

  1. https://github.com/Icerzack/avito-backend-internship/blob/main/internal/pkg/model/segment.go
  2. https://github.com/Icerzack/avito-backend-internship/blob/main/internal/pkg/model/user_segment.go

Примеры

Примеры запросов и ответов в Postman:

  1. Создать сегмент (корректный запрос):

image

  1. Удалить сегмент (корректный запрос):

image

  1. Создать сегмент (некорректный запрос, неправильное наименование полей):

image

  1. Добавить пользователя в сегмент (корректный запрос):

image

  1. Добавить пользователя в сегмент (корректный запрос, но некоторые названия не существуют):

image

  1. Удалить пользователя из сегмента (корректный запрос):

image

  1. Удалить пользователя из сегмента (корректный запрос, но некоторые названия не существуют):

image

  1. Одновременное создание и удаление (корректный запрос, но некоторые названия не существуют):

image

  1. Создать сегменты с временем жизни (корректный запрос)

image

  1. Получить сегменты пользователя (корректный запрос):

image

  1. Получить сегменты пользователя (корректный запрос, но данный пользователь не содержит сегментов):

image

Установка и запуск

Проект содержит Makefile, который имеет следующий вид:

.PHONY: compose-db-up
compose-db-up:
	docker-compose build
	docker-compose up -d postgres

.PHONY: compose-db-rm
compose-db-rm:
	docker-compose down

.PHONY: compose-app-up
compose-app-up:
	docker build -f build/Dockerfile -t avito-app .
	docker-compose up -d avito-app

.PHONY: compose-app-rm
compose-app-rm:
	docker-compose down

.PHONY: compose-all-up
compose-all-up: compose-db-up compose-app-up

.PHONY: compose-all-rm
compose-all-rm: compose-app-rm compose-db-rm

.PHONY: run-tests
run-tests:
	go test -v avito-backend-internship/internal/app

Для запуска конкретно одного из компонентов приложения нужно, например, прописать следующее:

$ make compose-db-up

Для поднятие всего целиком:

$ make compose-all-up

Тесты и CI

Для тестирования использовалась библиотека gomock. С ее помощью были созданы моковые реализации БД и Сервиса на основании их интерфейсов. Данные реализации применяются в файле (internal/app/handlers_test.go), который тестирует главный функционал приложения (корректная обработка URL и добавление в БД).

В проекте был настроен простой CI для запуска кода на тестах. Файл конфигурации может быть найден по следующему пути: (.github/workflows/tests.yml) и имеет следующий вид:

name: tests

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:

  build:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout repository
      uses: actions/checkout@v3

    - name: Set up Go
      uses: actions/setup-go@v3
      with:
        go-version: 1.19

    - name: Tests
      run: go test -v avito-backend-internship/internal/app

(К началу)

About

Сервис динамического сегментирования пользователей

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published