Разработка сервиса для свободного обмена одеждой

База данных как неотъемлемая часть информационной системы. Шардинг – стратегия масштабирования приложений. Характеристика основных критериев, от которых зависит выбор языка разработки серверной части. Описание экранов пользовательского интерфейса.

Рубрика Программирование, компьютеры и кибернетика
Вид дипломная работа
Язык русский
Дата добавления 24.08.2020
Размер файла 1,6 M

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

В документоориентированных СУБД, как правило, хранятся иерархические структуры данных. Сама база данных состоит из коллекций, в которых хранятся документы. В документе хранятся поля в виде «ключ-значение». Документ составлен в представлении JSON (Java Script Object Notation), а внутренняя организация хранения и передачи данных между клиентом и сервером осуществляется в его бинарном представлении: BSON (Binary-encoded serialization of JSON). Каждому документу автоматически присваивается идентификатор специального типа ObjectId шестнадцатеричное строковое значение - по сути первичный ключ. Если в базу данных записывается документ без идентификатора, MongoDB генерирует уникальное поле «_id» автоматически.

Всего необходимо реализовать 4 коллекции для хранения данных. Коллекции были созданы на основе диаграммы «Сущность - связь», проектирование документоориентированной базы данных не требует отвечать стандартам ACID, соответственно нормализация данных для документоориентированных БД необязательна. Данные в MongoDB хранятся в качестве атрибутов в едином документе формата JSON. В такой модели данные оптимизированы для интуитивно понятной разработки и горизонтальной масштабируемости.

Коллекция categories-справочник категорий предметов одежды, где:

«_id»-идентификатор типа «ObjectId».

«category» - строковое значение (название категории, например, «Обувь»).

Коллекция users - содержит сведения обо всех зарегистрированных пользователях, где:

«_id»-идентификатор пользователя. Тип «ObjectId».

«name» - имя пользователя, указанное при регистрации. Тип «String».

«email» - электронная почта пользователя, указанная при регистрации. Тип «String».

«password» - зашифрованная строка-токен, содержит пароль. Тип «String».

«date» - дата регистрации. Тип «Date».

«instagram» - профиль пользователя в Instagram. Тип «String».

«phone» - мобильный телефон пользователя. Тип «String».

«avatar» - ссылка на хранящееся изображение. Тип «String».

Коллекция items- содержит ссылку на документ в коллекции usersи массив поддокументов-товаров, где:

«_id»-идентификатор документа коллекции типа «ObjectId».

«userId» - связка с документом в коллекции «user» по идентификатору. Тип «String» или «ObjectId».

«items» - список всех опубликованных пользователем товаров. Тип «Array».

Массив «items» - это массив поддокументов, каждый из которых имеет следующую структуру:

«_id»-идентификатор типа «ObjectId».

«title»-наименование товара типа «String».

«description»-описание товара. Тип «String».

«userId»-ссылка на автора объявления. Тип «ObjectId».

«category»-ссылка к документу коллекции «categories». Тип «ObjectId».

«photos»-массив ссылок на фотографии, которые иллюстрируют загруженный товар. Тип «Array». Внутри объекты типа «String».

«tags»-массив тегов для описания товара. Тип «Array». Внутри хранятся объекты «tag» с типом «String» и идентификатором «_id» типа «ObjectId».

Коллекция likedItems - содержит ссылку на документ в коллекции users, массив поддокументовdislike, где хранятся «_id» товаров, которые не понравились пользователю и массив поддокументовpairs, где хранятся сведения о пользователе, которому текущий пользователь подошел. Коллекция описана следующим образом:

«_id»-идентификатор документа коллекции типа «ObjectId».

«dislike» -поддокумент, где хранятся идентификаторы товаров, которые не понравились пользователю (необходимо для формирования ленты товаров). Тип «Array». Внутри находятся объекты типа «String».

«userId» - идентификатор пользователя, для которого отслеживаются «pairs» и «dislike». Тип «String».

«pairs» -поддокумент, где хранятся понравившиеся товары. Тип «Array».

При этом в каждом объекте массива «pairs» находится:

«items» - поддокумент, который хранит идентификаторы понравившихся товаров. Тип «Array».

Вподдокументе хранятся ссылки на товары типа «ObjectId».

«_id» - идентификатор элемента массива «pairs». Тип «ObjectId».

«userId» - идентификатор пользователя, которому принадлежат эти товары. Тип «String».

Несмотря на то, что используется нереляционная база данных, необходимо выделить сущности.Основные сущности - это «Пользователь», «Товар» и «Обмены». Любой обмен состоит из пары пользователей и пары товаров соответственно (чтобы пара была подобрана, необходимо, чтобы пользователю А понравился товар Б, и наоборот). Также обмен может отслеживаться с помощью статусной модели. Каждый пользователь имеет отношение к двум спискам товаров: товары, которые опубликовал сам пользователь, и товары, которые понравились пользователю во время пролистывания ленты. Сущности «Тип товара» и «Стиль» достаточно условны, они содержат описательные данные о загружаемом товаре (предмете гардероба). Для каждого пользователя формируется «Рейтинг». Рейтинг высчитывается на основе успешных/неуспешных обменов, количества товаров и заполненности профиля. Модель данных представлена ниже. (см.рисунок 10).

Рис. 10. Диаграмма «Сущность - связь» для приложения обмена одеждой

Ввиду своей иерархической структуры хранения данных MongoDB позволит делать быстрые операции над коллекциями, документами, т.к. объектный язык запросов в этом случае намного эффективнее стандартных SQL-запросов.

Для манипуляции с данными необходимо разработать функции, которые позволяют:

Организовать операции CRUD для сущностей «Товар» и «Пользователь».

Соотнести списки понравившихся товаров для создания сущности «Обмен».

Отобразить контактные данные тех пользователей, которым взаимно понравились опубликованные предметы одежды.

Отслеживать, какие товары пользователь уже просмотрел, чтобы формировать качественную выдачу на главном экране.

В будущих версиях сервиса для свободного обмена одеждой также планируется:

Отслеживать статусную модель сущности «Обмен».

Высчитывать переменную «Рейтинг» в сущности «Пользователь» в зависимости от совершенных обменов.

Отслеживать местоположение пользователя (а соответственно и товаров) для более удобного поиска и настройки ленты просмотра.

Выводы по главе 3

В третьей главе выделены основные нефункциональные требования к приложению, а именно: адаптивность, кроссбраузерность, соответствие дизайн-системе продукта (общий унифицированный стиль). Также определены принципы визуального проектирования интерфейса: иерархия, фокус, контраст, выравнивание и баланс элементов, баланс пространства. Далее спроектированы и описаны экраны пользовательского интерфейса, которые полностью покрывают ранее смоделированный бизнес-процесс обмена одеждой. Из получившихся экранов сформирована диаграмма переходов между экранами - спроектирована навигация приложения.

Для выбранной СУБД во второй главе была составлена модель данных в виде диаграммы «сущность - связь». Описано представление коллекций и документов, которые будут разработаны далее средствами СУБД MongoDB. Описано четыре коллекции. Также перечислены виды функций, которые необходимо разработать для манипуляции с данными.

4. Разработка сервиса для свободного обмена одеждой

В четвертой главе представлен обзор процесса разработки серверной части, в том числе настройка маршрутизации и подключение к базе данных, представлен процесс разработки компонентов на клиентской части, управление состоянием приложения с помощью технологии redux.Описан процесс реализация базы данных, показан процесс и результаты тестирования запросов к базе данных, подключение к удаленному серверу MongoDBAtlas и реализация моделей данных, необходимых для корректной работы приложения. интерфейс шардинг информационный серверный

Реализация базы данных

В разделе приведен процесс и технология создания схем (моделей данных) для взаимодействия с коллекциями базы данных. Далее проиллюстрирован процесс тестирования созданных запросов к базе с помощью инструмента Postman.

Настройка подключения и развертка удаленного сервера базы данных

Необходимо установить MongoDBна компьютер, на котором ведется разработка. Установочный дистрибутив MongoDB«весит» 263МБ для операционной системы Windows 10.

Сама база данных будет развернута на удаленном сервере с помощью облачного сервиса MongoDBAtlas. В рамках бесплатного тарифа M0 MongoDBAtlasпредоставляет 500 мегабайт для хранения запросов и коллекций документов. Подключиться к удаленной базе данных можно и через локальный клиент MongoDBCompass. Для того, чтобы можно было получить доступ к удаленной базе данных необходимо добавить IPадрес в список разрешенныхIPадресов, создать пользователя и назначить ему соответствующие права доступа. Затем получить строку подключения MongoURI. Эту строку можно использовать в приложении.

Получать данные и отображать их очень просто. ВMongoDB достаточно гибкий JSON-формат документов и низкий порог освоения технологии: не нужно специальных знаний языка SQL. Таким образом, простая логика и простые запросы реже создают проблемы.

Для того, чтобы подключить базу данных к серверной части, необходимо знать строку подключения (MongoURI) и вызвать метод «.connect()», который принимает MongoURI (для удобства MongoURI вынесен в отдельный модуль), затем можно указать дополнительные параметры. Метод «.connect()»возвращает объект типа «Promise», где потребитель «.then()»сообщает в консоли сервера, что подключение было успешно, или в случае ошибки (потребитель «.catch()») записывает ошибку в консоль и завершает процесс подключения.

const mongoose = require('mongoose');

const db = require('./config/keys').mongoURI;

//db подключение

mongoose

.connect(db, {

useNewUrlParser: true,

useUnifiedTopology: true,

useFindAndModify: false

})

.then(() => console.log('Подключено к базе!'))

.catch((err) => {

console.log('Подключение невозможно');

console.log(err);

process.exit();

});

Описание моделей данных

Для работы с MongoDB вNode.js была использована технология Mongoose. Она позволяет объектно моделировать сущности для большего удобства работы с данными. Mongooseпозволяет реализовать валидацию, строить запросы и настраивать бизнес логику. Mongooseустанавливается с помощью пакетного менеджера NPM, а затем подключается в серверной части следующим образом: «constmongoose = require('mongoose');».

В серверной части каждая модель описана отдельным файлом, который находится в папке «Models». Всего для приложения реализовано шесть схем: «ItemSchema», «CategorySchema», «ChangeSchema», «LikedItemSchema», «LikedItemCollectionSchema», «UserSchema».

Пример описания схемы сущности «Items». Каждый раз, когда в контроллере необходимо будет обращаться к сущности, необходимо использовать экспортированный модуль «Items» (т.е. экспортированную схему). Схема представляет собой JavaScriptобъект, для полей которого можно настроить различные типы, ограничения, и значения по умолчанию.

const mongoose = require('mongoose');

const Schema = mongoose.Schema;

const CategorySchema = require('./Category');

const ItemSchema = new Schema({

_id: Schema.Types.ObjectId,

userId: Schema.Types.ObjectId,

title: String,

category: String | CategorySchema,

description: String,

tags: [

{

tag: String,

},

],

photos: [String],

});

const ItemsSchema = new Schema({

userId: {

type: String,

required: true,

},

items: [ItemSchema],

});

module.exports = Items = mongoose.model('items', ItemsSchema);

Описание всех моделей данных находится в приложении Д.

Процесс тестирования запросов к базе данных

Для тестирования запросов был использован сервис Postman.Postman предназначен для проверки запросов с клиента на сервер и получения ответа от бэкенда. Алгоритм работы с postmanпри тестировании всех запросов одинаков.

В ходе работы были использованы и протестированы следующие типы запросов: «POST», «GET», «PUT» или «DELETE». Каждый тестируемый адрес запроса состоит из двух частей: адрес хоста (в данном случае «http:/localhost:5000»), иадрес конечной точки, например, «/api/users/login».

Postmanпозволяет ввести данные для тестирования: во вкладке «Params» можно указать передаваемые параметры запроса. Во вкладке «Body» можно задать тело запроса, т.е. что именно будет отправлено на сервер.

Результат запроса всегда представлен в формате текста, JSON или HTML-страницы.

Рассмотрим пример тестирования регистрации пользователя. Для этого по указанному URL («http:/localhost:5000/api/users/register»)отправляются в теле запроса поля из формы регистрации: «Имя», «Email», «Пароль» и «Повторите пароль». Поле «Повторите пароль» перед сохранением в базе данных сравнивается с полем «Пароль», если они не одинаковы, сервер возвращает сообщение пользователю, что пароль должны совпадать. В данном случае пароли верны, Emailвведен корректно, пользователя с такими же параметрами еще не существует в базе данных, а следовательно, можно зарегистрировать пользователя. Сервер возвращает положительный результат (STATUS 200 OK) и возвращает объект в формате JSON. Это новый пользователь.

Разработка серверной части сервиса

В разделе описан процесс разработки серверной части сервиса для свободного обмена одеждой, используя технологии Node.jsи Express.js. Исходный код серверной части опубликован в репозитории на GitHub:https://github.com/plastya-flomaster/swop-backend.

Стартовая конфигурация сервера

Инициализация проекта выполнена с помощью пакетного менеджераNPM. Команда npminit позволяет инициализировать проект следующим образом:

Рис. 11. Инициализация проекта с помощью команд менеджера NPM

Для работы также потребовались следующие пакеты. Каждый пакет также установлен с помощью пакетного менеджера NPMкомандой «npminstall<Имя библиотеки>»:

bcryptjs: используется для хеширования паролей, перед записью в базу данных.

body-parser: промежуточное программное обеспечение, чтобы разбирать и считывать входящие запросы (тело запроса).

express: фреймворк для Node.js, чтобы реализовать маршрутизацию, обработку запросов, и формирование ответов с сервера.

jsonwebtoken: утилита для авторизации.

mongoose: библиотека для взаимодействия с MongoDB в объектном стиле.

passport: аутентифицирует запросы, с помощью плагинов-стратегий (в работе будет использоваться плагин passport-jwt).

passport-jwt: используется для аутентификации с помощью JSON Web Token (JWT);

validator: библиотека с инструментами для валидации полей.

Nodemon: полезная утилита для разработки, которая позволяет запускать и обновлять сервер автоматически, когда в файлы вносятся изменения.

Логика приложения разделена: выделена папка Controllers, в которой хранятся файлы, отвечающие за манипуляцию с данными, логику обработки данных во всем приложении.

Маршруты (папка routes) содержат адреса конечных точек (endpoints), и обращения к необходимым функциям, реализованных в контроллерах [16]. Для считывания или изменения данных используется экспортированная mongoose Schema той коллекции, с которой необходимо работать. На рисунке12 изображена схема взаимодействия внутри сервера, когда приходит запрос с клиентской части.

Рис. 12. Схема обработки запроса в серверной части приложения

Рассмотрим взаимодействие на примере получения данных обо всех товарах пользователя.

Шаг 1. Отправка данных с клиентской части

Для управления состоянием приложения в клиентской части используется Redux и Redux-thunk. Получение данных - это как раз thunkфункция. В ней выполняется GET-запрос по адресу `/api/items/:id'. Ранее в настройках axiosбыл указан параметр «BaseURL: `http:/localhost:5000'» поэтому можно указать только относительный путь.

Синтаксис ES6 позволяет использовать интерполяцию, и вставлять значение параметра прямо в строку, используя конструкцию ${<Параметр>}.

export const getAllMine = (userId: string) => (

dispatch: Dispatch<AppActionType>

) => {

dispatch(sendLoading());

axios

.get(`/api/items/${userId}`)

.then((res) => dispatch(sendItems(res.data)))

.catch((error) => dispatch(sendErrors(error.response.data)));

};

Шаг 2. Поиск конечной точки по маршруту

В папке «api/routes/» находится файл «items.js», где с помощью объекта router из библиотеки Express.jsможно получить доступ к функции контроллера«.getAllMine()».

const express = require('express');

const router = express.Router();

const items = require('../../controllers/items.controller');

//@route GET api/items/:id

//@desc получаем все товары пользователя

router.get('/:id', items.getAllMine);

Также, для того, чтобы приложение имело доступ к этим маршрутам, необходимо экспортировать их в главный файл «server.js», и вызвать команду «app.use()».

const items = require('./routes/api/items');

const app = express();

app.use('/api/items', items);

Шаг 3. Вызов функции в контроллере

Функция «.getAllMine()» - асинхронная. Реализация функции может быть на основе промисов (как в примере ниже), так и используя ключевые слова «async»,«await». Функция принимает до трех параметров: первый параметр - это объект запроса, второй параметр - объект ответа, который отправится на клиентскую сторону, третий параметр - это функция «.next()», которая может указывать, что выполнение еще не закончено, и требуется вызвать следующую функцию.

//найти товары пользователя

exports.getAllMine = (req, res) => {

const userId = req.params.id;

Items.findOne({ userId })

.then((items) => {

if (!items) {

return res.status(400).send('Ошибка пользователя нет в базе товаров!');

} else {

if (items.items) {

return res.status(200).send(items.items);

} else

return res

.status(400)

.send(

'Пока у вас нет товаров, чтобы обменяться! Добавьте новый товар!'

);

}

})

.catch((err) => {

return res.status(500).send(err);

});

Шаг 4. Возвращение результата запроса

Возвращение результата происходит внутри потребителя «.then()». Сначала для результата устанавливается HTTP-статус, а затем с помощью метода «.send()» отправляются необходимые данные. При этом «.send()» принимает как JSONтак и обычные типы JavaScript.

Подключение к базе данных и настройка удаленного сервера

Необходимо иметь MongoDBна компьютере, на котором ведется разработка. Установочный дистрибутив MongoDB«весит» 263МБ для операционной системы Windows 10.

Сама база данных будет развернута на удаленном сервере с помощью облачного сервиса MongoDBAtlas. В рамках бесплатного тарифа M0 MongoDBAtlasпредоставляет 500 мегабайт для хранения запросов и коллекций документов. Подключиться к удаленной базе данных можно и через локальный клиент MongoDBCompass. Для того, чтобы можно было получить доступ к удаленной базе данных необходимо добавить IPадрес в список разрешенныхIPадресов, создать пользователя и назначить ему соответствующие права доступа. Затем получить строку подключения MongoURI. Эту строку можно использовать в приложении.

База данных подключена к серверной части, через строку подключения (MongoURI) и метод «.connect()», который принимает MongoURI (для удобства MongoURI вынесен в отдельный модуль). Затем можно указать дополнительные параметры.

const mongoose = require('mongoose');

const db = require('./config/keys').mongoURI;

//db подключение

mongoose

.connect(db, {

useNewUrlParser: true,

useUnifiedTopology: true,

useFindAndModify: false

})

.then(() => console.log('Подключено к базе!'))

.catch((err) => {

console.log('Подключение невозможно');

console.log(err);

process.exit();

});

Разработка клиентской части сервиса

В этом разделе описано, почему выбран функциональный подход к разработке компонентов React, перечислены реализованные компоненты, дано описание процесса управления состоянием клиентского приложения с помощью технологии Redux и механизм отправки данных на сервер и получения данных. Также в конце раздела представлено описание реализованных визуальных форм.

Исходный код клиентской части приложения для свободного обмена одеждой опубликован в виде репозитория на GitHub. Доступ к исходному коду по ссылке: https://github.com/plastya-flomaster/swop-frontend.

Функциональный подход к созданию компонентов React

Функциональные компоненты в Reactдают возможность использовать новую технологию ReactHooks.Hooks -- нововведение в React 16.8, которое позволяет использовать состояние и другие возможности React без написания классов.

В React нет способа «присоединить» повторно используемое поведение к компоненту (например, подключение к хранилищу). Типичное крупное React-приложение представляет собой глубокое дерево иерархии компонентов, покружённых провайдерами, консьюмерами, компонентами высшего порядка, рендер-пропсами и другими абстракциями. Это проблема React, поэтому с 16.8 версии был предложен более удобный способ повторно использовать логику вокруг состояния.

С помощью Hooks можно извлечь логику состояния из компонента, чтобы её протестировать или повторно использовать. Hooks позволяют повторно использовать логику состояния, не затрагивая дерево компонентов. Благодаря этому, Hooks легко использовать в разных компонентах и делиться ими с сообществом.

В официальной документации Reactприводится два основных правила для использования Hooks:

Hooks следует вызывать только на верхнем уровне. Недопустим вызов hooks внутри циклов, условий или вложенных функций.

Hooks следует вызывать только из функциональных компонентов React. Недопустим вызов hooks из обычных JavaScript-функций. (Не распространяется на пользовательские hooks).

Более того, функциональный подход в написании компонентов позволяет облегчить восприятие и написание кода, исчезает парадигма жизненного цикла компонента, которая была неотъемлемой частью классовых компонентов. Hooks описывают все так, как оно должно быть в любой момент времени, и помогают не думать о том, как компоненту реагировать на изменения. В будущем чистые функции будут выигрывать по скорости работы в сравнении с классами из-за отсутствия методов жизненного цикла. Функции легче: для создания функции не нужно создавать класс, наследоваться от интерфейса (ключевое слово «extends»), не нужно создавать конструктор. А использование чистых функций уменьшает вероятность написания плохого кода. У разработчика нет необходимости использовать контекст. Код избавляется от конструкции thisи становится более удобочитаемым. С помощью чистых функций создаются компоненты без внутреннего состояния. Описание компонентов с помощью чистых функций создает меньше кода, следовательно, код легче поддерживать. Чистые функции намного проще тестировать. Необходимо просто передать propsи ожидать определенных действий.

Реализованные компоненты сервиса

В исходном коде проекта компоненты хранятся в папках, которые объединяют компоненты по смыслу. Самые объемные компоненты - компоненты, отвечающие за рендеринг целых страниц. Они находятся в папке «Pages». Таких компонентов всего шесть:

Компонент «Главная страница».

Компонент «Страница помощь».

Компонент «Страница редактирования данных о пользователе».

Компонент «Сведения о пользователе».

Компонент «Страница совпадений».

Вспомогательный компонент «Страница 404». Рендерится тогда, когда пользователь перешел на некорректный URL.

Для входа в приложение реализовано два компонента: «Окно регистрации пользователя» и «Окно входа в приложение».

Отображение ленты товаров (карточки товаров) реализовано вместе с вспомогательными кнопками «свайп влево» и «свайп вправо». Это компоненты группы «Cards».

Большое количество компонентов создано, для реализации функционала товаров: компонент для добавления и отображения информации о товаре, отображение всех товаров пользователя, отображения одного товара в сокращенном виде.

Также для сервиса реализовано два Reactrouter'а. Роутеры позволяют рендерить определённый компонент во время обращения к нему (например, при изменении URLили переходе по ссылке). Технологии динамического роутинга позволяют не держать в памяти приложения все варианты, а отображать только то, с чем работает пользователь, а остальным компонентам присваивать значение «null».

В приложении есть и более мелкие компоненты, которые не содержат логики или она достаточно простая, например: компонент «UserPic» служит, чтобы отобразить имя и фотографию пользователя, компонент «TagsInput» - самостоятельно реализованное поле ввода тегов и др.

Управление состоянием. React + Redux

Redux- это контейнер предсказуемых состояний для JavaScript приложений. Redux возник из идеи Flux -архитектурного паттерна, созданного инженерами из Facebook.Redux, с точки зрения кодаJavaScript- это объект, внутри которого лежат данные. Он используется остальными частями приложения для их хранения, изменения и извлечения. В терминологии Redux он называется контейнер, так как данные хранятся внутри.

Официальная документация Reduxрекомендует использовать свою библиотеку в тех случаях, если:

Существуют обоснованные объемы данных, меняющихся со временем.

Необходим один источник информации для состояния приложения.

Хранение состояния приложения в компоненте верхнего уровня уже недостаточно или небезопасно.

Основными терминами Redux являются:

State - состояние приложения (или его части) хранящееся в Store. Также есть специальный initialState - объект, описывающий начальное состояние приложения.

Store - все состояние приложения сохранено внутри этого объекта. Единственный способ изменить дерево состояния - это вызвать action.

Action - объект, который описывает то, что должно произойти. ПО соглашению, каждый Actionимеет тип (type) и загрузку (payload). Тип классически задается строкой, а загрузка это та часть нового состояния, которое предстоит изменить в Reducer.

Reducer - функция, которая ожидает Actionи в соответствии с тем, какой Actionпришел -возвращает новое состояние. Важно отметить то, что состояние никогда не мутирует, а всегда изменяется и замещается. Это позволяет Redux стабильно управлять состоянием всего приложения.

На рисунке 13 продемонстрирована схема взаимодействия при использовании технологии Redux. Как видно из схемы, действия могут поступать с представления от пользователя, или приходить из некого внешнего API. А представление ожидает изменения состояния в Store.

Рис. 13. Поток данных Redux + React.

Первоначальная настройка Reduxтребует относительно большого количества кода и времени, однако эта настройка выполняется один раз и для других компонентов может быть сделана по аналогии.

В проект была загружена библиотека Reduxс помощью пакетного менеджера NPM(Команда для загрузки«npminstall --saveredux»). Поскольку разработка ведется на языке Typescript, необходимо было также установить «@types»для библиотеки Redux. Затем аналогичной командой нужно установить библиотеку-связку с React «react-redux» и «redux-devtools»для того, чтобы стала возможна отладка в браузере.

Дальнейшая настройка Reduxбудет описана по шагам. Рассмотрим процесс настройки управления состояниями товаров.

Шаг 1. Инициализация Store и подключение Dev-tools

Для того, чтобы избежать путаницы и структурировать исходный код проекта, была создана новая директории «Redux», где выделено еще три директории: «Actions», «Stores», «Reducers». Каждая директория, соответственно хранит файлы, относящиеся к этим сущностям Redux. Все файлы в этих папках будут иметь расширение «.ts», поэтому потребуется дополнительный код, отвечающий за типизацию создаваемых объектов.

В папке Storesсоздан файл «Store.ts». Ниже представлен полный код, описывающий хранилище состояний в Redux.

import { createStore, applyMiddleware, compose } from 'redux';

import thunk from 'redux-thunk';

import rootReducer from '../Reducers/rootReducer';

const initialState = {};

export type AppState = ReturnType<typeof rootReducer>

const store = createStore(

rootReducer,

initialState,

compose(

applyMiddleware(thunk),

(window as any).__REDUX_DEVTOOLS_EXTENSION__ &&

(window as any).__REDUX_DEVTOOLS_EXTENSION__()

)

);

export default store;

Сначала задано начальное состояние приложения в виде пустого объекта «constinitialState = {}». Затем с помощью redux-функции «createStore()» сформированstore, который содержит корневой reducer (комбинация других reducer'ов приложения, см. Шаг 2), начальное состояние и store подключен к thunkдля создания асинхронной логики в работе со store, также здесь подключено расширение «REDUX_DEVTOOLS»для того, чтобы в браузере стала доступна отладка.

Последняя строка кода делает storeдоступным в приложении. Теперь storeможно подключить к любому компоненту как модуль с помощью конструкции «import<…>from<…>».

Чтобы приложение начало использовать store, необходимо подключить его в корневом компоненте. Для этого в компоненте «App.tsx», из библиотеки react-reduxимпортирован контейнер «Provider», который «оборачивает» весь код.Теперь, все дочерние компоненты, которые будут связаны с Redux, будут иметь доступ к глобальному store.

import * as React from 'react';

import { Provider } from 'react-redux';

import store from './redux/Stores/store';

const App: React.FC = () => {

return (

<Provider store={store}>

<Кодпрограммы>

</Provider>

);

};

export default App;

Шаг 2. Описание RootReducer и разделение кода на разные Reducer'ы

TypeScript требует четкой типизации во всем приложении. Код Redux- не исключение. Для того, чтобы описать reducer, сначала был описан интерфейс для этого reducer'а. Интерфейс для «ItemsReducer» будет выглядеть следующим образом:

export interface IItemsReducer {

loading: boolean;

items: IItem[];

error: any;

}

Где:

Поле «loading»- это логическая переменная, показывающая, загрузились ли все запрошенные товары на клиентскую часть. Поле «items» - это массив типаIItem, который описывает то, в каком виде представлены на клиентской части товары. И в случае, если ожидаемого результата не предвиделось, поле «error» типа«any». Как правило, устанавливать тип «any» считается плохой практикой в TypeScript. Однако, ошибки могут приходить в виде объектов, строк и даже массивов. Но здесь нам важно наличие или отсутствие ошибки, поэтому было принято решение не типизировать это поле. По умолчанию поле «error» имеет значение «null».

Теперь необходимо реализовать «itemsReducer» только что созданного типа.В reducer'eзадан «initialState» со значением пустого массива, затем реализована конструкция switch, где «ItemsReducer»в зависимости от пришедшего типа Action будет изменять состояние Store. Например, для типа «ITEM_LOADING», установлено соответствующее ему поле в значение «истина», а остальное состояние остается без изменений (оператор «…» (spread) помогает в этом). В любом ином случае (ветка «default»), «ItemsReducer»будет возвращать неизменное состояние.

import { IItemsReducer } from "./reducerTypes";

import { ItemsActions } from '../Actions/itemsActions';

const initialState: IItemsReducer = {

loading: false,

items: [],

error: null

};

export default (state = initialState, action: AppActionType): IItemsReducer => {

switch (action.type) {

case ItemsActions.GET_ITEMS:

return {

...state,

loading: false,

items: action.payload,

};

case ItemsActions.ITEM_LOADING:

return {

...state,

loading: true

};

case ItemsActions.ITEM_ERROR:

return {

...state,

loading: false,

error: action.payload

};

default:

return state;

}

}

Затем, с помощью функции combineReducers() в файле «RootReducer.ts» создается однаreducer-функция, объединяющая все имеющиеся. Импортированный «itemsReducer»передается в функцию и получает более краткое название для удобства («items»).

import { combineReducers } from 'redux';

import itemsReducer from './itemsReducer';

export default combineReducers({

items: itemsReducer,

<Другие reducer'ы>

});

Шаг 3. Описание Actions

Для корректной работы с TypeScript были описаны типы. Сначала задан общий тип «AppActionType», который описывает типы Аctions для всего приложения. «AppActionType» в свою очередь состоит из типов, которые будут принимать reducer'ы. В данном случае это будет тип «ItemTypes», который состоит из интерфейсов, соответствующих действиям в reducer'е:

interface IGetAllItems {

type: typeof ItemsActions.GET_ITEMS;

payload: IItem[];

}

interface IItemLoading {

type: typeof ItemsActions.ITEM_LOADING;

}

interface IItemError {

type: typeof ItemsActions.ITEM_ERROR;

payload: any;

}

type ItemTypes =

| IGetAllItems

| IItemLoading

| IItemError;

После того, как созданы типы для Actions, можно реализовать логику самих Actions (файл «itemsActions.ts»), где:

Описано в виде типа «enum»все существующие типы Actions для сущности «Товар». Это нужно для того, чтобы не допускать ошибок из-за опечаток и ускорить написание кода: TypeScript во время разработки будет подсказывать и автоматически дописывать типы, если они объявлены в «enum».

Затем реализован каждый Action. Ниже проиллюстрирован примерAction'а на удаление объекта. Внутри поля «payload» типа строка находится идентификатор того товара, который нужно удалить. Подобным образом реализованы все остальные Actions (всего шестьфункций). Важно отметить, чтоActionлишь сообщает что нужно сделать с состоянием, но не как. Само изменение состояния происходит внутри Reducer'a.

export const deleteItem = (payload: string): AppActionType => ({

type: ItemsActions.DEL_ITEM,

payload,

});

Шаг 4. Подключение Redux к функциональному компоненту

Нет необходимости связывать каждый компонент react-приложения с Redux. Так, например, глупые компоненты (от англ. «dummycomponents») нужны лишь для отображения данных, внутри них нет даже reactstate. Поэтому, следует подключать только те компоненты, от которых зависит изменение состояния. Так приложение получается более эффективным и быстрым.

В конце каждого компонента есть специальная конструкция, которая позволяет экспортировать этот компонент, и использовать его, например, в Router.

export default UserPage;

Для того, чтобы подключить компонент «UserPage» к reduxstore, необходимо положить его в специальный контейнер и по сути, экспортировать уже новый контейнер, внутри которого находится «UserPage». Для этого необходимо была импортирована функция «connect()» из библиотеки react-redux.

export default connect(mapStateToProps, mapDispatchToProps)(UserPage);

Внутри функции также находятся две функции: «mapStateToProps»и «mapDispatchToProps». Эти функции необходимы, чтобы связать компонент со store. Так, например, «mapStateToProps» получает часть состояния redux-state, а именно, объект «items» и присваивает его свойствам (props)react-компонента «UserPage». Для сохранения логики и читабельности кода этот propsкомпонента также назван «items».

Функция«mapDispatchToProps» позволяет отправлять Actions (dispatchactions) из компонента. В примере кода ниже видно, что в «mapDispatchToProps» выполняется привязка функции «getAllMine» к данном компоненту.

const mapStateToProps = (state: AppState) => ({

items: state.items.items,

});

const mapDispatchToProps = (

dispatch: ThunkDispatch<any, any, AppActionType>

) => ({

getAllMine: bindActionCreators(getAllMine, dispatch),

});

Шаг 5. Вызов функции и dispatch

В данном случае логика компонента такая: UserPageпоказывает сведения о пользователе и его товарах, здесь же пользователь может добавить, удалить, отредактировать товар и увидеть все свои товары. Нет необходимости каждый раз подгружать все товары пользователя, их можно хранить внутри состояния приложения, а отображать лишь при переходе на страницу. Поэтому функция «getAllMine()», которая получает все товары выбранного пользователя, будет срабатывать только тогда, когда меняется пользователь. (например, при выходе из приложения, и входе нового пользователя). Для этого используется reacthook«useEffect», который срабатывает после того, как изменилось состояние объекта «user». Благодаря функции «mapDispatchToProps», функция «getAllMine» стала доступна из свойств (props)компонента. В нее мы передаем идентификатор пользователя, товары которого мы должны получить.

useEffect(() => {

if (props.user._id) props.getAllMine(props.user._id);

}, [props.user]);

После этого функция отправляет соответствующие actionsи делает запрос к серверу для получения данных. Сначала в глобальном storeустанавливается состояние «загрузка», затем, функция дожидается прихода данных с сервера и устанавливает либо новое состояние store с пришедшим списком товаров, либо отображает ошибку.

export const getAllMine = (userId: string) => (

dispatch: Dispatch<AppActionType>

) => {

dispatch(sendLoading());

axios

.get(`/api/items/${userId}`)

.then((res) => dispatch(sendItems(res.data)))

.catch((error) => dispatch(sendErrors(error.response.data)));

};

Отправка данных на сервер

Отправка данных на сервер реализуется с помощью GET, POST, PUT, DELETE запросов. Чтобы упростить обработку запросов на сервере был использован фреймворк Express.js, который представляет собой надстройку над node.js, упрощающую реализацию серверной части.

Для обработки запросов в Express определено ряд встроенных функций, и одной из таких является функция.get(). Она обрабатывает GET-запросы протокола HTTP и позволяет связать маршруты с определенными обработчиками. Для этого первым параметром передается маршрут, а вторым - обработчик, который будет вызываться, если запрос к серверу соответствует данному маршруту:

const express = require('express');

const router = express.Router();

const categories = require('../../controllers/categories.controller');

// api/categories

router.get('/', categories.getAllCategories);

module.exports = router;

В приведенном выше фрагменте кода, создается объект router из библиотеки Express, а затем используется функция, куда передан метод из подключенного контроллера («categories.controller»).

Для отправки запросов с клиентской части приложения используется библиотека Axios.

Axios -- это Java Script-библиотека для выполнения либо HTTP-запросов в Node.js, либо XMLHttpRequests в браузере. Библиотека поддерживает промисы, а значит и синтаксис ESMASCRIPT 2016. Также Axiosпозволяет автоматически преобразовывать JSON-данные.

Ниже пример использования библиотеки Axios. Для создания GET-запроса на получение категорий товаров, вызывается метод .get(), куда передается маршрут. Далее подписывается потребитель (метод .then()) первым аргументом является функция, выполняющаяся при успешном выполнении. Потребитель «.catch()»будет отлавливать любые пришедшие в запросе ошибки.

//получаем список категорий

export const getCategories = () => (dispatch: Dispatch<AppActionType>) => {

dispatch(sendLoading());

axios

.get('/api/categories')

.then((res) => dispatch(sendCategories(res.data)))

.catch((err) => dispatch(sendErrors(err.response.data)));

};

Визуальное решение

На основе разработанных макетов экранных форм в работе были реализованы экраны интерфейса. Однако разработка визуальных компонентов с нуля достаточно трудозатратна, поэтому было принято решение использовать готовые UI-библиотеки. Всего рассмотрено было четыре популярные библиотеки: ReactBootstrap, MaterialUI, MaterializeCSS, Grommet.

Для разработки была выбрана библиотека Grommet, которая содержит полный набор стилей для элементов управления, визуальных компонентов, более того, поддерживает screenreader и другие инструменты для людей с ограниченными возможностями. Grommet - это библиотека стилизованных Reactкомпонентов, которые можно соединять между собой, образовывая новые и более сложные компоненты.

Для корректной работы с библиотекой Grommet[17]на языке typescript нет необходимости устанавливать дополнительные пакеты. В разрабатываемом сервисе использовалась библиотека компонентов Grommetи библиотека иконок для визуализации Grommet-icons.Grommet-icons позволяет добавлять SVG-изображения в виде компонента.

На рисунке 14 представлен пример реализации функционального компонента с радио-кнопками.

Рис. 14. Реализация визуального компонента из библиотеки grommet

На рисунке 15 представлен реализованный экран «Мои товары». Панель слева всегда остается видимой пользователю. В будущих версиях будет реализовано чтение местоположения пользователя, вкладка «История обмена». Справа расположена лента товаров пользователя. При нажатии на плитку «Добавить», открывается экран редактирования товара. При этом URLв адресной строке меняется: «/user/item/new». В случае же, если пользователь нажимает на плитку с товаром, то открывается страница редактирования или удаления товара, а URLв строке выглядит следующим образом: «/user/item/<идентификатор товара>».

Рис. 15. Экран приложения для свободного обмена одеждой. Экран «Мои товары»

Добавление и редактирование, удаление товаров происходит на одном и том же экране (см. Рисунок 16). Если открывается уже созданный товар, то текущий экран предзаполняется данными товара, а внизу появляются кнопки «Изменить товар» и «Удалить товар» соответственно.

Рис. 16. Экран приложения для свободного обмена одеждой. Экран «Добавить новый товар»

Лента товаров представляет собой «пачку» карточек, на которых изображены товары и дана краткая информация: категория, название, описание товара и теги, которые указал автор публикации (см. Рисунок 17). Перелистывать карточки можно, смахивая их влево или вправо либо воспользоваться кнопками, находящимися внизу. После того, как карточка свайпнута влево или вправо, внизу появляется уведомление о том, что действие завершилось. Когда вся лента закончится, появится карточка-заглушка, чтобы не допускать пустого экрана.

Рис. 17. Экран приложения для свободного обмена одеждой. Экран «Swipe»

Чтобы попасть на экран обмена (см. Рисунок 18), необходимо нажать кнопку «Узнать совпадения». После срабатывания запроса на совпадения, отразится информация. Если совпадений не было найдено, приложение предложит перейти на главную страницу или добавить еще товары - необходимо повысить вероятность совпадения.

На экране «Обмен» показан пользователь, которому понравились предметы гардероба, опубликованные текущим пользователем. В свою очередь, показаны предметы одежды этого пользователя, которые понравились. При нажатии на кнопку «Меняться» появится контактная информация у пользователя. Теперь оба пользователя могут связаться друг с другом. Можно выбрать предложенные предметы и сообщить пользователю об условиях обмена и передачи товара.

Рис. 18. Экран приложения для свободного обмена одеждой. Экран «Обмен»

Кроме проиллюстрированных основных экранов, в приложении также реализовано:

Экран ошибки (страница 404).

Экран редактирования информации о себе.

Экран с краткой инструкцией о пользовании приложением.

Экран регистрации.

Экран входа в приложение.

В заключительной главе выпускной квалификационной работы продемонстрирована настройка и развертывание базы данных на удаленном сервере MongoDBAtlas, реализация моделей данных, показан процесс тестирования запросов к БД. В рамках разработки серверной части приложения, проиллюстрирована схема взаимодействия частей приложения, настроены маршруты, используя фреймворк Express.js, указаны используемые в разработке библиотеки. Также показана схема отправки данных на сервер, процесс управления состоянием клиента, используя Redux. Описаны реализованные формы сервиса и визуальное решение (компоненты, UIбиблиотека). Исходные коды серверной и клиентской части опубликованы на GitHub, ссылки на соответствующие репозитории:

Клиентская часть: URL: https://github.com/plastya-flomaster/swop-frontend/

Серверная часть: URL: https://github.com/plastya-flomaster/swop-backend/

Заключение

Чтобы справиться со снижением спроса и отказа от «перепотребления», мир переходит к переиспользованию и перераспределению ресурсов. Причем, это не идея «утилизации», а скорее исследование способов взаимодействия в экономике и восстановление неких старых традиций (например, передача особо ценных вещей между поколениями). Такая система воспитывает альтруизм и заботу о других, и люди, и бизнес оказываются в плюсе от взаимодействия. Сервисы, позволяющие поддерживать такую философию, положительно встречаются обществом и служат ступенью к новой модели потребления.

Результаты исследования российского рынка показали, что российский потребитель готов использовать digitalпродукты. Было выявлено, что элементы геймификации положительно влияют на пользовательский опыт, который, в свою очередь, все больше экспертов называют конкурентным преимуществом бизнеса [22].

В выпускной квалификационной работе приведен анализ текущей ситуации на рынке и инсайты отрасли шеринг-экономики. На основе анализа установлено, что российский потребитель в целом готов к появлению на рынке сервиса для свободного обмена одеждой.

На следующем шаге были сформированы бизнес-требования и нужды пользователя, опираясь на них, были определены функциональные и нефункциональные требования к приложению. Описание бизнес-процессов обмена одеждой также помогло сформироватьи организовать функциональные требования к приложению, спроектировать правильную навигацию в приложении, реализовать макеты интерфейса.

В заключительной главе работы описан процесс разработки приложения, обоснован выбор функционального подхода в реализации компонентов React, показаны реализованные экраны веб-приложения, описано взаимодействие клиентской и серверной частейи реализация этого взаимодействия, описан процесс работы с данными и состоянием приложения, продемонстрировано тестирование запросов к базе данных.

Реализованный сервис для свободного обмена одеждой решает поставленную задачу, однако обозримо и большое поле для развития и внедрения новых функций. Например, такими функциями могут стать: разные способы регистрации (через социальные сети или google-аккаунт), сортировка и настройка категорий товаров для отображения, реализация чата внутри приложения, помощь в доставке товаров для обмена до пользователей, выход на новые платформы (например, мобильное приложение).

Исходные коды серверной и клиентской частей опубликованы на GitHub, ссылки на соответствующие репозитории:

Клиентская часть: URL: https://github.com/plastya-flomaster/swop-frontend/

Серверная часть: URL: https://github.com/plastya-flomaster/swop-backend/

Литература

1. LVMH будут работать с ЮНЕСКО над защитой биоразнообразия // TheBlueprintmagazine, 2020. URL: https://theblueprint.ru/news/11069(дата обращения: 01.02.2020).

2. Z. Mi, D.M. Coffman. The sharing economy promotes sustainable societies // Nature Communications, vol. 149, pp. 123-130, 2019.

3. Euromonitor Passport: Digital consumer in Russia // Euromonitor international, 2019.

4. Was 2018 The Year Of The Influential Sustainable Consumer? // Nielsen Company. URL: https://www.nielsen.com/us/en/insights/article/2018/was-2018-the-year-of-the-influential-sustainable-consumer/, 2018 (дата обращения: 23.12.2019).

5. Г. И. Абдрахманова, К. О. Вишневский, Л. М. Гохберг и др., науч. ред. Л. М. Гохбергдокл. к XX Апр. междунар. науч. конф. по проблемам развития экономики и общества, Москва, 9-12 апр. 2019 г. Нац. исслед. ун-т «Высшая школа экономики», 2019.

6. V. B. Betelin, Challenges and Opportunities in Forming a Digital Economy in Russia // Scientific Research Institute of System Analysis, Russian Academy of Sciences, 2019.

7. L. Loussaпef, I. Ulrich, C. Damay. How does access to luxury fashion challenge self-identity? Exploring women's practices of joint and non-ownership // Universitйd'Angers, Journal of Business Research, vol. 102, pp. 263-272, 2019.

8. K. Gera, P. Hasdell. Gamified Sharing Economy The Role of Game // School of Design, The Hong Kong Polytechnic University, Hung Hom, Kowloon, Hong Kong, 2020.

9. Tingyi S. Lin. Conceptualizing a Sharing Economy Service Through an Information Design Approach Design Department // National Taiwan University of Science and Technology, International Conference on Human Factors in Artificial Intelligence and Social Computing, Washington D.C., USA, July 24-28, pp.329-335, 2019.

10. POINTFOR SERVICES LTD // The Open Database Of The Corporate World, 2020. URL: https://opencorporates.com/companies/cy/HE393603 (датаобращения: 23.02.2020).

11. Best JavaScript framework for frontend // RubyGarage magazine, 2020. URL: https://rubygarage.org/blog/best-javascript-frameworks-for-front-end (дата обращения: 03.03.2020).

12. State of JS // Data aggregation of popularity JavaScript technologies, https://stateofjs.com/(датаобращения: 23.02.2020).

13. Db-EnginesRanking chart // Knowledge Base of Relational and NoSQL Database Management Systems. URL: https://db-engines.com/en/ranking (датаобращения: 02.03.2020).

14. Баланс в веб-дизайне или как должен выглядеть продающий сайт // ThePoltor, 2018. URL: https://poltor.com/stati/balance.html (дата обращения: 04.02.2020).

15. Стрибли М. Элементы и принципы дизайна // Mediumblog, 2019. URL: https://medium.com/основы-визуального-дизайна/элементы-и-принципы-дизайна-c6d47be46b2c (дата обращения: 07.01. 2020).

16. Документация Express.js // Opensource community. URL: https://github.com/expressjs/express, 2020 (дата обращения: 13.05.2020).

17. Библиотека визуальных компонентов Grommet // Grommetinc. URL: https://v2.grommet.io/, 2020 (дата обращения: 15.05.2020).

18. J. Holtstrцm, C. Bjellerup and J. Eriksson. Business model development for sustainable apparel consumption The case of Houdini Sportswear // Linkцping University, Journal of Strategy and Management, vol. 12 No. 4, pp. 481-504, 2019.

19. Реляционные базы данных // AmazonCloudBlog. URL: https://aws.amazon.com/ru/relational-database/ , 2018 (дата обращения: 02.05.2020).

20. A New Textiles Economy: Redesigning Fashion's Future // Ellen Macarthur Foundation. URL: https://www.ellenmacarthurfoundation.org/assets/downloads/publications/A-New-Textiles-Economy_Full-Report.pdf (дата обращения: 21.02.2020).

21. B.J. Corbitt, T. Thanasankit. Trust and e-commerce: a study of consumer perceptions // Electronic commerce research and applications, vol. 2, pp. 203-215, 2003.

22. S. Deterding, D. Dixon, R. Khaled, L. NackeFrom game design elements to gamefulness: defining “gamification // Proceedings of the 15th International Academic MindTrek Conference: Envisioning Future Media Environments, Amsterdam, The Netherlands, September 12-16, 2011, pp. 111-143.

23. S. Sahai, R. Goel, A. Venaik, V. Garg. Impact of Digital Commerce on Fashion Industry to Gain Customer Loyalty // International Journal of Engineering and Advanced Technology (IJEAT), Vol. 8, 2019.

24. Xun Xu. How Do Consumers in the Sharing Economy Value Sharing? Evidence from Online Reviews // College of Business Administration, California State University, Stanislaus, Decision Support Systems, vol. 108, pp. 456-469, 2020.

Приложение А

Рис. 19

Рис. 20

Приложение Б

Рис. 21

Приложение В

Рис. 22

Приложение Г

Описание моделей данных в виде MongooseSchema

СхемаUser

const mongoose = require('mongoose');

...

Подобные документы

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.