Please use this identifier to cite or link to this item: https://er.chdtu.edu.ua/handle/ChSTU/8018
Title: Оптимізація продуктивності роботи з базою даних високонавантаженого додатку на прикладі інтернет-магазину техніки
Authors: Півень, Олександр Борисович
Чоповенко, Василь Миколайович
Keywords: пагінація;індекси;кешування;spring boot;postgresql;оптимізація;продуктивність;серверна частина;великі дані;pagination;indexes;caching;spring boot;postgresql;optimization;performance;backend;big data
Issue Date: 9-Dec-2024
Abstract: АНОТАЦІЯ Чоповенко Василь Миколайович Оптимізація продуктивності роботи з базою даних високонавантаженого додатку на прикладі інтернет-магазину техніки 121 - Інженерія програмного забезпечення Черкаський державний технологічний університет Черкаси, 2024 Сучасні веб-додатки вимагають обробки великих обсягів даних із максимальною продуктивністю. Зростання обсягів інформації у базах даних висуває нові вимоги до оптимізації серверної частини, зокрема, через ефективну організацію комунікації з базою даних. Актуальність теми спричинена популярністю онлайн платформ для продажу речей і великою кількістю користувачів, як що накладає додаткове навантаження на сервер Мета дослідження Метою роботи є аналіз впливу використання пагінації, індексів, Fetch стратегій та кешування на продуктивність серверної частини веб-додатків на прикладі Spring Boot. Дослідження включає порівняння часу обробки запитів із пагінацією та без неї, а також оцінку впливу індексів та інших перерахованих раніше технік на швидкодію операцій читання та запису Методологія дослідження Проведено серію експериментів із двома контролерами: один обробляє запити з пагінацією, інший — без неї. Експерименти виконано на різних наборах даних (від 10 до 50 000 записів), із додаванням та без додавання індексів у базі даних. Аналіз здійснено за допомогою інструментів PostgreSQL та Spring Boot із використанням бібліотек для тестування продуктивності. Результати дослідження При малих обсягах даних використання пагінації не суттєво впливає на продуктивність, однак із збільшенням записів у базі даних середній час відповіді зменшується в кілька разів завдяки розбиттю результатів на сторінки. Додавання індексів суттєво покращує швидкість виконання запитів читання, зменшуючи час відповіді у 2–3 рази для великих таблиць. Проте спостерігається збільшення часу на операції вставки даних, що необхідно враховувати при проектуванні. Використання індексів є ефективним лише за умови їх правильної оптимізації для часто використовуваних операцій, таких як фільтрація або сортування. Правильний підбір Fetch стратегій допомагає оптимізувати час обробки запитів для повʼязаних записів, а також організувати використання памʼяті у додатку. Кешування дозволяє пришвидшити виконання запиту до сервера шляхом уникнення додаткового запиту до бази даних. Правильна інвалідація кешу дозволяє досягти максимальної точності даних, які відображаються користувачеві. Висновки Запровадження описаних технік є ключовими факторами для оптимізації продуктивності серверної частини веб-додатків. Робота демонструє, що ці підходи значно знижують навантаження на сервер і забезпечують стабільну роботу системи, особливо при роботі з великими наборами даних. Правильний підбір технік та їх конфігурація дозволяє досягти значних покращень роботи системи з точки зору стабільності та швидкодії, за рахунок чого кінцеві користувачі отримують максимальний досвід використання додатку, що робить його більш конкурентно спроможним на фоні інших додатків, які можуть мати такий самий набір функціоналу, але програють за рахунок нестабільного і довгого часу відповіді.
ANNOTATION Chopovenko Vasyl Mykolayovych Optimization of Database Performance for a High-Load Application: A Case Study of an Electronics Online Store 121 - Software engineering Cherkasy State Technical University Cherkasy, 2024 Modern web applications require processing large volumes of data with maximum performance. The increasing amount of information in databases imposes new demands on backend optimization, particularly through efficient database communication management. The relevance of this topic is driven by the popularity of online sales platforms and a large number of users, which adds extra load on the server. Research Objective The aim of this work is to analyze the impact of pagination, indexing, fetch strategies, and caching on the backend performance of web applications using Spring Boot as an example. The research includes a comparison of query processing times with and without pagination, as well as an evaluation of how indexing and the aforementioned techniques affect the speed of read and write operations. Research Methodology A series of experiments were conducted using two controllers: one handling requests with pagination and the other without it. The experiments were performed on different datasets (ranging from 10 to 50,000 records), with and without indexes added to the database. The analysis was carried out using PostgreSQL and Spring Boot tools, utilizing libraries for performance testing. Research Results With small data volumes, using pagination does not significantly affect performance. However, as the number of records in the database increases, the average response time decreases several times due to result splitting into pages. Adding indexes significantly improves query execution speed, reducing response time by 2–3 times for large tables. However, an increase in data insertion time was observed, which must be considered during system design. The use of indexes is effective only when properly optimized for frequently used operations, such as filtering or sorting. Choosing the right fetch strategies helps optimize query processing time for related records and efficiently manage application memory usage. Caching speeds up query execution by avoiding additional database requests. Proper cache invalidation ensures maximum data accuracy displayed to users. Conclusions The implementation of these techniques is essential for backend performance optimization in web applications. The study demonstrates that these approaches significantly reduce server load and ensure stable system operation, especially when working with large datasets. Correctly selecting and configuring these techniques leads to significant system performance improvements in terms of stability and speed. As a result, end users receive a better application experience, making the application more competitive compared to others with similar functionality but slower and less stable response times.
URI: https://er.chdtu.edu.ua/handle/ChSTU/8018
Appears in Collections:121 Інженерія програмного забезпечення (Інженерія програмного забезпечення)



Items in DSpace are protected by copyright, with all rights reserved, unless otherwise indicated.

Extracted text
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
ЧЕРКАСЬКИЙ ДЕРЖАВНИЙ ТЕХНОЛОГІЧНИЙ УНІВЕРСИТЕТ
Факультет інформаційних технологій і систем
Кафедра програмного забезпечення автоматизованих систем
ПОЯСНЮВАЛЬНА ЗАПИСКА
до кваліфікаційної роботи
«магістра»
освітній рівень
на тему: Оптимізація продуктивності роботи з базою даних
високонавантаженого додатку на прикладі інтернет-магазину
техніки
Виконав: магістрант 2 курсу, групи МПЗ-2304
Спеціальності
121 «Інженерія програмного забезпечення»
(шифр і назва напряму підготовки)
Магістрант Чоповенко В.М.
(прізвище та ініціали)
Керівник Півень О. Б.
(прізвище та ініціали)
Рецензент Радзіна Ж. С.
(прізвище та ініціали)
Черкаси 2024
Черкаський державний технологічний університет
повне найменування вищого навчального закладу
Факультет інформаційних технологій і систем
Кафедра програмного забезпечення автоматизованих систем
Освітній рівень магістр
Спеціальність 121 «Інженерія програмного забезпечення»
Освітня програма Інженерія програмного забезпечення
ЗАТВЕРДЖУЮ
Зав. кафедри ПЗАС, професор
___________ Голуб С. В.
«___» _______________ 2024 року
ЗАВДАННЯ
НА КВАЛІФІКАЦІЙНУ РОБОТУ МАГІСТРАНТА
Чоповенко Василь Миколайович
(прізвище, ім’я, по батькові)
1. Тему проекту (роботи) Оптимізація продуктивності роботи з базою даних
високонавантаженого додатку на прикладі інтернет-магазину техніки
Керівник проекту (роботи) Півень Олександр Борисович к. ф.-м. н, доцент
(прізвище, ім’я , по батькові, науковий ступінь, вчене звання)
Затверджені наказом Черкаського державного технологічного університету від « 7 » жовтня
2024 року №299/04
2. Строк подання магістрантом проекту (роботи) 9 грудня 2024 р.
3. Вхідні дані до проекту (роботи) стандарти програмного забезпечення; процеси управління;
вимоги до проекту; календарне планування проекту; управління ресурсами
4. Зміст розрахунково-пояснювальної записки (перелік питань, які потрібно розробити)
Вступ; Існуючі методи та засоби розвʼязання поставлених завдань; Теоретичні та
експериментальні дослідження; Впровадження результатів досліджень у практику
проектування програмного забезпечення інформаційних систем; Розробка та тестування
програмного забезпечення; Висновок; Список використаних джерел;
Додаток А. Специфікація, Додаток Б. Лістинг програми, Додаток В. Інструкція користувача,
Додаток Г. Графічні матеріали
5. Перелік графічного матеріалу (з точним зазначенням обов’язкових робіт проекту);
Додаток Г. Слайд 1 - Титульний слайд, Слайд 2 - Вступ, Слайд 3 - Формалізація завдання,
Слайд 4 - Гіпотеза, Слайд 5 - Проведені експерименти, Слайд 6 - Проведені експерименти -
продовження, Слайд 7 - Результати експериментів, Слайд 8 - Результати експериментів -
продовження, Слайд 9 - Діаграма прецедентів, Слайд 10 - Проектування логічної структури
(діаграма класів), Слайд 11 - Проектування логічної структури (діаграма класів -
продовження), Слайд 12 - Проектування логічної структури (діаграма пакетів), Слайд 13 -
Архітектурне проектування (діаграма компонентів), Слайд 14 - Архітектурне проектування
(діаграма розгортання), Слайд 15 - Моделювання поведінки системи (діаграма діяльності),
Слайд 16 - Моделювання поведінки системи (діаграма послідовності), Слайд 17 -
Моделювання поведінки системи (діаграма послідовності - продовження), Слайд 18 -
Моделювання поведінки системи (діаграма комунікації), Слайд 19 - Моделювання поведінки
системи (діаграма скінченного автомату), Слайд 20 - Вибір засобів реалізації, Слайд 21 -
Функціональна схема системи, Слайд 22 - Логічна схема системи, Слайд 23 - Інтерфейс
користувача, Слайд 24 - Тестування, Слайд 25 - Дякую за увагу
6. Консультанти розділів проекту (роботи)
Прізвище, ініціали та посади Підпис, дата
Розділ
консультанта Завдання видав Завдання прийняв
1
2
7. Дата видачі завдання 3 вересня 2024 р.
КАЛЕНДАРНИЙ ПЛАН
Строк виконання
№
Назва етапів випускної роботи етапів випускної Примітки
п/п
роботи
1 Постановка задачі 04.09.2024 виконано
2 Підготовка завдання 07.09.2024 виконано
3 Погодження завдання 09.09.2024 виконано
4 Затвердження завдання 15.09.2024 виконано
Основна стадія 16.09.2024
1 Підбір матеріалів 17.09.2024 виконано
2 Аналіз шляхів вирішення поставленої задачі 23.09.2024 виконано
3 Розрахунок основних параметрів роботи 30.09.2024 виконано
4 Вибір кінцевого варіанту проектного рішення 05.10.2024 виконано
5 Оформлення первісної редакції роботи 09.11.2024 виконано
Заключна стадія 12.11.2024
1 Узгодження прийнятих проектних рішень з 13.11.2024 виконано
керівником
2 Оформлення пояснювальної записки роботи в 20.11.2024 виконано
кінцевій редакції
3 Попередній захист роботи 25.11.2024 виконано
4 Затвердження роботи 29.11.2024 виконано
5 Рецензування роботи 30.11.2024 виконано
6 Захист роботи 12.12.2024
Магістрант _____________________ Чоповенко В.М.
(підпис) (прізвище та ініціали)
Керівник проекту (роботи) _____________________ Півень О. Б.
(підпис) (прізвище та ініціали)
АНОТАЦІЯ
Чоповенко Василь Миколайович
Оптимізація продуктивності роботи з базою даних високонавантаженого
додатку на прикладі інтернет-магазину техніки
121 - Інженерія програмного забезпечення
Черкаський державний технологічний університет
Черкаси, 2024
Сучасні веб-додатки вимагають обробки великих обсягів даних із
максимальною продуктивністю. Зростання обсягів інформації у базах даних
висуває нові вимоги до оптимізації серверної частини, зокрема, через
ефективну організацію комунікації з базою даних. Актуальність теми
спричинена популярністю онлайн платформ для продажу речей і великою
кількістю користувачів, як що накладає додаткове навантаження на сервер
Мета дослідження
Метою роботи є аналіз впливу використання пагінації, індексів, Fetch
стратегій та кешування на продуктивність серверної частини веб-додатків на
прикладі Spring Boot. Дослідження включає порівняння часу обробки запитів із
пагінацією та без неї, а також оцінку впливу індексів та інших перерахованих
раніше технік на швидкодію операцій читання та запису
Методологія дослідження
Проведено серію експериментів із двома контролерами: один обробляє
запити з пагінацією, інший — без неї. Експерименти виконано на різних
наборах даних (від 10 до 50 000 записів), із додаванням та без додавання
індексів у базі даних. Аналіз здійснено за допомогою інструментів PostgreSQL
та Spring Boot із використанням бібліотек для тестування продуктивності.
Результати дослідження
При малих обсягах даних використання пагінації не суттєво впливає на
продуктивність, однак із збільшенням записів у базі даних середній час
відповіді зменшується в кілька разів завдяки розбиттю результатів на сторінки.
Додавання індексів суттєво покращує швидкість виконання запитів
читання, зменшуючи час відповіді у 2–3 рази для великих таблиць. Проте
спостерігається збільшення часу на операції вставки даних, що необхідно
враховувати при проектуванні.
Використання індексів є ефективним лише за умови їх правильної
оптимізації для часто використовуваних операцій, таких як фільтрація або
сортування.
Правильний підбір Fetch стратегій допомагає оптимізувати час обробки
запитів для повʼязаних записів, а також організувати використання памʼяті у
додатку.
Кешування дозволяє пришвидшити виконання запиту до сервера шляхом
уникнення додаткового запиту до бази даних. Правильна інвалідація кешу
дозволяє досягти максимальної точності даних, які відображаються
користувачеві.
Висновки
Запровадження описаних технік є ключовими факторами для оптимізації
продуктивності серверної частини веб-додатків. Робота демонструє, що ці
підходи значно знижують навантаження на сервер і забезпечують стабільну
роботу системи, особливо при роботі з великими наборами даних.
Правильний підбір технік та їх конфігурація дозволяє досягти значних
покращень роботи системи з точки зору стабільності та швидкодії, за рахунок
чого кінцеві користувачі отримують максимальний досвід використання
додатку, що робить його більш конкурентно спроможним на фоні інших
додатків, які можуть мати такий самий набір функціоналу, але програють за
рахунок нестабільного і довгого часу відповіді.
Ключові слова: пагінація, індекси, кешування, spring boot, postgresql,
оптимізація, продуктивність, серверна частина, великі дані.
ANNOTATION
Chopovenko Vasyl Mykolayovych
Optimization of Database Performance for a High-Load Application: A Case
Study of an Electronics Online Store
121 - Software engineering
Cherkasy State Technical University
Cherkasy, 2024
Modern web applications require processing large volumes of data with
maximum performance. The increasing amount of information in databases imposes
new demands on backend optimization, particularly through efficient database
communication management. The relevance of this topic is driven by the popularity
of online sales platforms and a large number of users, which adds extra load on the
server.
Research Objective
The aim of this work is to analyze the impact of pagination, indexing, fetch strategies,
and caching on the backend performance of web applications using Spring Boot as an
example. The research includes a comparison of query processing times with and
without pagination, as well as an evaluation of how indexing and the aforementioned
techniques affect the speed of read and write operations.
Research Methodology
A series of experiments were conducted using two controllers: one handling requests
with pagination and the other without it. The experiments were performed on
different datasets (ranging from 10 to 50,000 records), with and without indexes
added to the database. The analysis was carried out using PostgreSQL and Spring
Boot tools, utilizing libraries for performance testing.
Research Results
With small data volumes, using pagination does not significantly affect
performance. However, as the number of records in the database increases, the
average response time decreases several times due to result splitting into pages.
Adding indexes significantly improves query execution speed, reducing
response time by 2–3 times for large tables. However, an increase in data insertion
time was observed, which must be considered during system design.
The use of indexes is effective only when properly optimized for frequently
used operations, such as filtering or sorting.
Choosing the right fetch strategies helps optimize query processing time for
related records and efficiently manage application memory usage.
Caching speeds up query execution by avoiding additional database requests.
Proper cache invalidation ensures maximum data accuracy displayed to users.
Conclusions
The implementation of these techniques is essential for backend performance
optimization in web applications. The study demonstrates that these approaches
significantly reduce server load and ensure stable system operation, especially when
working with large datasets.
Correctly selecting and configuring these techniques leads to significant system
performance improvements in terms of stability and speed. As a result, end users
receive a better application experience, making the application more competitive
compared to others with similar functionality but slower and less stable response
times.
Keywords: pagination, indexes, caching, spring boot, postgresql, optimization,
performance, backend, big data.
ЗМІСТ
ПЕРЕЛІК УМОВНИХ ПОЗНАЧЕНЬ....................................................................5
ВСТУП........................................................................................................................ 6
РОЗДІЛ 1 ІСНУЮЧІ МЕТОДИ ТА ЗАСОБИ РОЗВ’ЯЗАННЯ
ПОСТАВЛЕНИХ ЗАВДАНЬ................................................................................. 11
1.1 Актуальні проблеми, що виникають в процесі виконання завдань...........11
1.2 Методи та засоби, які вже використовуються для усунення проблем та
виконання завдань................................................................................................ 11
1.3 Пошук та опис аналогів.................................................................................12
1.4 Конкретизація завдань роботи...................................................................... 16
РОЗДІЛ 2 ТЕОРЕТИЧНІ ТА ЕКСПЕРИМЕНТАЛЬНІ ДОСЛІДЖЕННЯ.. 20
2.1 Теоретичні дослідження................................................................................24
2.2 Експериментальні дослідження....................................................................26
РОЗДІЛ 3 ВПРОВАДЖЕННЯ РЕЗУЛЬТАТІВ ДОСЛІДЖЕНЬ У
ПРАКТИКУ ПРОЕКТУВАННЯ ПРОГРАМНОГО ЗАБЕЗПЕЧЕННЯ
ІНФОРМАЦІЙНИХ СИСТЕМ.............................................................................48
3.1 Моделювання предметної області................................................................ 48
3.1.1 Предметна область моделювання. Модель предметної області.
Словник предметної області.......................................................................... 48
3.1.2 Елементи моделювання предметної області....................................... 49
3.1.3 Робоча область моделювання................................................................50
3.2 Формування та аналіз вимог......................................................................... 50
3.2.1 Формування вимог до програмного забезпечення..............................51
3.2.2 Формування вимог за допомогою діаграми прецедентів...................54
3.2.3 Проектування логічної структури програмного комплексу...............57
3.2.3.1 Діаграми класів.............................................................................. 57
3.2.3.2 Діаграми пакетів............................................................................ 58
3.2.4 Архітектурне проектування.................................................................. 59
3.2.4.1 Діаграма компонентів....................................................................59
3.2.4.2 Розгортання програмної системи на апаратних засобах.
Діаграма розгортання................................................................................ 60
ЧДТУ 242322.009 ПЗ
Зм Лист № документа Підпис Дата
Розроб. Чоповенко. В. М. Оптимізація продуктивності Літ. Лист Листів.
Керівник Півень О. Б. роботи з базою даних [н] 3
високонавантаженого
Н. контр. Півень О. Б. додатку на прикладі ФІТІС,
Затверд. Голуб С. В. інтернет-магазину техніки Кафедра ПЗАС, МПЗ-2304
3.2.5 Моделювання поведінки системи...................................................... 61
3.2.5.1 Діаграма діяльності.....................................................................61
3.2.5.2 Діаграма послідовності...............................................................71
3.2.5.3 Діаграма комунікації................................................................... 80
3.2.5.4 Діаграма скінченного автомату.................................................. 81
РОЗДІЛ 4 РОЗРОБКА ТА ТЕСТУВАННЯ ПРОГРАМНОГО
ЗАБЕЗПЕЧЕННЯ................................................................................................. 83
4.1 Розробка програмного комплексу.............................................................. 83
4.1.1 Обґрунтування вибору засобів реалізації..........................................83
4.1.2 Опис структурної (функціональної) схеми....................................... 86
4.1.3 Опис логічної схеми системи............................................................. 87
4.1.4 Розробка бази даних............................................................................ 89
4.1.5 Розробка інтерфейсу користувача...................................................... 93
4.1.6 Опис розробки програмних компонентів..........................................93
4.2 Тестування системи..................................................................................... 95
4.2.1 Модульне тестування.......................................................................... 95
4.2.2 Інтеграційне тестування......................................................................96
4.2.3 Системне тестування...........................................................................96
4.2.4 Приймальне тестування.................................................................... 100
4.2.5 Реалізація та впровадження результатів..........................................103
ЗАГАЛЬНІ ВИСНОВКИ................................................................................... 107
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ........................................................ 108
ДОДАТОК А......................................................................................................... 112
ДОДАТОК Б......................................................................................................... 114
ДОДАТОК В......................................................................................................... 139
ДОДАТОК Г......................................................................................................... 147
ЧДТУ 242322.009 ПЗ
ЧДТУ 242322.009 ПЗ
ПЕРЕЛІК УМОВНИХ ПОЗНАЧЕНЬ
Абревіатура Пояснення
API Application Programming Interface
AWS Amazon Web Services
DTO Data Transfer Object
ECS Elastic Container Service
HTTP HyperText Transfer Protocol
JAR Java archive
JSON JavaScript Object Notation
JVM Java Virtual Machine
ORM Object-relational mapping
REST Representational State Transfer
SQL Structured Query Language
UML Unified Modeling Language
URL Uniform Resource Locator
БД База даних
ПЗ Програмне забезпечення
СУБД Система управління базою даних
5
ЧДТУ 242322.009 ПЗ
ВСТУП
Актуальність теми. В області інженерії програмного забезпечення є
поширеним явище зростання обсягів даних, які ставлять нові вимоги до
продуктивності веб-додатків. Інтернет-магазини, які щодня обробляють тисячі
запитів, потребують стабільної роботи та швидкого часу відповіді навіть при
високих навантаженнях. Оптимізація серверної частини стає критично
важливою для забезпечення конкурентоспроможності бізнесу, адже час
завантаження сторінок та стабільність сервісу безпосередньо впливають на
задоволеність користувачів.
Особливо важливим є ефективне управління великими обсягами даних,
що зберігаються у базах. Веб-додатки часто стикаються з проблемами довгого
часу виконання запитів, нераціонального використання ресурсів пам’яті та
зниження швидкодії під час пікових навантажень. Впровадження методів
оптимізації, таких як пагінація, індексація баз даних, застосування стратегій
вибірки даних (Fetch), а також кешування, дозволяє значно зменшити затримки
у роботі додатків. Ці підходи допомагають знижувати навантаження на сервер,
ефективно використовувати обчислювальні ресурси та покращувати
продуктивність системи навіть за наявності великих наборів даних.
Вивчення впливу цих методів у реальних сценаріях дозволяє оцінити їх
ефективність і знайти оптимальні підходи до організації запитів та управління
даними. Дослідження таких аспектів, як вплив пагінації на швидкість запитів,
користь від використання індексів для операцій читання, оптимізація запитів до
пов’язаних таблиць і застосування кешування для прискорення відповіді
сервера, допомагає зрозуміти, як досягти стабільності й високої продуктивності
веб-додатків.
Результати цих досліджень є особливо цінними для компаній, які
прагнуть створити високопродуктивні системи з можливістю масштабування,
забезпечуючи найкращий користувацький досвід.
Зв’язок з науковими програмами, планами, темами. Дослідження теми
оптимізації продуктивності високонавантажених додатків безпосередньо
6
ЧДТУ 242322.009 ПЗ
пов'язане з сучасними науковими програмами в сфері інформаційних
технологій. В рамках цих програм акцентується увага на розробці ефективних
рішень для обробки великих обсягів даних, вдосконаленні методів роботи з
базами даних та підвищенні продуктивності програмного забезпечення. Тема
дослідження відповідає стратегічним планам розвитку IT-індустрії, які
передбачають створення інноваційних технологій для підвищення ефективності
бізнес-процесів у сфері електронної комерції. Дослідження також базується на
актуальних наукових темах, присвячених оптимізації баз даних, методам
кешування та індексації, які активно обговорюються в наукових колах і
професійних конференціях.
Мета і завдання дослідження. Оптимізація процесу комунікації
високонавантаженого додатку з базою даних для забезпечення ефективної та
стабільної системи обробки даних інтернет-магазину техніки. Основна увага
буде приділена вдосконаленню взаємодії серверної частини з базою даних,
зниженню часу відповіді на запити та забезпеченню оптимальної роботи
системи за умов інтенсивного навантаження.
Пункти дослідження
1 Аналіз і впровадження пагінації
– Оцінка впливу пагінації на продуктивність серверної частини.
– Розробка пагінації для сторінок із великою кількістю записів
(списки товарів, історія замовлень).
– Визначення оптимального розміру сторінки для різних типів даних
і запитів.
2 Використання кешування
– Впровадження кешування для зменшення кількості запитів до бази
даних.
– Оцінка зниження навантаження на інфраструктуру.
– Аналіз покращення часу відповіді для часто запитуваних даних.
3 Оптимізація SQL-запитів
– Аналіз запитів на кшталт SELECT COUNT для визначення їхнього
7
ЧДТУ 242322.009 ПЗ
впливу на продуктивність.
– Розробка оптимізованих запитів для підрахунку кількості записів у
таблицях.
4 Застосування Fetch стратегій
– Впровадження стратегій Fetch для ефективного завантаження
пов’язаних даних.
– Уникнення надмірної кількості запитів до бази даних (проблеми
N+1 запитів).
– Мінімізація передачі зайвих даних та покращення обробки
складних запитів.
5 Впровадження індексів
– Аналіз таблиць із великим обсягом даних для визначення
необхідності індексів.
– Розробка індексів для забезпечення швидкого пошуку та фільтрації
записів.
– Балансування продуктивності між операціями читання, вставки та
оновлення даних.
6 Комплексна оцінка продуктивності системи
– Вимірювання впливу кожного впровадженого методу на час
відповіді серверної частини.
– Оцінка стабільності роботи системи за умов зростаючого
навантаження.
– Розробка рекомендацій щодо впровадження оптимізацій для інших
високонавантажених систем.
Очікувані результати
Ці дослідження разом забезпечать створення продуктивної та
масштабованої серверної частини, здатної підтримувати безперебійну роботу
інтернет-магазину навіть за умов інтенсивного навантаження.
Об’єкт дослідження. Процес взаємодії серверної частини
високонавантаженого веб-додатку на основі Spring Boot з базою даних
8
ЧДТУ 242322.009 ПЗ
(PostgreSQL) під час обробки запитів.
Предмет дослідження. Методи та інструменти оптимізації
продуктивності обробки запитів до бази даних, зокрема, використання пагінації,
індексів, кешування, Fetch стратегій та оптимізація SQL-запитів.
Методи дослідження.
1 Аналітичні методи: огляд літератури та аналіз існуючих підходів до
оптимізації продуктивності серверних частин інтернет-магазинів,
зокрема в контексті роботи з базами даних та високими
навантаженнями.
2 Експериментальні методи: розробка та тестування рішень для
оптимізації пагінації, кешування, запитів, Fetch стратегій та індексів, а
також проведення експериментів для оцінки їхнього впливу на
швидкість відповіді та навантаження на сервер.
3 Статистичні методи: аналіз результатів експериментів з метою
оцінки покращення продуктивності, включаючи час виконання
запитів, частоту звернень до бази даних та ефективність кешування.
4 Комп'ютерне моделювання: моделювання взаємодії серверної
частини з базою даних для визначення найбільш ефективних стратегій
роботи при високому навантаженні, зокрема в умовах великої
кількості одночасних запитів.
5 Системний аналіз: вивчення та аналіз архітектури серверної частини
інтернет-магазину з метою інтеграції оптимізаційних рішень для
забезпечення стабільної роботи системи за умов високих навантажень.
Наукова новизна отриманих результатів: вперше запропоновано метод
оптимізації процесу взаємодії Spring Boot додатків із базами даних, який
полягає у використанні індексації, кешування, пагінації та оптимізації запитів із
сучасними фреймворками та інструментами і, на відміну від існуючих,
забезпечують інтеграцію цих технік у веб-додатках, що дозволяє підвищити
продуктивність і масштабованість серверних веб-додатків.
Отримали подальший розвиток впливу індексів, кешування та пагінації на
9
ЧДТУ 242322.009 ПЗ
продуктивність веб-додатків шляхом проведення експериментальних
вимірювань у реальних умовах високонавантажених систем.
Практичне значення отриманих результатів. Результати дослідження
можуть бути безпосередньо застосовані у розробці високонавантажених
веб-додатків, зокрема інтернет-магазинів, для підвищення їх продуктивності та
масштабованості. Запропонований підхід дозволяє:
– оптимізувати роботу з базою даних за допомогою ефективного
налаштування Fetch стратегій, Batch Fetching, індексів та пагінації, що
зменшує час обробки запитів;
– знизити витрати ресурсів сервера, використовуючи налаштоване
кешування на рівні застосунку або бази даних, що сприяє зменшенню
затримок під час обробки даних;
– покращити користувацький досвід шляхом підвищення швидкодії
веб-додатків у реальних умовах.
Результати можуть бути корисними для компаній, які використовують
Spring Boot, Hibernate ORM та PostgreSQL у своїх проєктах, а також для
розробників, які прагнуть забезпечити стабільну роботу додатків під високими
навантаженнями. Крім того, підхід може бути використаний для навчальних
цілей у курсах із розробки програмного забезпечення та баз даних.
Особистий внесок автора. Всі відображені в роботі результати
представлені автором особисто.
Особистий внесок автора полягає в розробці програмного продукту на
основі якого досліджено різні методики оптимізації цього ж додатку та часу
обробки запитів, відправлених багатьма користувачами одночасно.
10
ЧДТУ 242322.009 ПЗ
РОЗДІЛ 1 ІСНУЮЧІ МЕТОДИ ТА ЗАСОБИ РОЗВ’ЯЗАННЯ
ПОСТАВЛЕНИХ ЗАВДАНЬ
1.1 Актуальні проблеми, що виникають в процесі виконання завдань
В процесі виконання завдань, пов'язаних з оптимізацією продуктивності
високонавантажених додатків, виникає кілька актуальних проблем, які можуть
значно ускладнити досягнення цілей дослідження.
Великий обсяг даних. Сучасні інтернет-магазини обробляють величезні
обсяги даних, що створює труднощі у швидкій та ефективній обробці запитів.
Проблеми з масштабуванням бази даних і необхідність оптимізації зберігання
даних вимагають особливої уваги [1].
Складність SQL-запитів. Неоптимізовані SQL-запити можуть суттєво
уповільнити роботу додатку. Виявлення і виправлення вузьких місць у запитах
вимагає глибокого аналізу та знань про механізми виконання запитів бази даних
[2].
Кешування даних. Хоча кешування є потужним інструментом для
підвищення продуктивності, його реалізація може бути складною. Неправильна
конфігурація кешу може призвести до застарілих або некоректних даних, що
негативно вплине на користувацький досвід [2].
Управління транзакціями. Оптимізація продуктивності часто
супроводжується необхідністю управління транзакціями, що вимагає
ретельного підходу до проектування архітектури бази даних, щоб уникнути
проблем з цілісністю даних [3].
Тестування продуктивності. Вимірювання та оцінка продуктивності є
важливими етапами, але реалізація адекватних тестів, які відображають реальні
умови навантаження, може бути складною задачею [4].
1.2 Методи та засоби, які вже використовуються для усунення
проблем та виконання завдань
Оптимізація SQL-запитів: Використання методів аналізу виконання
11
ЧДТУ 242322.009 ПЗ
запитів (EXPLAIN) для виявлення вузьких місць. Оптимізація запитів, зокрема,
шляхом уникнення вкладених запитів та використання JOIN замість підзапитів,
дозволяє знизити час їх виконання [5].
Індексація: Створення індексів на частих колонках запитів суттєво
прискорює доступ до даних. Правильна стратегія індексації, включаючи
використання складених індексів, дозволяє покращити продуктивність без
значного збільшення витрат на запис [6].
Кешування: Застосування механізмів кешування, таких як Redis[7] або
Memcached, для зберігання часто використовуваних даних у пам’яті. Це
дозволяє значно зменшити навантаження на базу даних і скоротити час
відповіді [8].
Масштабування бази даних: Реалізація горизонтального або
вертикального масштабування бази даних для підвищення її продуктивності. Це
включає в себе розподіл даних між кількома серверами (шардинг) або
підвищення ресурсів одного сервера [3].
Моніторинг і профілювання: Використання інструментів моніторингу,
таких як New Relic або Prometheus, для відстеження продуктивності додатку та
ідентифікації потенційних проблем. Профілювання коду та запитів дозволяє
своєчасно виявляти і усувати проблеми [9].
Тестування продуктивності: Проведення регулярних тестів навантаження
та стрес-тестування для оцінки продуктивності системи під різними умовами,
що дозволяє вчасно виявити проблеми з продуктивністю [10].
Рефакторинг коду: Аналіз та оптимізація існуючого коду, щоб зменшити
його складність і підвищити ефективність виконання. Це може включати
поліпшення алгоритмів або впровадження нових архітектурних рішень [11].
1.3 Пошук та опис аналогів
– Опис: Apache Cassandra (Рис. 1.1) — це потужна розподілена NoSQL
база даних, розроблена для обробки великих обсягів даних з високою
доступністю і масштабованістю. Вона здатна працювати в умовах
12
ЧДТУ 242322.009 ПЗ
великої кількості записів, забезпечуючи швидке виконання операцій
запису і читання на масштабованому рівні [12].
– Плюси:
– Висока масштабованість для обробки великих обсягів даних.
– Висока доступність завдяки розподіленій архітектурі.
– Мінуси:
– Складність налаштування і управління кластером.
– Необхідність ретельного проектування архітектури для досягнення
оптимальних результатів.
Рис. 1.1 - Apache Cassandra [12]
– Опис: MongoDB (Рис. 1.2) — це документно-орієнтована NoSQL база
даних, яка забезпечує гнучкість в зберіганні даних у форматі BSON
(бінарний JSON). Вона дозволяє легко масштабувати та зберігати
великі обсяги структурованих або напівструктурованих даних,
підтримує індексацію і кешування для покращення швидкості
виконання запитів [13].
– Плюси:
– Гнучкість у зберіганні та обробці даних завдяки форматам
JSON/BSON.
13
ЧДТУ 242322.009 ПЗ
– Підтримка індексації та кешування для підвищення
продуктивності.
– Мінуси:
– Потрібна обережність при виборі структури даних, оскільки вона
впливає на продуктивність запитів.
– Може бути складно підтримувати консистентність даних при
високих навантаженнях.
Рис. 1.2 - Mongo Database [13]
– Опис: PostgreSQL (Рис. 1.3) — це потужна реляційна база даних з
підтримкою SQL, яка пропонує широкий спектр можливостей для
роботи з великими обсягами даних, включаючи транзакції, складні
запити та функціональність для оптимізації продуктивності [14].
– Плюси:
– Підтримка складних запитів, зокрема з’єднань і агрегатів.
14
ЧДТУ 242322.009 ПЗ
– Можливість створення індексів та користувацьких функцій для
оптимізації запитів.
– Багатий набір інструментів для моніторингу та налаштування.
– Мінуси:
– Може вимагати складного налаштування для досягнення найвищої
продуктивності.
– Високі вимоги до ресурсів для великомасштабних систем.
Рис. 1.3 - PostgreSQL [14]
– Опис: Redis (Рис. 1.4) — це швидке кешуюче сховище даних у
пам’яті, яке використовується для збереження даних у форматі
ключ-значення. Вона дозволяє значно підвищити продуктивність
додатків за рахунок швидкого доступу до даних та зменшення
навантаження на основну базу даних [7].
– Плюси:
– Дуже швидкий доступ до даних завдяки кешуванню в пам’яті.
– Підтримка широкого спектра структур даних, таких як списки,
множини та хеш-таблиці.
– Зменшення навантаження на основні бази даних, підвищення
загальної продуктивності.
– Підтримка кластеризації для горизонтального масштабування.
– Мінуси:
15
ЧДТУ 242322.009 ПЗ
– Обмежена довгострокова стійкість даних — при відключенні Redis
дані можуть бути втрачені.
– Складність управління великими обсягами даних і
консистентністю при частих оновленнях.
– Потрібна ретельна настройка для досягнення бажаного рівня
продуктивності.
Рис. 1.4 - Redis [7]
1.4 Конкретизація завдань роботи
– аналіз існуючих проблем продуктивності - провести всебічний
аналіз поточної архітектури системи та виявити основні вузькі місця,
що негативно впливають на продуктивність додатку;
– вивчення методів оптимізації - дослідити сучасні методи та
технології оптимізації продуктивності, такі як кешування, індексація
та оптимізація SQL-запитів;
– розробка стратегії оптимізації - на основі отриманих даних
розробити комплексну стратегію оптимізації, яка включає
рекомендації щодо поліпшення продуктивності, з урахуванням
16
ЧДТУ 242322.009 ПЗ
специфіки інтернет-магазину техніки;
– впровадження кешування - реалізувати ефективні механізми
кешування, що дозволяють зменшити навантаження на базу даних і
прискорити обробку запитів;
– оптимізація SQL-запитів - провести профілювання SQL-запитів,
виявити повільні запити, а також розробити їхні покращені версії;
– тестування продуктивності - провести тестування продуктивності
системи до і після впровадження оптимізаційних заходів,
використовуючи методи навантажувального тестування для оцінки
результатів;
– документування процесу - зібрати всі дані, методи, результати та
рекомендації у формі документації, яка може бути використана для
подальших досліджень або впроваджень.
17
ЧДТУ 242322.009 ПЗ
Висновок до першого розділу
Отже в рамках роботи буде найбільш доцільно розробити
інтернет-магазин техніки, так як він дозволить реалізувати весь функціонал
притаманний високонавантаженому додатку в інтернет-просторі,
продуктивність і стабільність якого залежить від кількості запитів, одночасних
користувачів та часу обробки запитів.
З огляду на величезні обсяги даних, з якими працюють сучасні
онлайн-магазини, оптимізація продуктивності стає необхідною умовою для
безперебійної роботи. Системи повинні бути здатні швидко обробляти запити, а
також ефективно управляти даними, що постійно змінюються. Однією з
основних проблем, яка виникає при обробці великих обсягів інформації, є
необхідність в оптимізації SQL-запитів, кешуванні даних, а також у
правильному виборі індексації і стратегій вибірки.
Особливо важливим є дослідження таких аспектів, як пагінація,
індексація, кешування, Fetch стратегії та оптимізація SQL-запитів, оскільки
кожен з них має безпосередній вплив на продуктивність системи. Пагінація
дозволяє зменшити навантаження на сервер при обробці великих обсягів даних,
розподіляючи їх на менші частини, що забезпечує швидший доступ до
інформації. Індексація, в свою чергу, значно прискорює виконання запитів до
бази даних, особливо при роботі з великими таблицями, де кожен додатковий
індекс може суттєво скоротити час вибірки. Кешування, як потужний
інструмент, дозволяє знизити кількість звернень до основної бази даних, що
зменшує навантаження на сервер і прискорює час відповіді на запити
користувачів. Стратегії Fetch, зокрема Lazy і Eager, є важливими для оптимізації
роботи з великими обсягами зв'язаної інформації, що дозволяє контролювати,
коли і які дані повинні бути завантажені.
Оптимізація SQL-запитів є ще одним важливим аспектом, адже
неправильно спроектовані або неефективні запити можуть значно уповільнити
роботу системи, навіть при наявності оптимізованих баз даних та кешування.
Виявлення та усунення "вузьких місць" у запитах через аналіз планів виконання
18
ЧДТУ 242322.009 ПЗ
(EXPLAIN) є важливим кроком для підвищення продуктивності.
Метою роботи є дослідження та реалізація цих підходів для
вдосконалення високонавантаженого додатку у вигляді інтернет-магазину
техніки. Ці комплексні методи в подальшому можуть бути використані для
розробки високонавантажених додатків іншими розробниками на основі
практик, досліджених далі в цій роботі.
19
ЧДТУ 242322.009 ПЗ
РОЗДІЛ 2 ТЕОРЕТИЧНІ ТА ЕКСПЕРИМЕНТАЛЬНІ ДОСЛІДЖЕННЯ
Формалізація задачі у математичній формі
У просторі ��, який представляє набір можливих конфігурацій серверної
частини веб-додатку, визначається непуста множина �� варіантів рішень. Ця
множина включає можливі параметри конфігурації бази даних та налаштування
Hibernate і PostgreSQL. Множина �� називається допустимою множиною.
Для кожного елемента �� із множини �� задана функція продуктивності
обробки запиту ��(��), яка відображає середній час виконання запиту. Ця
функція, названа цільовою функцією, визначена для всіх точок множини ��.
Задача оптимізації полягає в тому, щоб знайти такий набір параметрів ��  ϵ ��,
0
для якого ��(��) досягає мінімального значення, тобто:
��(�� ) ≤  ��(��)  ∀ ϵ �� (2.1)
0
Таке формулювання дозволяє систематизувати підхід до оптимізації,
виділити ключові параметри, що впливають на продуктивність, і оцінити
ефективність різних конфігурацій або підходів до виконання запитів.
Дано:
– ��(��) — час виконання операцій над базою даних (залежить від
параметрів оптимізації xxx);
– ��(��) — використання ресурсів (процесор, пам’ять, тощо);
– ��(��) —швидкість обробки запитів;
– �� =  (�� , �� ,... �� ) — множина параметрів оптимізації (наприклад,
1 2 ��
кількість кешованих даних, вибір індексів, Fetch стратегії, тощо).
Мета дослідження
Мінімізувати функцію витрат ��(��), яка враховує баланс між швидкістю
виконання операцій та використанням ресурсів:
��(��) =  �(��) +  �(��)) (2.2)
де α, β — вагові коефіцієнти, які визначають пріоритет між швидкістю
виконання операцій і ресурсами.
20
ЧДТУ 242322.009 ПЗ
Обмеження
1 ��(��) ≥  �� ,
������
де �� — мінімально допустима швидкість обробки запитів.
������
2 ��(��) ≤  �� ,
������
де �� — максимально допустиме використання ресурсів.
������
3 ��(��) ≤  �� ,
������
де �� — максимально допустимий час виконання операцій.
������
Етапи реалізації
1 Інформаційний аналіз: визначення початкових значень параметрів ��
та їх впливу на ��(��), ��(��), ��(��) на основі взаємодії між додатком та
базою даних.
2 Розробка та впровадження методів оптимізації: використання
різних стратегій:
– вибір ефективних Fetch стратегій;
– оптимізація SQL-запитів (��(��), де ��(��) — набір оптимізованих
запитів);
– додавання індексів (��(��));
– реалізація кешування (��(��)).
3 Оцінка ефективності:
Проведення тестування для порівняння початкових та оптимізованих
значень:
∆�� = ��  −  �� (2.3)
�������������� ������������������
де �� - значення функції витрат до оптимізації
��������������
 �� - значення функції витрат після оптимізації.
������������������
Таким чином, задача зводиться до розв'язання задачі багатокритеріальної
оптимізації із заданими обмеженнями.
21
ЧДТУ 242322.009 ПЗ
Структурно-функціональний аналіз
Система взаємодії з базою даних представлена такими компонентами:
1 модуль запитів до бази даних: формує та надсилає SQL-запити.
2 модуль оптимізації: реалізує методи кешування, індексації, пагінації та
fetch стратегій.
3 модуль обробки даних: обробляє отримані результати для передачі у
фронтенд.
Для кожного методу оптимізації проведено аналіз функціональних
можливостей та ефективності:
– пагінація зменшує обсяг даних, що передаються за один запит.
– індексація прискорює доступ до даних, але збільшує час операцій
запису.
– кешування скорочує кількість запитів до бази даних.
– fetch стратегії оптимізують обробку пов’язаних об’єктів.
Пошук раціонального компромісу
Ключовим завданням було досягти оптимального балансу між швидкістю
роботи та використанням ресурсів.
Розкриття невизначеностей
1 Динамічність обсягів даних: розмір бази даних змінюється з часом,
тому було враховано ефективність методів при різних обсягах.
2 Повторюваність даних: застосування кешування розглядається для
часто використовуваних даних.
Багатофакторність ризиків
1 Ризики перевантаження кешу:
– неправильна інвалідація може призводити до неконсистентності
даних.
2 Витрати на індексацію:
– додавання великої кількості індексів може збільшити час запису.
– неправильно підібраний тип індекса може тільки погіршити час
запису не маючи жодного впливу на час завантаження даних.
22
ЧДТУ 242322.009 ПЗ
3 Стратегії Fetch:
– неправильний вибір між EAGER та LAZY завантаженням може
призводити до надмірного навантаження на базу даних.
Методика проведення досліджень
1 Модель даних:
– тестування проводилося на базі даних із варіативними обсягами
даних (10, 4000, 50 000 записів).
– для кожного сценарію змінювались параметри пагінації, типи
індексів та стратегії завантаження даних.
2 Процес експерименту:
– визначення початкових параметрів системи;
– впровадження оптимізацій;
– збір і аналіз метрик.
Аналіз результатів
1 Ефективність пагінації:
– при великих обсягах даних час відповіді зменшується вдвічі
завдяки зниженню навантаження на сервер.
2 Вплив індексації:
– індексація знижує час вибірки даних у 2–3 рази, але збільшує час
вставки.
3 Кешування:
– час обробки запитів скорочувався на 40%–60%.
4 Fetch стратегії:
– LAZY підходить для невеликих наборів даних, але EAGER стає
ефективнішим при великих обсягах пов’язаних даних.
5 Оптимізація SELECT COUNT запиту:
– оптимізація запиту має сенс, якщо операції читання виконуються
дуже часто, а запису - рідко.
23
ЧДТУ 242322.009 ПЗ
2.1 Теоретичні дослідження
Аналіз сутності процесів
Процес оптимізації взаємодії серверного додатку з базою даних базується
на кількох ключових підходах:
1 Пагінація: забезпечує поділ великих наборів даних на менші частини,
які передаються клієнту, зменшуючи навантаження на сервер [15].
2 Індексація: дозволяє значно прискорити виконання операцій вибірки
та пошуку в базі даних за рахунок створення структурованих ключів
для полів таблиць [6].
3 Кешування: спрямоване на скорочення кількості запитів до бази
даних шляхом зберігання часто використовуваних результатів у
пам’яті [8].
4 Fetch стратегії: оптимізують завантаження асоційованих об’єктів,
дозволяючи зменшити кількість запитів або обсяг даних, які
передаються за один раз [16].
Ці методи взаємодіють із зовнішнім середовищем, враховуючи змінність
обсягів даних, частоту запитів і ресурсні обмеження серверної інфраструктури.
Формулювання гіпотези
Власна гіпотеза припускає, що комплексний підхід до оптимізації
продуктивності високонавантаженого додатку, що використовує сучасні
технології, такі як індексація, кешування, оптимізація SQL-запитів, ефективні
Fetch стратегії та пагінація, дозволяє суттєво зменшити час обробки запитів і
підвищити ефективність роботи системи.
Побудова моделі
1 Модель пагінації: формула для визначення даних, які слід повернути
для заданої сторінки.
������������ =  (�������� −  1) ⋅ �������� (2.4)
���������� =  �������� (2.5)
Це дозволяє обмежити кількість записів, які обробляє сервер і передає клієнту.
24
ЧДТУ 242322.009 ПЗ
2 Модель індексації: оптимізація запитів здійснюється через побудову
індексів для найчастіше використовуваних полів, таких як:
– Пошукові поля (наприклад, назва продукту).
– Поля, що використовуються для сортування (наприклад, ціна).
3 Модель кешування: кешування будується на основі ключів, що
формуються із запиту. Наприклад, результати запиту до продуктів
кешуються з використанням унікального ідентифікатора ключа
(наприклад, cacheKey = categoryId + page).
4 Модель Fetch стратегій: експерименти порівнювали жадібне
завантаження (EAGER) і ліниве завантаження (LAZY) в асоціаціях.
Проведення математичного дослідження
1 Пагінація: дослідження продемонстрували, що час відповіді при
зростанні кількості записів із використанням пагінації зростає значно
повільніше, ніж без неї.
2 Індексація: аналіз показав, що додавання індексів на поля для пошуку
та сортування дозволяє зменшити час виконання запиту вибірки
вдвічі, хоча й збільшує час на запис у базу даних на 15-20%.
3 Кешування: запити, що використовують кеш, виконуються на 40-60%
швидше, ніж ті, які безпосередньо звертаються до бази даних.
4 Fetch стратегії: при невеликих обсягах даних EAGER-стратегія
демонструє кращі результати, тоді як LAZY-стратегія є більш
ефективною для обробки великих наборів асоційованих даних.
Аналіз теоретичних рішень
Отримані результати підтверджують ефективність кожного з методів
окремо та в рамках комплексного підходу.
Формулювання висновків
1 Використання пагінації є доцільним для великих наборів даних,
оскільки зменшується обсяг інформації, яку потрібно обробити та
передати за один запит.
25
ЧДТУ 242322.009 ПЗ
2 Індексація ефективна для пошуку та сортування, але її вплив слід
ретельно балансувати з урахуванням частоти операцій запису.
3 Кешування істотно покращує продуктивність у сценаріях з
повторюваними запитами.
4 Правильний вибір Fetch стратегії залежить від структури даних і
частоти доступу до них.
Ці результати можуть бути узагальнені для інших подібних систем, що
працюють із великими обсягами даних, та впроваджені для підвищення їхньої
продуктивності.
2.2 Експериментальні дослідження
Пагінація
Експеримент №1
Мета: дослідити вплив пагінації на швидкість обробки запиту на
прикладі таблиці з 10 записами.
Хід експерименту: розгорнемо два контролери - один контролер
обробляє запити із застосуванням пагінації, інший — без неї. Далі запустимо
навантажувальний тест на API для читання категорій, який підвищував
кількість одночасних користувачів з 250 до 300 протягом 20 секунд і вичитував
категорії.
Результати: зображені на рисунку 2.1, 2.2 і 2.3.
Рис. 2.1 - Результати тестів після виконання запитів на читання категорій
26
ЧДТУ 242322.009 ПЗ
Рис. 2.2 - Час обробки запиту читання категорій (без пагінації)
Рис. 2.3 - Час обробки запиту читання категорій (з пагінацією)
Аналіз отриманих результатів: порівняння показало, що при незначній
кількості записів у базі даних використання пагінації не є критично важливим
для продуктивності, оскільки серверна обробка та передача даних клієнту
проходять достатньо швидко.
Експеримент №2
Мета: дослідити вплив пагінації на швидкість обробки запиту на
прикладі таблиці з 2 тисячами записів.
Хід експерименту: проведемо дії ідентичні до тих, які були описані в
експерименті №1, відрізняючись лише API, над яким проводилось тестування -
читання клієнтів. В таблицю клієнтів додаємо 2 тисячі записів.
Результати: зображені на рисунку 2.4, 2.5 і 2.6.
27
ЧДТУ 242322.009 ПЗ
Рис. 2.4 - Результати виконання запитів на читання клієнтів (6 тисяч записів)
Рис. 2.5 - Час обробки запиту читання клієнтів (без пагінації)
Рис. 2.6 - Час обробки запиту читання клієнтів (з пагінацією)
Аналіз отриманих результатів: зі збільшенням обсягу даних (Рис. 2.5)
максимальний час відповіді для запиту без пагінації збільшується в середньому
до 41 мс. Такий підхід значно збільшує навантаження на сервер, оскільки всі
записи передаються за один запит (Рис. 2.6), що ускладнює обробку та
споживає більше ресурсів (перший експеримент проведено на 4 тисячах
28
ЧДТУ 242322.009 ПЗ
записів).
На противагу цьому запит з пагінацією (Рис. 2.7) продемонстрував
середній час відповіді в 8 мс при великих обсягах даних. Поділ результатів на
окремі сторінки дозволяє суттєво зменшити обсяг даних, які повертаються за
кожен запит, завдяки чому зменшився час обробки на сервері і було знижено
навантаження на мережу.
Експеримент №3
Мета: дослідити вплив пагінації на швидкість обробки запиту на
прикладі таблиці з 50 тисячами записів.
Хід експерименту: проводимо дії ідентичні до тих, які були описані в
експерименті №1 та 2, відрізняючись лише збільшенням кількості записів в
таблиці до 50 тисяч.
Результати: зображені на рисунку 2.7, 2.8 і 2.9.
Рис. 2.7 - Результати виконання запитів на читання клієнтів (50 тисяч записів)
Рис. 2.8 - Час обробки запиту читання категорій (без пагінації) (50 тисяч
записів)
29
ЧДТУ 242322.009 ПЗ
Рис. 2.9 - Час обробки запиту читання категорій (з пагінацією) (50 тисяч
записів)
Аналіз отриманих результатів: час відповіді росте прямо пропорційно
кількості записів (Рис. 2.9) в базі даних і ще раз підтверджує ефективність
пагінації при зчитуванні такої кількості записів. Час відповіді зменшується
вдвічі (Рис. 2.10), що можна бачити на графіках і метриках, зображених нижче.
Висновок: згідно з результатами дослідження, можна стверджувати, що
використання пагінації є доцільним при роботі з великими обсягами даних у
базі даних. Пагінація дозволяє значно зменшити навантаження на сервер та
знижує середній час відповіді на запити, особливо коли кількість записів
достатньо велика. Таким чином, впровадження пагінації для додатків, які
передбачають великі набори даних, є ефективною стратегією для оптимізації
продуктивності серверної частини та забезпечення стабільної роботи додатку.
Індекси
Експеримент №1
Мета: дослідити вплив індексів на швидкість операції запису в таблиці
продуктів.
Хід експерименту: для таблиці продуктів додаємо індекси описані на
рисунку нижче. Їх кількість обумовлена полями, над якими може виконуватись
фільтрація продуктів через розроблений API.
30
ЧДТУ 242322.009 ПЗ
Рис. 2.10 - Додані індекси на полях таблиці product
Результати: зображені на рисунку 2.11 і 2.12.
Рис. 2.11 - Час виконання запиту на створення продуктів (без індексів)
Рис. 2.12 - Час виконання запиту на створення продуктів (з індексами)
31
ЧДТУ 242322.009 ПЗ
Аналіз отриманих результатів: як можна побачити на рисунках 2.11 і
2.12, додавання індексів збільшує час операції INSERT приблизно на 15 - 20%,
так як тепер разом із записом даних в таблицю система також повинна оновити
кожен індекс, повʼязаний з цією таблицею.
Експеримент №2
Мета: дослідити вплив індексів на швидкість операції читання продуктів.
Хід експерименту: проведемо експерименти на запитах читання
продуктів та використанні різних фільтрів разом з індексами та без них при
наявності 2 тисяч записів в таблиці продуктів.
Результати: зображені на рисунку 2.13 і 2.14 - без фільтрації, 2.15 і 2.16 -
фільтрація по імені, 2.17 і 2.18 - фільтрація по ціні та кількості.
Рис. 2.13 - Час виконання запиту на отримання продуктів (без індексів)
Рис. 2.14 - Час виконання запиту на отримання продуктів (з індексами)
32
ЧДТУ 242322.009 ПЗ
Рис. 2.15 - Час виконання запиту читання продуктів - фільтрація по імені (без
індексів)
Рис. 2.16 - Час виконання запиту читання продуктів - фільтрація по імені (з
індексами)
Рис. 2.17 - Час виконання запиту читання продуктів - фільтрація по кількості та
ціні (без індексів)
33
ЧДТУ 242322.009 ПЗ
Рис. 2.18 - Час виконання запиту читання продуктів - фільтрація по кількості та
ціні (з індексами)
Аналіз отриманих результатів: враховуючи подані результати вище,
можна стверджувати, що впровадження індексів в таблицю продуктів вдвічі
зменшило час обробки запитів, враховуючи той факт, що запити будуються на
полях, по яких створені індекси.
Експеримент №3
Мета: дослідити поведінку та вплив індексів на швидкість операції
читання замовлення та їх асоціацій без фільтрів.
Хід експерименту: проведемо експерименти на запитах читання
замовлень та повʼязаних з ними продуктів разом з індексами та без них при
наявності 5 тисяч записів в таблиці продуктів.
Додаємо нову міграцію (Рис. 2.19) додаючи індекси на поля, які беруть
участь в фільтрації, а також order_item.id - по ньому відбувається звʼязування
батьківського обʼєкта з дочірніми.
Рис. 2.19 - Індекси для таблиць order та order_item
34
ЧДТУ 242322.009 ПЗ
Результати: зображені на рисунку 2.20 і 2.21.
Рис. 2.20 - Час виконання запиту читання замовлень без фільтрів (без індексів)
Рис. 2.21 - Час виконання запиту читання замовлень без фільтрів (з індексами)
Аналіз отриманих результатів: після проведення тесту складається
неоднозначна картина - додавання індексів не має суттєвого впливу на
загальний час запиту (Рис. 2.20) без використання фільтрів (лише стандартна
пагінація). Можемо бачити лише певне покращення на початку другого графіку
(Рис. 2.21), де спостерігається зменшення часу відповіді протягом кількох
секунд, хоча в кінці графіку можемо бачити сплеск часу виконання.
Максимальний час відповіді зменшився після додавання індексу з 220 мс до 160
мс, що підтверджує позитивний вплив індексів на обробку.
Експеримент №4
35
ЧДТУ 242322.009 ПЗ
Мета: дослідити поведінку та вплив індексів на швидкість операції
читання замовлення та їх асоціацій при фільтрації по полю в батьківській
таблиці.
Хід експерименту: проведемо експерименти на запитах читання
замовлень та повʼязаних з ними продуктів, описаних відношенням one-to-many,
використовуючи фільтрацію по customer_id разом з індексами та без них при
наявності 5 тисяч записів в таблиці продуктів.
Результати: зображені на рисунку 2.22 і 2.23.
Рис. 2.22 - Час виконання запиту читання замовлень по customer id (без індексів)
Рис. 2.23 - Час виконання запиту читання замовлень по customer id (з індексами)
Аналіз отриманих результатів: експеримент показав неоднозначний
результат, так як на певних проміжках бачимо покращення при використанні
індексів (Рис. 2.23), але в той же час на інших проміжках бачимо, що швидкодія
36
ЧДТУ 242322.009 ПЗ
без індексів вища (Рис. 2.22), ніж з ним. Варто враховувати, що PostgreSQL
Query Planner/Optimizer намагається обрати найбільш оптимальний варіант та
підхід до обробки запиту, що може також впливати на те, чи використовується
індекс чи ні.
Експеримент №5
Мета: дослідити поведінку та вплив індексів на швидкість операції
читання замовлення та їх асоціацій при фільтрації по полю в асоційованій
таблиці.
Хід експерименту: проведемо експерименти на запитах читання
замовлень та повʼязаних з ними продуктів, описаних відношенням one-to-many,
використовуючи фільтрацію по product_id разом з індексами та без них при
наявності 5 тисяч записів в таблиці продуктів.
Результати: зображені на рисунку 2.24 і 2.25.
Рис. 2.24 - Час виконання запиту читання замовлень по product id (без індексів)
Рис. 2.25 - Час виконання запиту читання замовлень по product id (з індексами)
37
ЧДТУ 242322.009 ПЗ
Аналіз отриманих результатів: як і в двох попередніх тестах, ситуація
ідентична - маємо покращення в певних часових проміжках (Рис. 2.24). Однак в
іншій частині графіку спостерігаємо значне погіршення швидкодії (Рис. 2.25).
Висновок: приріст часу після додавання індексів залежить від
правильності обраного типу індексу, полів таблиці, на яких він був створений, а
також загальної кількості індексів. Надмірна їх кількість може принести
покращення для операцій читання, але погіршить час виконання запису, тому це
також варто враховувати в конкретному проєкті. Крім того, при додаванні
індексів необхідно також провести тести, щоб перевірити його вплив і досягти
максимального приросту швидкодії на конкретному проекті й технології.
Кешування
Експеримент №1
Мета: дослідити поведінку та вплив індексів на швидкість операції
читання замовлення та їх асоціацій при фільтрації по полю в батьківській
таблиці.
Хід експерименту: запустимо навантажувальне тестування на контролері
для вичитки продуктів, щоб перевірити ефективність кешування на реальному
прикладі. В тесті використовується приріст віртуальних користувачів від 50 до
100 на всьому проміжку виконання тесту.
Результати: зображені на рисунку 2.26 і 2.27.
Рис. 2.26 - Час виконання запитів читання продуктів без кешування
38
ЧДТУ 242322.009 ПЗ
Рис. 2.27 - Час виконання запитів читання продуктів з кешуванням
Аналіз отриманих результатів: як можна бачити на рисунках 2.27 і 2.28,
кешування значно покращує продуктивність додатку (звертаємо увагу на зміну
шкали з 100 мс до 60 мс на графіках вище), так як кешований запис більше не
потрібно вичитувати з БД, за рахунок чого й досягається економія ресурсів та
покращується швидкодія.
Висновок: кешування є дуже ефективним методом оптимізації взаємодії з
БД. Воно забезпечує економію ресурсів і зниження часу обробки запитів, так як
підключення до бази даних не потрібне. Дані читаються з ближчого кешу. Але
необхідно продумати стратегію інвалідації кешу, щоб не допустити застарілих
даних.
Оптимізація запиту SELECT COUNT
Експеримент №1
Мета: дослідити можливий спосіб оптимізації знаходження загальної
кількості записів в таблиці (SELECT COUNT) на прикладі простої таблиці,
заповненої 1 мільйоном записів.
Хід експерименту: створимо таблицю та заповнимо її 1 млн записів (Рис.
2.28).
39
ЧДТУ 242322.009 ПЗ
Рис. 2.28 - Створення та заповнення таблиці items на 1 млн записів
Вимірювання часу виконання запиту SELECT COUNT дає основу для
оцінки швидкості інших типів підрахунку.
Команда pgbench надає зручний спосіб багаторазово виконувати запит і
збирати статистику щодо продуктивності:
echo "SELECT count(*) FROM items;" | pgbench -d
count -t 50 -P 1
Результат:
- середній час - 84.915 мс;
- стандартне відхилення - 5.251 мс.
Варто зауважити щодо count(1) та count(*). Можна подумати, що count(1)
буде швидшим, оскільки count(*) начебто звертається до даних всього рядка.
Однак насправді все навпаки. Зірочка у цьому випадку не має значення, на
відміну від її використання у SELECT *. PostgreSQL обробляє вираз count(*) як
окремий випадок, що не приймає аргументів.
З іншого боку, count(1) приймає аргумент, і PostgreSQL мусить перевіряти
для кожного рядка, чи цей аргумент, тобто значення 1, справді не є NULL.
Виконання наведеного вище тесту продуктивності з count(1) дає такі
результати:
- середній час - 98.896 мс;
- стандартне відхилення - 7.280 мс.
Однак обидві форми, count(1) і count(*), за своєю суттю є повільними.
Не існує єдиного універсального підрахунку рядків, який база даних
могла б кешувати, тому вона повинна сканувати всі рядки, підраховуючи,
40
ЧДТУ 242322.009 ПЗ
скільки з них є видимими. Продуктивність точного підрахунку збільшується
лінійно зі зростанням розміру таблиці.
Рис. 2.29 - Вартість операції COUNT, описана командою EXPLAIN
Сканування становить 85% від загальної вартості (Рис. 2.29). Коли ми
подвоюємо розмір таблиці, час виконання запиту приблизно також подвоюється,
оскільки вартість сканування та агрегації зростає пропорційно одна до одної.
Таблиця 2.1
Кількість записів та середній час запиту COUNT
К-сть записів Середній час
1 млн 85 мс
2 млн 161 мс
4 млн 343 мс
Єдиний дієвий спосіб, який можна використати - це кешувати кількість
рядків вручну, використовуючи ручний лічильник, який збільшується або
зменшується залежно від операцій. Такий лічильник треба буде підтримувати
для кожної таблиці у вигляді тригеру [17], написаного на одному з діалектів, які
надає PostgreSQL.
41
ЧДТУ 242322.009 ПЗ
Нижче наведено рішення, яке використовує тригер на таблицях для
підрахунку кількості записів (Рис. 2.30):
Рис. 2.30 - Тригер для оновлення лічильника записів
Результати:
Після перевірки швидкодії з використанням тригера необхідно виділити,
що швидкість читання та оновлення кешованого значення не залежить від
розміру таблиці, а саме читання виконується дуже швидко. Однак цей підхід
переносить навантаження на операції вставки та видалення.
Без тригера виконання наступного оператора в середньому займає 4,7
секунди (Рис. 2.31), тоді як вставки з використанням тригера стають у 50 разів
повільнішими:
Рис. 2.31 - Вставка даних в таблицю items
42
ЧДТУ 242322.009 ПЗ
Fetch Strategy
Існує дві стратегії завантаження [16]:
– жадібне завантаження (Eager Loading) — ініціалізація даних
відбувається одразу при їх отриманні.
– ліниве завантаження (Lazy Loading) — використовується для
відтермінування ініціалізації об’єкта настільки довго, наскільки це
можливо. В цьому випадку, Spring JPA створює proxy обʼєкт на місці
асоціації і завантажує реальні дані з БД лише при доступі до них.
Експеримент №1
Мета: дослідити вплив стратегій завантаження асоційованих даних на
малій кількості повʼязаних даних.
Хід експерименту: Проведемо експеримент на контролері, який повертає
обʼєкт замовлення та його продукти. В даному випадку між даними існує
асоціація one-to-many.
В першому експерименті заповнюємо базу даних замовленнями і до
кожного з них приєднуємо від 1 до 10 продуктів, щоб перевірити вплив
стратегій завантаження при малому наборі повʼязаних даних.
Результати: зображені на рисунку 2.32 і 2.33.
Рис. 2.32 - Час виконання запиту читання замовлень - EAGER стратегія (1-10
продуктів в замовленні)
43
ЧДТУ 242322.009 ПЗ
Рис. 2.33 - Час виконання запиту читання замовлень - LAZY стратегія (1-10
продуктів в замовленні)
Аналіз отриманих результатів: на основі отриманих графіків
навантажувального тестування контролера для вичитки замовлень з
повʼязаними продуктами, можна зробити висновок, що ліниве завантаження
вимагає більше часу, так як кожен продукт вичитується окремим запитом до
бази даних. Spring JPA надає свої методи оптимізації, тому витрачається менше
ресурсів, ніж було б необхідно для відкриття окремого зʼєднання до БД і
вичитування кожного окремого продукту, але тим не менш - швидкодія падає.
Експеримент №2
Мета: дослідити вплив стратегій завантаження асоційованих даних на
більшій кількості повʼязаних даних.
Хід експерименту: наступний експеримент проводимо з тією ж
таблицею, що використовувалась в попередньому експерименті, але тепер
додамо від 30 до 50 продуктів до кожного замовлення, щоб перевірити
поведінку стратегій завантаження при більшій кількості асоційованих даних.
Результати: зображені на рисунку 2.34 і 2.35.
44
ЧДТУ 242322.009 ПЗ
Рис. 2.34 - Час виконання запиту читання замовлень - EAGER стратегія (30-50
продуктів в замовленні)
Рис. 2.35 - Час виконання запиту читання замовлень - LAZY стратегія (30-50
продуктів в замовленні)
Аналіз отриманих результатів: як можна помітити на графіках вище,
при більшій кількості асоційованих даних, середній час обробки значно
збільшується, що й очікувалось від окремого завантаження кожного продукту.
Висновок: можна зробити висновок, що правильно підібрана стратегія
завантаження даних для асоціацій значно впливає на час обробки запиту до БД.
При виборі стратегії варто враховувати наступні фактори:
45
ЧДТУ 242322.009 ПЗ
– якщо необхідні всі повʼязані дані батьківського обʼєкта, то EAGER
стратегія підходить значно краще, так як всі дані витягуються в
одному запиті до БД;
– якщо необхідні лише деякі з повʼязаних даних батькіського обʼєкта і
таких даних достатньо багато, то LAZY стратегія буде оптимальним
рішенням, так як дані не будуть витягуватись всі за один раз, а лише
при доступі до конкретного обʼєкта зі списку, таким чином
зменшуючи кількість даних, які необхідно передати по мережі, а
також памʼять, в якій ці дані зберігаються в процесі виконання запиту.
46
ЧДТУ 242322.009 ПЗ
Висновки до другого розділу
Дослідження, проведені у цьому розділі, підтвердили доцільність
використання різних підходів для оптимізації продуктивності серверної частини
додатків, які працюють із великими обсягами даних. Основні результати можна
резюмувати наступним чином:
1 Пагінація суттєво покращує продуктивність серверної частини при
роботі з великими обсягами даних, особливо за умови зростання
кількості записів у таблицях. Вона знижує навантаження на сервер та
мережу, а також забезпечує швидку обробку запитів навіть при значній
кількості одночасних користувачів.
2 Індекси значно зменшують час обробки запитів на читання, якщо
вони створені на ключових полях, які використовуються у фільтрації.
Проте вони також додають витрати на операції запису, що вимагає
обережного підходу до їх впровадження та тестування.
3 Кешування демонструє значні переваги для скорочення часу обробки
запитів і зниження навантаження на базу даних. Проте, для
ефективного використання кешу необхідно враховувати стратегії
інвалідації, щоб уникнути роботи із застарілими даними.
4 Оптимізація запитів для великих обсягів даних, таких як SELECT
COUNT, є складним завданням. Традиційні підходи потребують
повного сканування таблиці, що робить їх дорогими за великих
обсягів даних. Використання кешування для підрахунку рядків або
інших альтернативних підходів може значно зменшити час виконання
таких запитів.
Таким чином, застосування описаних оптимізаційних підходів є
ефективним способом забезпечення стабільної роботи додатків із високими
навантаженнями, дозволяючи знизити використання серверних ресурсів і
покращити користувацький досвід. Кожен із методів має свої переваги і
недоліки, що вимагає ретельного аналізу та тестування перед впровадженням у
реальних умовах.
47
ЧДТУ 242322.009 ПЗ
РОЗДІЛ 3 ВПРОВАДЖЕННЯ РЕЗУЛЬТАТІВ ДОСЛІДЖЕНЬ У
ПРАКТИКУ ПРОЕКТУВАННЯ ПРОГРАМНОГО ЗАБЕЗПЕЧЕННЯ
ІНФОРМАЦІЙНИХ СИСТЕМ
3.1 Моделювання предметної області
В наступних підпунктах буде розглянуто предметну область проекту,
змодельовано його структуру та описано робочу область моделювання.
3.1.1 Предметна область моделювання. Модель предметної області.
Словник предметної області
Рис. 3.1 - Модель предметної області
Замовлення – це процес, коли клієнт вибирає певні продукти в
інтернет-магазині та оформляє їх для покупки.
Елемент замовлення – це окремий запис у замовленні, який містить
інформацію про конкретний продукт, його кількість та ціну.
Клієнт – це користувач, який здійснює покупки в інтернет-магазині.
Продукт – це товар або послуга, що пропонується в інтернет-магазині для
купівлі.
Рейтинг продукту – це оцінка продукту, яку дають користувачі або
клієнти інтернет-магазину на основі їхнього досвіду.
Картинка продукту – це зображення, яке супроводжує опис продукту на
сторінці інтернет-магазину.
48
ЧДТУ 242322.009 ПЗ
3.1.2 Елементи моделювання предметної області
Для моделювання предметної області використовується мова UML (Рис.
3.2, 3.3). На етапі проектування будуть наведені діаграми, зображені на рисунку
нижче.
Рис. 3.2 - Основні графічні символи UML [18]
Рис. 3.3 - Єднальні елементи UML [18]
49
ЧДТУ 242322.009 ПЗ
Дані графічні схеми описують предметну область проекту, дають
уявлення про роботу системи, як в загальному вигляді, так і на рівні
компонентів та їх взаємодії. Слідуючи цим діаграмам можна відносно швидко
зрозуміти структуру нашого додатку, його інтеграцію з іншими системами
університету.
3.1.3 Робоча область моделювання
Система забезпечує повний цикл обробки замовлень: від управління
каталогом товарів до оформлення та доставки замовлень клієнтам.
Кожен товар в системі має унікальний ідентифікатор, назву, ціну та
кількість на складі. Товари організовані за категоріями для зручної навігації та
пошуку. Система підтримує кешування часто запитуваних товарів для
оптимізації продуктивності та зменшення навантаження на базу даних.
Клієнти можуть переглядати каталог товарів з можливістю фільтрації за
різними параметрами: назвою, ціновим діапазоном, категорією та наявністю на
складі. При оформленні замовлення система автоматично перевіряє наявність
необхідної кількості товару на складі та оновлює складські залишки.
Адміністратор може оновити замовлення, додати або прибрати позиції,
кількість замовлень, змінити ціну конкретного продукту в замовленні, якщо
необхідно.
Система відстежує статус кожного замовлення від моменту створення до
доставки. У разі скасування замовлення, товари автоматично повертаються на
склад. Для кожного замовлення зберігається інформація про клієнта, обрані
товари, їх кількість та ціну на момент замовлення.
Система забезпечує можливість оновлення інформації про товари,
категорії та клієнтів, зберігаючи при цьому цілісність даних та зв'язків між
ними.
3.2 Формування та аналіз вимог
Далі проаналізуємо та опишемо вимоги до проекту, який розробляється.
50
ЧДТУ 242322.009 ПЗ
3.2.1 Формування вимог до програмного забезпечення
Первинні і детальні вимоги
ПЕРВИННІ ВИМОГИ
1 Система повинна забезпечувати управління товарами, категоріями,
замовленнями та клієнтами.
2 Необхідно реалізувати можливість створення, редагування та
видалення всіх сутностей.
3 Система має відстежувати наявність товарів на складі.
4 Потрібно забезпечити можливість фільтрації та пошуку даних.
ДЕТАЛЬНІ ВИМОГИ
1 Система повинна зберігати інформацію про товари: назва, ціна,
кількість на складі, категорія.
2 При створенні товару обов'язково вказується його категорія.
3 Система повинна автоматично відстежувати дату створення та
оновлення товару.
4 Повинна бути можливість фільтрації товарів за:
a Назвою (частковий пошук).
b Ціновим діапазоном (мінімальна та максимальна ціна).
c Кількістю на складі.
d Категорією.
e Датою створення та оновлення.
f Система повинна кешувати інформацію про товари для оптимізації
продуктивності.
5 Кожне замовлення повинно містити:
a Інформацію про клієнта.
b Список товарів з кількістю та ціною на момент замовлення.
c Статус замовлення (очікує, скасовано, доставлено).
d Дату створення та оновлення.
6 При створенні замовлення система повинна:
a Перевіряти наявність достатньої кількості товарів на складі.
51
ЧДТУ 242322.009 ПЗ
b Автоматично зменшувати кількість товарів на складі.
c Фіксувати поточну ціну товарів.
7 При скасуванні замовлення система повинна:
a Автоматично повертати товари на склад.
b Перевіряти можливість скасування (не можна скасувати
доставлене замовлення).
8 Система повинна забезпечувати пошук замовлень за:
a Статусом.
b Клієнтом.
c Товаром.
d Періодом створення.
e Періодом оновлення.
9 Система повинна зберігати основну інформацію про клієнтів:
a Ім'я.
b Email.
c Дату реєстрації.
d Дату останнього оновлення.
10 Повинна бути можливість пошуку клієнтів за:
a Ім'ям (частковий пошук).
b Email (частковий пошук).
c Датою реєстрації.
d Датою останнього оновлення.
11 Система повинна забезпечувати можливість оновлення інформації
про клієнта.
12 Система повинна підтримувати створення та редагування категорій
товарів.
13 Кожна категорія повинна мати:
a Унікальний ідентифікатор.
b Назву.
c Дату створення.
52
ЧДТУ 242322.009 ПЗ
d Дату останнього оновлення.
14 При видаленні категорії система повинна перевіряти відсутність
пов'язаних товарів.
Вимоги замовника і розробника. Функціональні та нефункціональні
вимоги
Функціональні вимоги
Пагінація та фільтрація:
– Система повинна підтримувати пагінацію для всіх списків
(замовлення, продукти, категорії, клієнти).
– Система повинна надавати можливість фільтрації даних за різними
критеріями.
Валідація та обробка помилок:
– Система повинна забезпечувати валідацію вхідних даних для всіх
операцій.
– Система повинна обробляти помилки, такі як відсутність продукту або
клієнта, недостатня кількість продукту на складі, дублювання
ідентифікаторів продуктів у замовленні.
Кешування:
– Система повинна використовувати кешування для збільшення
швидкодії обробки запитів.
Нефункціональні вимоги
– Використання Spring Framework.
– Реалізація кешування.
– Використання UUID для ідентифікації.
– Маппінг DTO/Entity.
– Транзакційність операцій.
– Валідація даних.
– Обробка помилок.
– Імплементація пагінації для кожного сутності (якщо є необхідність).
53
ЧДТУ 242322.009 ПЗ
3.2.2 Формування вимог за допомогою діаграми прецедентів
Діаграма на рисунку 3.4 показує функціональність управління
категоріями в системі інтернет-магазину. Є три типи користувачів: Відвідувач,
Покупець і Адміністратор. Відвідувач і Покупець можуть лише переглядати
категорії, тоді як Адміністратор має розширені права — створювати,
оновлювати та видаляти категорії. Діаграма відображає межі системи та
взаємодію користувачів із відповідними функціями.
Рис. 3.4 - Діаграма прецедентів функціоналу менеджменту категорій
Діаграма на рисунку 3.5 демонструє функціональність управління
клієнтами в системі.
Є три ролі користувачів:
– Відвідувач не має доступу до функцій клієнтської системи.
– Покупець може оновлювати власну інформацію.
– Адміністратор має розширені права: переглядати клієнтів, оновлювати
інформацію про них, створювати та видаляти облікові записи.
Діаграма окреслює межі функціональності, що стосуються клієнтів.
54
ЧДТУ 242322.009 ПЗ
Рис. 3.5 - Діаграма прецедентів функціоналу менеджменту клієнтів
Діаграма на рисунку 3.6 описує функціональність перегляду продуктів та
оформлення замовлень у системі.
Покупець має доступ до перегляду продуктів за фільтрами чи
категоріями, додавання оцінок і коментарів до продуктів, оформлення
замовлень, перегляду статусу та редагування замовлень (допустиме до
створення).
Адміністратор може створювати, редагувати, оновлювати інформацію,
додавати фото та видаляти продукти. Крім того, він управляє замовленнями:
переглядає, редагує та оновлює їх статус.
Діаграма окреслює дії та ролі користувачів, пов’язані з продуктами та
замовленнями.
55
ЧДТУ 242322.009 ПЗ
Рис. 3.6 - Діаграма прецедентів функціоналу процесу вибору та оформлення
замовлення
56
ЧДТУ 242322.009 ПЗ
3.2.3 Проектування логічної структури програмного комплексу
Нижче будуть наведені діаграми UML, які описують проект, його
компоненти та структуру на різних рівнях.
3.2.3.1 Діаграми класів
Класова діаграма на рисунку 3.7 відображає архітектуру
інтернет-магазину, що включає основні сутності, бізнес-логіку та взаємодію між
компонентами. У центрі діаграми розміщені сутності, які представляють
категорії, продукти, клієнтів і замовлення. Category зберігає інформацію про
категорії товарів, а Product пов’язаний із категоріями та включає назву, опис,
ціну, кількість і можливість додавання зображення через сутність ProductImage.
Клієнти представлені через сутність Customer, яка зберігає основні дані про
користувачів, а замовлення описані сутністю Order, що включає статус, загальну
ціну та перелік продуктів із кількістю через проміжний клас OrderItem.
Робота із запитами користувачів забезпечується контролерами, такими як
CategoryController, ProductController, CustomerController і OrderController, які
взаємодіють із сервісами. Сервіси, такі як CategoryService, ProductService,
CustomerService та OrderService, реалізують бізнес-логіку, забезпечуючи
створення, оновлення, видалення та пошук даних. Для роботи з базою даних
використовуються репозиторії (CategoryRepo, ProductRepo, CustomerRepo,
OrderRepo), а мапери перетворюють сутності у DTO та навпаки, ізолюючи дані
від логіки представлення.
Діаграма показує чіткий розподіл обов’язків між компонентами, що
відповідає принципам багаторівневої архітектури. Завдяки цьому
забезпечується гнучкість і можливість масштабування системи, що спрощує
додавання нової функціональності або зміну існуючої логіки.
Крім того, система демонструє використання принципу інкапсуляції, де
кожен компонент відповідає за свою зону відповідальності, що покращує її
модульність. Контролери забезпечують взаємодію з користувачем, сервіси
реалізують бізнес-логіку, а репозиторії абстрагують доступ до бази даних.
57
ЧДТУ 242322.009 ПЗ
Рис. 3.7 - Діаграма класів
3.2.3.2 Діаграми пакетів
Діаграма на рисунку 3.8 ілюструє архітектуру системи управління
замовленнями, що складається з кількох ключових компонентів, таких як
контролер, сервісний шар, репозиторій, утиліти, мапери, сутності та
58
ЧДТУ 242322.009 ПЗ
перерахування. Контролер обробляє запити від користувачів і взаємодіє з
об'єктами передачі даних (DTO), передаючи їх до сервісного шару, де
реалізується бізнес-логіка. Сервісний шар взаємодіє з репозиторієм для доступу
до бази даних, а мапери забезпечують перетворення між сутностями та DTO.
Загалом, діаграма демонструє чітку структуру, що дозволяє ефективно
організувати код і спростити підтримку та розширення системи.
Рис. 3.8 - Діаграма пакетів
3.2.4 Архітектурне проектування
Наступні діаграми UML описують архітектурну будову проекту та його
складових.
3.2.4.1 Діаграма компонентів
Діаграма на рисунку 3.9 представляє архітектуру системи, що складається
з кількох компонентів, які взаємодіють через REST API інтерфейс. Основний
компонент — це "Backend", який включає в себе різні сервіси: "Customer
Service", "Category Service", "Product Service" та "Order Service". Кожен з цих
сервісів відповідає за певну функціональність системи, наприклад, управління
59
ЧДТУ 242322.009 ПЗ
клієнтами, категоріями, продуктами та замовленнями відповідно. Всі ці сервіси
взаємодіють з базою даних, що забезпечує зберігання та отримання необхідної
інформації.
Сервіси взаємодіють між собою та з базою даних через чітко визначені
інтерфейси, що дозволяє забезпечити модульність та гнучкість системи. Це
дозволяє легко оновлювати або замінювати окремі компоненти без впливу на
інші частини системи. REST API інтерфейс забезпечує зовнішню взаємодію з
системою, дозволяючи клієнтам надсилати запити та отримувати відповіді у
стандартному форматі. Така архітектура сприяє масштабованості та підтримці
системи, роблячи її зручною для розробки та обслуговування.
Рис. 3.9 - Діаграма компонентів
3.2.4.2 Розгортання програмної системи на апаратних засобах. Діаграма
розгортання
Діаграма на рисунку 3.10 ілюструє процес розгортання додатка в хмарі
AWS. Розробник контейнеризує додаток за допомогою Docker і завантажує
образ в Amazon ECR (Elastic Container Registry). Звідти Amazon ECS (Elastic
Container Service) витягує образ для розгортання. ECS використовує AWS
60
ЧДТУ 242322.009 ПЗ
Fargate для безсерверного управління контейнерами, що дозволяє автоматично
масштабувати додаток без необхідності керувати серверами.
Користувачі взаємодіють з додатком через інтернет, надсилаючи
HTTP-запити, які проходять через шлюз і потрапляють на Application Load
Balancer. Балансувальник розподіляє навантаження між контейнерами,
розгорнутими в публічній підмережі VPC (Virtual Private Cloud). Додаток
взаємодіє з базою даних, розміщеною в Amazon RDS (Relational Database
Service), яка знаходиться в приватній підмережі, забезпечуючи безпечний
доступ через JDBC.
Рис. 3.10 - Діаграма розгортання
3.2.5 Моделювання поведінки системи
Далі будуть наведені діаграми повʼязані з моделюванням поведінки системи.
3.2.5.1 Діаграма діяльності
На рисунку 3.11 процес починається з отримання запиту від користувача.
Сервіс готує параметри для пошуку, виконує запит до бази даних, знаходить всі
відповідні замовлення, перетворює їх у зручний формат і повертає користувачу.
61
ЧДТУ 242322.009 ПЗ
Рис. 3.11 - Діаграма діяльності для отримання замовлень
На рисунку 3.12 процес починається з отримання нового замовлення.
Сервіс перевіряє унікальність продуктів, знаходить їх у базі даних, створює
нове замовлення, додає продукти, оновлює їх кількість на складі, зберігає
замовлення і повертає підтвердження користувачу.
Рис. 3.12 - Діаграма діяльності для створення замовлення
62
ЧДТУ 242322.009 ПЗ
На рисунку 3.13 процес починається з отримання запиту на оновлення
замовлення. Сервіс перевіряє унікальність продуктів, знаходить замовлення,
перевіряє можливість його оновлення, оновлює дані замовлення та кількість
продуктів на складі, зберігає зміни і повертає оновлене замовлення користувачу.
Рис. 3.13 - Діаграма діяльності для оновлення замовлення
На рисунку 3.14 починається з отримання запиту на зміну статусу. Сервіс
перевіряє можливість зміни статусу, і якщо це можливо, оновлює його. Якщо
новий статус — "Скасовано", продукти повертаються на склад. Після цього
зміни зберігаються.
63
ЧДТУ 242322.009 ПЗ
Рис. 3.14 - Діаграма діяльності для оновлення статусу замовлення
На рисунку 3.15 процес починається з отримання запиту на видалення.
Сервіс знаходить замовлення в базі даних, видаляє його і підтверджує
видалення користувачу.
Рис. 3.15 - Діаграма діяльності для видалення замовлення
64
ЧДТУ 242322.009 ПЗ
На рисунку 3.16 діаграма активності показує, як створюється нова
категорія. Спочатку ми отримуємо інформацію про категорію, яку потрібно
створити. Потім ми перетворюємо цю інформацію у форму, з якою може
працювати система. Далі ми встановлюємо час, коли категорія була створена і
оновлена. Після цього ми зберігаємо всю цю інформацію в базі даних і
повертаємо інформацію про створену категорію.
Рис. 3.16 - Діаграма діяльності для створення категорії
На рисунку 3.17 діаграма активності описує, як ми отримуємо
інформацію про категорію за її унікальним ідентифікатором. Спочатку ми
отримуємо цей ідентифікатор, потім шукаємо відповідну категорію в базі даних.
Після цього ми перетворюємо знайдену інформацію у зручний для користувача
формат і повертаємо її.
65
ЧДТУ 242322.009 ПЗ
Рис. 3.17 - Діаграма діяльності для отримання категорії
На рисунку 3.18 діаграма активності описує, як ми оновлюємо
інформацію про категорію. Спочатку ми отримуємо унікальний ідентифікатор
категорії та нову інформацію про неї. Потім ми знаходимо категорію в базі
даних, оновлюємо її дані, зберігаємо зміни і повертаємо оновлену інформацію у
зручному форматі.
Рис. 3.18 - Діаграма діяльності для оновлення категорії
66
ЧДТУ 242322.009 ПЗ
На рисунку 3.19 діаграма активності показує, як створюється новий
продукт. Спочатку ми отримуємо інформацію про продукт, яку потрібно
створити. Потім ми перетворюємо цю інформацію у форму, з якою може
працювати система. Далі ми встановлюємо час, коли продукт був створений і
оновлений, знаходимо категорію, зберігаємо продукт у базі даних, оновлюємо
кеш і повертаємо інформацію про створений продукт.
Рис. 3.19 - Діаграма діяльності для створення продукту
На рисунку 3.20 діаграма активності описує, як ми отримуємо
інформацію про продукт за його унікальним ідентифікатором. Спочатку ми
отримуємо цей ідентифікатор, потім шукаємо відповідний продукт у базі даних.
Після цього ми перетворюємо знайдену інформацію у зручний для користувача
формат і повертаємо її.
67
ЧДТУ 242322.009 ПЗ
Рис. 3.20 - Діаграма діяльності для отримання продукта
На рисунку 3.21 діаграма активності описує, як ми оновлюємо
інформацію про продукт. Спочатку ми отримуємо унікальний ідентифікатор
продукту та нову інформацію про нього. Потім ми знаходимо продукт у базі
даних, оновлюємо його дані, зберігаємо зміни і повертаємо оновлену
інформацію у зручному форматі.
Рис. 3.21 - Діаграма діяльності для оновлення продукта
68
ЧДТУ 242322.009 ПЗ
На рисунку 3.22 діаграма активності описує, як ми видаляємо продукт.
Спочатку ми отримуємо унікальний ідентифікатор продукту, потім знаходимо
його у базі даних і видаляємо.
Рис. 3.22 - Діаграма діяльності для видалення продукта
На рисунку 3.23 діаграма активності показує, як створюється новий
клієнт. Спочатку ми отримуємо інформацію про клієнта, яку потрібно створити.
Потім ми перетворюємо цю інформацію у форму, з якою може працювати
система. Далі ми встановлюємо час, коли клієнт був створений і оновлений,
зберігаємо клієнта у базі даних і повертаємо інформацію про створеного
клієнта.
Рис. 3.23 - Діаграма діяльності для створення клієнта
69
ЧДТУ 242322.009 ПЗ
На рисунку 3.24 діаграма активності описує, як ми отримуємо
інформацію про клієнта за його унікальним ідентифікатором. Спочатку ми
отримуємо цей ідентифікатор, потім шукаємо відповідного клієнта у базі даних.
Після цього ми перетворюємо знайдену інформацію у зручний для користувача
формат і повертаємо її.
Рис. 3.24 - Діаграма діяльності для отримання клієнта
На рисунку 3.25 діаграма активності описує, як ми оновлюємо
інформацію про клієнта. Спочатку ми отримуємо унікальний ідентифікатор
клієнта та нову інформацію про нього. Потім ми знаходимо клієнта у базі
даних, оновлюємо його дані, зберігаємо зміни і повертаємо оновлену
інформацію у зручному форматі.
70
ЧДТУ 242322.009 ПЗ
Рис. 3.25 - Діаграма діяльності для оновлення клієнта
На рисунку 3.26 діаграма активності описує, як ми видаляємо клієнта.
Спочатку ми отримуємо унікальний ідентифікатор клієнта, потім видаляємо
його з бази даних.
Рис. 3.26 - Діаграма діяльності для видалення клієнта
3.2.5.2 Діаграма послідовності
На рисунку 3.27 користувач надсилає запит на отримання списку
замовлень. Сервіс обробляє цей запит, отримуючи параметри для пошуку, і
звертається до бази даних, щоб знайти відповідні замовлення. Після цього дані
перетворюються у зручний формат і повертаються користувачу.
71
ЧДТУ 242322.009 ПЗ
Рис. 3.27 - Діаграма послідовності для отримання всіх замовлень
На рисунку 3.28 користувач надсилає нове замовлення, сервіс перевіряє,
чи всі продукти унікальні, і знаходить їх у базі даних. Якщо всі продукти
знайдені, створюється нове замовлення, додаються продукти, оновлюється їх
кількість на складі, і замовлення зберігається. Користувач отримує
підтвердження про успішне створення замовлення.
Рис. 3.28 - Діаграма послідовності для створення замовлення
Рисунок 3.29 починається з отримання оновлення замовлення від
користувача. Сервіс перевіряє унікальність продуктів, знаходить замовлення в
базі даних і перевіряє можливість його оновлення. Після цього оновлюються
дані замовлення та кількість продуктів на складі, і результат повертається
користувачу.
72
ЧДТУ 242322.009 ПЗ
Рис. 3.29 - Діаграма послідовності для оновлення замовлення
На рисунку 3.30 користувач запитує зміну статусу замовлення. Сервіс
перевіряє, чи можлива зміна статусу, і якщо так, оновлює його. Якщо новий
статус — "Скасовано", продукти повертаються на склад. Після цього зміни
зберігаються.
Рис. 3.30 - Діаграма послідовності для оновлення статусу замовлення
Рисунок 3.31 починається з отримання запиту на видалення замовлення.
Сервіс знаходить замовлення в базі даних і видаляє його, підтверджуючи
видалення користувачу.
73
ЧДТУ 242322.009 ПЗ
Рис. 3.31 - Діаграма послідовності для видалення замовлення
Діаграма на рисунку 3.32 ілюструє процес створення нової категорії.
Користувач ініціює створення, надаючи інформацію про категорію. Сервіс
перетворює цю інформацію у внутрішній формат, встановлює час створення та
оновлення, зберігає в базі даних. Нарешті, збережена інформація
перетворюється назад у зручний формат і повертається користувачу.
Рис. 3.32 - Діаграма послідовності для створення категорій
74
ЧДТУ 242322.009 ПЗ
Діаграма на рисунку 3.33 показує процес отримання категорії за її ID.
Користувач запитує категорію, сервіс отримує її з бази даних, перетворює в
зручний формат і повертає користувачу.
Рис. 3.33 - Діаграма послідовності для отримання категорії
Діаграма на рисунку 3.34 послідовності деталізує процес оновлення
категорії. Користувач надає ID та нову інформацію про категорію, сервіс
отримує існуючу категорію, оновлює її дані, зберігає та повертає оновлену
інформацію у зручному форматі.
Рис. 3.34 - Діаграма послідовності для оновлення категорії
75
ЧДТУ 242322.009 ПЗ
Діаграма на рисунку 3.35 ілюструє процес створення нового продукту.
Користувач ініціює створення, надаючи інформацію про продукт. Сервіс
перетворює цю інформацію у внутрішній формат, встановлює час створення та
оновлення, знаходить категорію, зберігає продукт у базі даних, оновлює кеш і
повертає інформацію про створений продукт.
Рис. 3.35 - Діаграма послідовності для створення продуктів
Діаграма на рисунку 3.36 показує процес отримання продукту за його ID.
Користувач запитує продукт, сервіс отримує його з бази даних, перетворює в
зручний формат і повертає користувачу.
Рис. 3.36 - Діаграма послідовності для отримання продукта
76
ЧДТУ 242322.009 ПЗ
Діаграма на рисунку 3.37 деталізує процес оновлення продукту.
Користувач надає ID та нову інформацію про продукт, сервіс отримує існуючий
продукт, оновлює його дані, зберігає та повертає оновлену інформацію у
зручному форматі.
Рис. 3.37 - Діаграма послідовності для оновлення продукта
Діаграма на рисунку 3.38 показує процес видалення продукту. Користувач
ініціює видалення, надаючи ID продукту. Сервіс знаходить продукт у базі даних
і видаляє його.
Рис. 3.38 - Діаграма послідовності для видалення продукта
77
ЧДТУ 242322.009 ПЗ
Діаграма на рисунку 3.39 ілюструє процес створення нового клієнта.
Користувач ініціює створення, надаючи інформацію про клієнта. Сервіс
перетворює цю інформацію у внутрішній формат, встановлює час створення та
оновлення, зберігає клієнта у базі даних і повертає інформацію про створеного
клієнта.
Рис. 3.39 - Діаграма послідовності для створення клієнта
Діаграма на рисунку 3.40 показує процес отримання клієнта за його ID.
Користувач запитує клієнта, сервіс отримує його з бази даних, перетворює в
зручний формат і повертає користувачу.
Рис. 3.40 - Діаграма послідовності для отримання клієнта
78
ЧДТУ 242322.009 ПЗ
Діаграма на рисунку 3.41 деталізує процес оновлення клієнта. Користувач
надає ID та нову інформацію про клієнта, сервіс отримує існуючого клієнта,
оновлює його дані, зберігає та повертає оновлену інформацію у зручному
форматі.
Рис. 3.41 - Діаграма послідовності для оновлення клієнта
Діаграма на рисунку 3.42 діаграма послідовності показує процес
видалення клієнта. Користувач ініціює видалення, надаючи ID клієнта. Сервіс
видаляє клієнта з бази даних.
Рис. 3.42 - Діаграма послідовності для видалення клієнта
79
ЧДТУ 242322.009 ПЗ
3.2.5.3 Діаграма комунікації
Діаграма на рисунку 3.43 демонструє комунікацію системи, що
складається з контролерів, сервісів, репозиторіїв та кешу. Контролери
взаємодіють з клієнтом через REST API. Наприклад, OrderController обробляє
запити на створення замовлень, ProductController — на отримання інформації
про товари, CustomerController — на створення клієнтів, а CategoryController —
на отримання категорій. Сервіси реалізують бізнес-логіку. OrderService
відповідає за створення замовлень і перевірку наявності товарів. ProductService
керує товарами, включаючи кешування та перевірку наявності. CustomerService
та CategoryService обробляють відповідні запити. Репозиторії взаємодіють з
базою даних для збереження та отримання даних. Наприклад, OrderRepo
зберігає замовлення, а ProductRepo — товари. Кеш використовується для
зберігання часто запитуваних даних, щоб зменшити навантаження на базу
даних. Ця структура забезпечує чіткий розподіл відповідальностей між
компонентами системи.
Рис. 3.43 - Діаграма комунікації системи
80
ЧДТУ 242322.009 ПЗ
3.2.5.4 Діаграма скінченного автомату
Діаграма на рисунку 3.44 відображає життєвий цикл замовлення,
починаючи зі створення і закінчуючи доставкою. Спочатку замовлення
створюється і переходить у стан "Очікує оплати". Після отримання оплати
статус змінюється на "Оплачено", а потім на "В обробці". Якщо замовлення
оброблено, воно переходить у стан "Відправлено". Після доставки статус
змінюється на "Доставлено", що завершує процес. Замовлення може бути
скасовано на будь-якому етапі, що призводить до повернення товару на склад і
оновлення кількості. Діаграма також включає перевірки, такі як унікальність
продуктів, наявність на складі та можливість зміни статусу.
Рис. 3.44 - Діаграма скінченного автомату функціоналу замовлень
81
ЧДТУ 242322.009 ПЗ
Висновок до третього розділу
У цьому розділі було розглянуто основні аспекти проектування системи
інтернет-магазину. Було створено модель предметної області, яка відображає
ключові сутності, їхні атрибути та взаємозв'язки. Завдяки використанню мови
моделювання UML були створені діаграми класів, пакетів, компонентів та
розгортання, що забезпечують структуроване уявлення про архітектуру
системи.
Описані діаграми прецедентів дозволили визначити функціональні
можливості системи та розподіл ролей користувачів. Діаграми діяльності та
послідовності деталізували роботу основних бізнес-процесів, включаючи
управління замовленнями, продуктами, категоріями та клієнтами.
Проектування системи враховує основні принципи модульності,
масштабованості та підтримуваності, що дозволяє легко розширювати
функціонал у майбутньому. Завдяки чітко визначеній логічній та фізичній
архітектурі забезпечується ефективна взаємодія між компонентами, знижується
складність підтримки та поліпшується продуктивність системи.
82
ЧДТУ 242322.009 ПЗ
РОЗДІЛ 4 РОЗРОБКА ТА ТЕСТУВАННЯ ПРОГРАМНОГО
ЗАБЕЗПЕЧЕННЯ
4.1 Розробка програмного комплексу
Далі розглянемо процес побудови програмного комплексу на основі
обраних технологій.
4.1.1 Обґрунтування вибору засобів реалізації
Мова програмування
Для реалізації поставленої задачі було обрано мову програмування Java. Її
популярність у сфері веб-розробки (а також в інших областях) зумовлена
великим ком’юніті, що спрощує пошук рішень для багатьох проблем. Завдяки
активній спільноті Java має широкий вибір фреймворків, бібліотек і технологій
для розробки різноманітних програм, серед яких Spring Framework,
використаний у цьому проєкті.
Java є кросплатформною мовою, що означає її незалежність від
операційної системи. Завдяки виконанню коду у віртуальній машині зібрана
програма може працювати на різних пристроях [19].
Ця мова програмування водночас проста для початківців і потужна для
створення складних програмних систем. Підтримка об’єктно-орієнтованого
програмування також є однією з причин вибору Java для розробки.
Фреймворк
Spring Framework — один із найпопулярніших фреймворків для серверної
розробки на Java. Він складається з численних модулів, кожен з яких пропонує
потужні інструменти для вирішення різноманітних завдань, поставлених у
рамках цього проєкту [20].
Spring Data Access забезпечує зручну роботу з базою даних,
автоматизуючи управління транзакціями [21].
Spring Security надає засоби для аутентифікації, авторизації та захисту від
поширених атак. У цьому проєкті він дозволив легко реалізувати логін за
допомогою JWT.
83
ЧДТУ 242322.009 ПЗ
Spring MVC [22] підтримує розробку веб-додатків за шаблоном MVC,
виконуючи обробку HTTP-запитів [23], визначення відповідних контролерів та
іншу пов’язану логіку.
Spring Test спрощує написання як модульних, так і інтеграційних тестів.
Він включає інструменти для створення моків, використання команд перевірки
(assert) [24] та дозволяє частково або повністю запускати додаток залежно від
потреб тестування.
СУБД
Під час розробки було обрано PostgreSQL — систему управління базами
даних із відкритим вихідним кодом, яка забезпечує надійне збереження даних
[14]. Вона пропонує:
– підтримку стандартів SQL і сумісність із багатьма системами;
– широкий набір вбудованих типів даних;
– підтримку транзакцій, що гарантує коректність і цілісність даних;
– активну спільноту розробників, яка сприяє постійному розвитку
системи;
– зручну та детальну документацію, що дозволяє швидко розпочати її
використання.
ORM
Hibernate — це фреймворк для об'єктно-реляційного відображення
(ORM), який спрощує взаємодію з базою даних, дозволяючи працювати з нею
через Java-об'єкти, що відповідають сутностям бази даних (entity).
Фреймворк значно зменшує обсяг SQL-коду, оскільки більшість типових
операцій вже реалізовані. Робота з базою даних здійснюється через
об'єктно-орієнтований підхід, що полегшує розробку.
Hibernate також забезпечує захист від SQL-ін'єкцій, а його архітектура
дозволяє легко переносити код між різними системами управління базами даних
без необхідності змінювати логіку взаємодії [25].
Система контролю версій
84
ЧДТУ 242322.009 ПЗ
Git — популярна система контролю версій, яка пропонує широкий набір
переваг [26]:
– дозволяє зберігати копії проєкту на декількох робочих пристроях;
– забезпечує високу швидкість роботи навіть з великими проєктами та
файлами;
– підтримує створення окремих гілок для одночасної роботи над
різними частинами проєкту;
– зберігає повну історію змін, що дозволяє відстежувати, хто, коли та які
зміни вносив;
– сумісний із різними операційними системами та інтегрується з
іншими інструментами розробки;
– дозволяє синхронізувати локальні зміни з віддаленими репозиторіями,
що спрощує командну роботу.
Платформа для розміщення коду
Github - це сервіс для хостингу репозиторіїв Git та керування ними, який
має безліч переваг [27]:
– дозволяє вести спільну розробку та зливати код з різних гілок в
головну за допомогою такого поняття, як pull request - запит на злиття
гілки;
– стандартний функціонал дозволяє створювати дошки з завданнями,
призначати їм статуси, додавати описи, призначати спринти і все це
безкоштовно;
– інтегрується з середовищем розробки Intellij IDEA, що дає можливість
конфігурувати та керувати репозиторієм в редакторі;
– має дуже зручний інтерфейс для перевірки змін в коді при проведенні
pull request, підсвічуючи змінені, видалені, додані ділянки коду, файли;
дозволяє залишати коментарі, зауваження щодо змін.
Середовище розробки
Для розробки проєкту використовувалось інтегроване середовище Intellij
IDEA [28] — потужний редактор коду для Java. Воно забезпечує швидку
85
ЧДТУ 242322.009 ПЗ
розробку завдяки автодоповненню, рефакторингу, аналізу коду та автоматичній
генерації структур, таких як if або try-catch.
IDE підтримує роботу з фреймворками, зокрема Spring, інтеграцію з
базами даних (наприклад, PostgreSQL), що дозволяє виконувати запити та
керувати таблицями безпосередньо в редакторі.
Intellij IDEA також інтегрується з Git і GitHub, спрощуючи спільну
розробку: управління репозиторіями, комітами, гілками та змінами виконується
швидко і зручно.
Система збирання
Для збирання проєкту використовується Gradle [29] — інструмент, що
працює на JVM, забезпечуючи зручність для Java-розробників. Gradle
підтримує додавання власної логіки, задач і плагінів завдяки простому
синтаксису, а кросплатформність JVM дозволяє запускати його на різних
системах.
IntelliJ IDEA інтегрується з Gradle, надаючи графічний інтерфейс для
запуску етапів збирання та моніторингу його статусу. Швидкість збирання
забезпечується використанням кешів і уникненням повторного виконання задач.
4.1.2 Опис структурної (функціональної) схеми
Рис. 4.1 - Функціональна схема системи
86
ЧДТУ 242322.009 ПЗ
Рис. 4.2 - Структурна схема системи
Підсистема Order (Замовлення)
Керує життєвим циклом замовлень від створення до доставки. Забезпечує
управління статусами, валідацію замовлень та взаємодію з підсистемами
продуктів і клієнтів. Включає розширений пошук та фільтрацію замовлень за
різними параметрами.
Підсистема Product (Продукти)
Відповідає за управління каталогом товарів. Має функції пошуку з
фільтрацією за категоріями, цінами та кількістю. Використовує кешування для
оптимізації роботи та включає контроль залишків товарів.
Підсистема Customer (Клієнти)
Забезпечує управління базою клієнтів з можливістю пошуку за різними
критеріями. Включає валідацію даних клієнтів та відстеження історії змін через
дати створення та оновлення записів.
Підсистема Category (Категорії)
Найпростіша підсистема, що забезпечує управління категоріями
продуктів. Надає базові CRUD операції та використовується як довідник для
класифікації товарів у каталозі.
4.1.3 Опис логічної схеми системи
Система побудована за архітектурним шаблоном MVC
(Model-View-Controller) (Рис. 4.3), який розділяє бізнес-логіку, дані та їх
відображення. Схема представлена на рисунку 3.24.
– Model відповідає за структуру, доступ до даних і їх оновлення.
– View визначає вигляд і відображення даних.
87
ЧДТУ 242322.009 ПЗ
– Controller обробляє дії користувача та оновлює дані.
У проєкті цей принцип реалізується таким чином:
Контролерами слугують класи з анотацією @Controller [20], які
отримують запити на певні URL, обробляють дані користувача та передають їх
у моделі.
Моделі представлені:
– класами з анотацією @Entity, які відповідають за сутності бази даних;
– класами з анотацією @Repository, які виконують операції з базою
даних (INSERT, UPDATE, SELECT, DELETE) [20].
У серверній частині, яка надає REST API [30], роль представлення
виконує JSON-дані, що генеруються з моделей.
Рис. 4.3 - Логічна схема системи на основі MVC
Між контролером і моделлю виступають зв'язуючі класи з анотацією
@Service. Вони приймають дані від контролера, оновлюють моделі або
отримують необхідну інформацію з них, повертаючи результат користувачеві у
вигляді DTO (Data Transfer Object).
DTO — це прості Java-об’єкти, що не містять бізнес-логіки, а лише
забезпечують передачу даних між сервером і користувачем. Дані, отримані у
форматі JSON, сервер серіалізує в DTO. Контролер передає DTO у сервіс, де
88
ЧДТУ 242322.009 ПЗ
його перетворюють в Entity для подальшого збереження через репозиторій у
базу даних (Рис. 4.4) [31].
При операціях читання контролер викликає метод сервісу, який
звертається до репозиторія. Репозиторій отримує дані з таблиці у вигляді Entity,
які сервіс трансформує в DTO і повертає користувачеві. На сервері DTO знову
десеріалізується у формат JSON.
Рис. 4.4 - Потік даних в системі (MVC шаблон)
4.1.4 Розробка бази даних
Логічні моделі даних
customer (Клієнт)
– id: UUID, первинний ключ, генерується автоматично
– first_name: VARCHAR(255), ім'я клієнта, обмеження - NOT NULL
– last_name: VARCHAR(255), прізвище клієнта, обмеження - NOT NULL
– email: VARCHAR(255), електронна пошта, обмеження - NOT NULL,
UNIQUE
– created_at: TIMESTAMP, дата створення, значення за замовчуванням -
CURRENT_TIMESTAMP
– updated_at: TIMESTAMP, дата оновлення, значення за замовчуванням -
CURRENT_TIMESTAMP
category (Категорія)
– id: UUID, первинний ключ, генерується автоматично
– name: VARCHAR(255), назва категорії, обмеження - NOT NULL,
UNIQUE
– created_at: TIMESTAMP, дата створення, значення за замовчуванням -
CURRENT_TIMESTAMP
89
ЧДТУ 242322.009 ПЗ
– updated_at: TIMESTAMP, дата оновлення, значення за замовчуванням -
CURRENT_TIMESTAMP
product (Продукт)
– id: UUID, первинний ключ, генерується автоматично
– name: VARCHAR(255), назва продукту, обмеження - NOT NULL,
UNIQUE
– description: TEXT, опис продукту
– quantity: INT, кількість, обмеження - NOT NULL, CHECK (quantity >=
0)
– price: DECIMAL(10,2), ціна, обмеження - NOT NULL, CHECK (price
>= 0)
– category_id: UUID, зовнішній ключ на таблицю Category
– created_at: TIMESTAMP, дата створення, значення за замовчуванням -
CURRENT_TIMESTAMP
– updated_at: TIMESTAMP, дата оновлення, значення за замовчуванням -
CURRENT_TIMESTAMP
order (Замовлення)
– id: UUID, первинний ключ, генерується автоматично
– customer_id: UUID, зовнішній ключ на таблицю Customer, обмеження -
NOT NULL
– status: VARCHAR(255), статус замовлення, обмеження - NOT NULL
– created_at: TIMESTAMP, дата створення, значення за замовчуванням -
CURRENT_TIMESTAMP
– updated_at: TIMESTAMP, дата оновлення, значення за замовчуванням -
CURRENT_TIMESTAMP
order_item (Позиція замовлення)
– id: UUID, первинний ключ, генерується автоматично
– order_id: UUID, зовнішній ключ на таблицю Order, обмеження - NOT
NULL
90
ЧДТУ 242322.009 ПЗ
– product_id: UUID, зовнішній ключ на таблицю Product, обмеження -
NOT NULL
– quantity: INT, кількість, обмеження - NOT NULL, CHECK (quantity >=
0)
– price: DECIMAL(10,2), ціна, обмеження - NOT NULL, CHECK (price
>= 0)
– created_at: TIMESTAMP, дата створення, значення за замовчуванням -
CURRENT_TIMESTAMP
– updated_at: TIMESTAMP, дата оновлення, значення за замовчуванням -
CURRENT_TIMESTAMP
product_image (Зображення продукту)
– id: UUID, первинний ключ, генерується автоматично
– content: BYTEA, зображення, обмеження - NOT NULL
– content_type: VARCHAR(255), тип контенту, обмеження - NOT NULL
– product_id: UUID, зовнішній ключ на таблицю Product
– created_at: TIMESTAMP, дата створення, значення за замовчуванням -
CURRENT_TIMESTAMP
– updated_at: TIMESTAMP, дата оновлення, значення за замовчуванням -
CURRENT_TIMESTAMP
product_rating (Рейтинг продукту)
– id: UUID, первинний ключ, генерується автоматично
– product_id: UUID, зовнішній ключ на таблицю Product, обмеження -
NOT NULL
– rating: INT, оцінка, обмеження - NOT NULL, CHECK (rating >= 1 AND
rating <= 5)
– created_at: TIMESTAMP, дата створення, значення за замовчуванням -
CURRENT_TIMESTAMP
– updated_at: TIMESTAMP, дата оновлення, значення за замовчуванням -
CURRENT_TIMESTAMP
91
ЧДТУ 242322.009 ПЗ
Фізична модель даних
Рис. 4.5 - Фізична модель частини бази даних системи деканату
92
ЧДТУ 242322.009 ПЗ
4.1.5 Розробка інтерфейсу користувача
Розробка серверної частини не передбачає графічного інтерфейсу.
Інтерфейсом будемо розглядати створені ендпоїнти, описані за допомгою
OpenAPI специфікації (Рис. 4.6), які можна імпортувати в клієнт для виконання
запитів і подальшого використання. В проекті також додано конфігурацію
Swagger, що дозволяє тестувати створені API [32].
Рис. 4.6 - Інтерфейс побудованого API, візуалізований за допомогою Swagger
4.1.6 Опис розробки програмних компонентів
Система складається з чотирьох основних сервісних компонентів:
OrderService, ProductService, CustomerService та CategoryService. Кожен з них
відповідає за управління відповідною бізнес-сутністю та реалізує патерн
Repository для роботи з базою даних.
OrderService забезпечує повний життєвий цикл замовлень. При створенні
замовлення (метод create) відбувається валідація наявності товарів, перевірка
унікальності продуктів у замовленні та оновлення складських залишків. Метод
93
ЧДТУ 242322.009 ПЗ
update дозволяє модифікувати існуюче замовлення з автоматичним
перерахунком кількості товарів на складі. Особливу увагу приділено методу
updateStatus, який керує зміною статусів замовлення та, у випадку скасування,
повертає товари на склад. Система підтримує розширений пошук замовлень за
різними критеріями через метод getAll, включаючи фільтрацію за статусом,
клієнтом, продуктом та часовими рамками.
ProductService реалізує управління товарами з використанням кешування
для оптимізації продуктивності. Методи create та update автоматично
оновлюють кеш при змінах. Сервіс підтримує складну систему фільтрації
товарів за категоріями, ціновим діапазоном, кількістю та іншими параметрами.
Особливістю є використання анотацій @Cacheable, @CachePut та @CacheEvict
для ефективного управління кешем.
CustomerService забезпечує базові операції з клієнтами та їх даними.
Реалізовано пошук клієнтів за різними параметрами, включаючи ім'я, email та
часові межі створення/оновлення записів. Всі операції зміни даних автоматично
оновлюють часові мітки через механізм аудиту.
CategoryService представляє найпростіший компонент системи, що
забезпечує управління категоріями товарів. Реалізує базові CRUD-операції та
використовується як довідник для класифікації товарів.
Всі компоненти використовують єдиний підхід до обробки помилок через
спеціалізовані винятки (NotFoundException, UnprocessableEntityException), що
забезпечує уніфіковану обробку помилкових ситуацій. Система підтримує
автоматичне відстеження дат створення та оновлення записів через тригери
бази даних, що реалізовано в міграційних файлах (Рис. 4.7) в папці ресурсів.
Рис. 4.7 - Папка з міграціями
94
ЧДТУ 242322.009 ПЗ
4.2 Тестування системи
Далі буде описано процес тестування системи та підходи, які були
використані для досягнення максимально стабільної та перевіреної системи.
4.2.1 Модульне тестування
Для написання модульних тестів було використано бібліотеки JUnit [33],
AssertJ та функціонал Spring фреймоворка. Результатом модульного тестування
є згенерований Jacoco звіт, який показує покриття модулів тестами [34].
Рис. 4.8 - Результат unit-тестів для модуля конвертації entity в DTO
Рис. 4.9 - Результат unit-тестів для модуля з exception класами
Рис. 4.10 - Результат unit-тестів для модуля сервісів
95
ЧДТУ 242322.009 ПЗ
Рис. 4.11 - Результат unit-тестів для модуля контролерів
4.2.2 Інтеграційне тестування
Результатом інтеграційного тестування є JaCoCo звіт [34], який показує
загальне покриття коду кожного модуля системи.
Рис. 4.12 - Покриття тестами інтеграційного тестування
Рис. 4.13 - Результати інтеграційного тестування
4.2.3 Системне тестування
Системне тестування передбачає тестування всієї системи в цілому. В
даному випадку компонентами системи є серверна частина та база даних. Для
взаємодії з базою даних було використано бібліотеку Testcontainers [35], яка
дозволяє створювати базу даних та запускати її в Docker [36] для кожного класу
96
ЧДТУ 242322.009 ПЗ
з тестами. Таким чином досягається ізоляція кожного набору тестів конкретного
модуля, а також перевіряється поведінка системи при взаємодії з реальною
базою даних [37].
Рис. 4.14 - Підключення тестової залежності Testcontainers
Рис. 4.15 - Клас AbstractPostgresTests для запуску контейнера БД
Кожен клас з тестами наслідується від базового класу, який в свою чергу
займається конфігурацією та запуском контейнера з базою даних. Для
97
ЧДТУ 242322.009 ПЗ
тестування всієї системи піднімається сервер, якому можна відправляти запити
за допомогою MockMvc біна та отримувати відповідь, а також конфігурувати
очікувані результати.
Рис. 4.16 - Наслідування AbstractPostgresTests класу
Рис. 4.17 - Відправка запиту на контролер за допомогою класу MockMvc
98
ЧДТУ 242322.009 ПЗ
Таблиця 4.1
Результати системного тестування
Завдання тестування Очікуваний результат Отриманий результат
Перевірка створення Замовлення створюється з Замовлення створено
успішно
нового замовлення валідними параметрами
Перевірка отримання Повертається замовлення з Повертається
замовлення за відповідним id очікуване замовлення
ідентифікатором.
Перевірка оновлення Замовлення успішно Дані замовлення
деталей замовлення оновлюється з новими оновлені
даними
Перевірка видалення Замовлення успішно Замовлення видалене
замовлення за видаляється
ідентифікатором
Перевірка коректної Список замовлень Повертається
роботи пагінації при повертається згідно з коректна сторінка
отриманні списку заданими параметрами списку замовлень
замовлень сторінки
Перевірка роботи кешу Повернення замовлення з Запит до бази даних
при повторному запиті кешу без додаткового не здійснюється,
одного й того ж запиту до бази даних замовлення
замовлення повернуто з кешу
Перевірка стратегії Дані завантажуються Дані завантажуються
Fetch при завантаженні відповідно до обраної згідно з
зв’язаних сутностей стратегії (eager/lazy) очікуваннями
99
ЧДТУ 242322.009 ПЗ
4.2.4 Приймальне тестування
Рис. 4.18 - ProductCreateLoadTest клас - результат навантажувального
тестування
Рис. 4.19 - CustomerCreateLoadTest клас - результат навантажувального
тестування
100
ЧДТУ 242322.009 ПЗ
Рис. 4.20 - OrderCreateLoadTest клас - результат навантажувального тестування
Рис. 4.21 - CategoryGetLoadTest клас - результат навантажувального тестування
101
ЧДТУ 242322.009 ПЗ
Рис. 4.22 - CustomerGetLoadTest клас - результат навантажувального тестування
Рис. 4.23 - OrderGetLoadTest клас - результат навантажувального тестування
102
ЧДТУ 242322.009 ПЗ
Рис. 4.24 - ProductGetLoadTest клас - результат навантажувального тестування
4.2.5 Реалізація та впровадження результатів
Результати роботи, а саме розроблена серверна частина, не була
розгорнута і впроваджена для реального використання на даний момент, але
планується зробити це в близькому майбутньому. Для цього можна використати
сервіс AWS, який називається AWS ESC (Elastic Container Service) [38] - це
повністю керована служба оркестровки контейнерів, яка допомагає легко
розгортати, керувати та масштабувати контейнерні програми. Будучи повністю
керованим сервісом, Amazon ECS постачається з вбудованою конфігурацією
AWS і найкращими практичними порадами. Він інтегрований як з AWS, так і зі
сторонніми інструментами, такими як Amazon Elastic Container Registry і
Docker. Завдяки цій інтеграції командам легше зосередитися на створенні
програм, а не на середовищі. Ви можете запускати та масштабувати свої
контейнерні робочі навантаження в регіонах AWS у хмарі та локально без
складнощів керування рівнем керування.
Відправка Docker-образу в ECR (Рис. 4.25):
aws ecr get-login-password --region <your-region> | docker login --username
AWS --password-stdin <account_id>.dkr.ecr.<your-region>.amazonaws.com
Додавання тегу на Docker-образ:
103
ЧДТУ 242322.009 ПЗ
docker tag your-app:latest
<account_id>.dkr.ecr.<your-region>.amazonaws.com/your-app-repo:latest
Завантаження образу в ECR:
docker push
<account_id>.dkr.ecr.<your-region>.amazonaws.com/your-app-repo:latest
Рис. 4.25 - Dockerfile [39]
Рис. 4.26 - Gradle задача [40] для побудови образу
104
ЧДТУ 242322.009 ПЗ
Висновок до четвертого розділу
Розроблений програмний комплекс демонструє сучасний підхід до
створення серверної частини веб-додатку для інтернет-магазину з
використанням перевірених технологій Java, Spring Boot, Hibernate та
PostgreSQL. Вибір цих інструментів обґрунтований їхньою популярністю,
потужними можливостями та підтримкою великої спільноти розробників.
Архітектура системи базується на шаблоні MVC, що забезпечує чітке
розділення логіки, доступу до даних і представлення. Реалізовано основні
підсистеми для управління клієнтами, продуктами, замовленнями та
категоріями, кожна з яких відповідає за конкретну бізнес-логіку. Використання
DTO та сервісного шару спрощує передачу даних між компонентами та
підтримує масштабованість.
Дані організовані у структурованій базі даних із логічними зв'язками та
обмеженнями, що забезпечує коректність і цілісність збереженої інформації.
Описані CRUD-операції, фільтрація, пошук і кешування дозволяють досягти
високої продуктивності навіть у великих системах.
Контроль версій реалізовано за допомогою Git та GitHub, що підтримує
командну розробку та управління репозиторіями. Інтеграція зі середовищем
IntelliJ IDEA та інструментом збирання Gradle сприяє ефективній розробці,
автоматизації процесів і підтримці проєкту.
Таким чином, створений програмний комплекс відповідає сучасним
вимогам до веб-додатків і демонструє добре спроєктовану архітектуру, що
дозволяє легко розширювати функціонал, підтримувати і модернізувати систему
в майбутньому.
Тестування є важливою складовою розробки програмного забезпечення,
що забезпечує стабільність, коректність та надійність роботи системи. У проєкті
були застосовані модульні, інтеграційні та навантажувальні тести. Для
модульного тестування використовувались бібліотеки JUnit та Mockito, що
дозволяють створювати тести для окремих класів та перевіряти їх
функціональність у ізольованому середовищі. Інтеграційні тести забезпечували
105
ЧДТУ 242322.009 ПЗ
перевірку коректної взаємодії між різними компонентами системи, включаючи
базу даних та зовнішні сервіси. Для навантажувального тестування
використовувався інструмент Gatling, який дозволив виміряти продуктивність
ключових ендпоїнтів системи за різних рівнів навантаження. Завдяки
комплексному підходу до тестування вдалося виявити та усунути більшість
критичних помилок ще на етапі розробки.
Процес впровадження системи включав розгортання програмного
забезпечення на сервері та налаштування необхідного середовища для його
стабільної роботи. Для автоматизації розгортання використовувалися
інструменти управління конфігураціями, такі як Docker та Docker Compose, що
забезпечили контейнеризацію додатку та його залежностей. Система бази даних
PostgreSQL була налаштована з урахуванням резервного копіювання та стратегії
відновлення.
Для забезпечення високої доступності та безперебійної роботи були
впроваджені механізми моніторингу та логування за допомогою таких
інструментів, як Prometheus і Grafana. Це дозволило контролювати основні
показники продуктивності, оперативно реагувати на можливі збої та
оптимізувати використання ресурсів. Завдяки ретельному тестуванню та
грамотній стратегії розгортання вдалося впровадити систему без серйозних
збоїв та забезпечити її готовність до роботи в умовах високого навантаження.
106
ЧДТУ 242322.009 ПЗ
ЗАГАЛЬНІ ВИСНОВКИ
У межах магістерської роботи було розроблено серверну частину
інтернет-магазину, яка забезпечує ефективну взаємодію між клієнтським
інтерфейсом та базою даних. Для реалізації було використано сучасні підходи
до розробки та оптимізації, що дозволило значно підвищити продуктивність і
масштабованість системи.
Основною метою було створення функціональної, надійної та
продуктивної системи, що відповідає потребам сучасного електронного бізнесу.
Для досягнення цієї мети розглянуто та впроваджено низку методів оптимізації.
Пагінація - забезпечила обмеження обсягу даних, що передаються між
сервером і клієнтом. Це зменшило навантаження на сервер та підвищило
швидкість обробки запитів.
Оптимізація запитів - було розглянуто потенційні способи оптимізації
роботи з базою даних та їх недоліки.
Fetch strategy - вибір правильних стратегій завантаження даних (eager та
lazy fetching) дав змогу зменшити кількість зайвих операцій з базою даних,
особливо для складних зв’язків між таблицями.
Кешування - впровадження кешів дозволило повторно використовувати
вже отримані дані без додаткових звернень до бази даних, що значно зменшило
час виконання запитів.
Індексація - створення індексів для полів, що часто використовуються в
запитах, суттєво пришвидшило пошук та сортування даних у базі.
Усі ці методи були інтегровані в проєкт з урахуванням специфіки
бізнес-логіки інтернет-магазину, що дало змогу створити систему з високою
продуктивністю навіть при зростанні обсягів даних та кількості користувачів.
Результати цієї роботи можуть бути корисними для розробників
серверних додатків, які прагнуть підвищити ефективність роботи своїх систем
та забезпечити позитивний досвід користувачів.
107
ЧДТУ 242322.009 ПЗ
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ
1 Database Performance Issues - [Електронний доступ] - Режим доступу:
https://www.loadview-testing.com/blog/5-most-common-database-perform
ance-issues-fixes/
2 SQL Queries Optimization - [Електронний доступ] - Режим доступу:
https://www.thoughtspot.com/data-trends/data-modeling/optimizing-sql-qu
eries
3 Scaling PostgreSQL for High Transaction Volumes - [Електронний
доступ] - Режим доступу:
https://kisztof.medium.com/scaling-postgresql-for-high-transaction-volume
s-in-fintech-a328759cba1d
4 Load Testing Tools - [Електронний доступ] - Режим доступу:
https://testguild.com/load-testing-tools/
5 Optimize SQL Query (Count) - [Електронний доступ] - Режим доступу:
https://www.brentozar.com/archive/2019/12/how-to-make-select-count-que
ries-crazy-fast/
6 Indexes - [Електронний доступ] - Режим доступу:
https://www.postgresql.org/docs/current/indexes.html
7 Redis - [Електронний доступ] - Режим доступу: https://redis.io/
8 Spring Caching - [Електронний доступ] - Режим доступу:
https://docs.spring.io/spring-boot/reference/io/caching.html
9 Prometheus - [Електронний ресурс] - Режим доступу:
https://prometheus.io/docs/introduction/overview/
10 Gatling Load Testing - [Електронний доступ] - Режим доступу:
https://docs.gatling.io/reference/script/core/simulation/
11 Robert C. Martin. Clean Code. (2009) Language: English 462 pages ISBN:
978-0-13-235088-4
12 Apache Cassandra - [Електронний доступ] - Режим доступу:
https://cassandra.apache.org/_/index.html
108
ЧДТУ 242322.009 ПЗ
13 Mongo DB - [Електронний доступ] - Режим доступу:
https://www.mongodb.com/
14 PostgreSQL - [Електронний ресурс] - Режим доступу:
https://www.postgresql.org/about/
15 Spring Pagination - [Електронний доступ] - Режим доступу:
https://www.baeldung.com/spring-data-jpa-pagination-sorting
16 Eager/Lazy Loading In Hibernate - [Електронний доступ] - Режим
доступу: https://www.baeldung.com/hibernate-lazy-eager-loading
17 PostgreSQL Triggers - [Електронний доступ] - Режим доступу:
https://www.postgresql.org/docs/current/sql-createtrigger.html
18 Методичні рекомендації до підготовки кваліфікаційної роботи
бакалавра для здобувачів вищої освіти зі спеціальності 121 «Інженерія
програмного забезпечення» усіх форм навчання [Текст] /Укл.: Голуб
С.В., Заспа Г.О., Півень О.Б., Катаєва Є.Ю., Салапатов В.І., Метелап
В.В., Олексюк В.В.; М-во освіти і науки України, Черкас, держ.
технол. ун-т. - Черкаси : ЧДТУ, 2023. - 97 с.
19 Herbert Schildt. Java. The complete reference; 11th edition (2019)
Language: English 1882 pages ISBN: 978-1-26-044024-9
20 Spring in Action. Craig S. Walls. Manning Publications; 5th edition (2019)
Language: English 521 pages ISBN: 978-1-61729-494-5
21 Spring Framework Features - [Електронний ресурс] - Режим доступу:
https://www.bmc.com/blogs/spring-framework/
22 MVC - [Електронний ресурс] - Режим доступу:
https://developer.mozilla.org/en-US/docs/Glossary/MVC
23 HTTP Protocol - [Електронний ресурс] - Режим доступу:
https://www.freecodecamp.org/news/what-is-http/
24 JUnit Assertions - [Електронний ресурс] - Режим доступу:
https://www.baeldung.com/junit-assertions
25 Hibernate - [Електронний ресурс] - Режим доступу:
https://hibernate.org/orm/
109
ЧДТУ 242322.009 ПЗ
26 Introduction to Git features - [Електронний ресурс] - Режим доступу:
https://blog.knoldus.com/introduction-to-git-features-and-commands/
27 GitHub Features - [Електронний ресурс] - Режим доступу:
https://github.com/features
28 IntelliJ IDEA Features - [Електронний ресурс] - Режим доступу:
https://www.jetbrains.com/idea/features/#features
29 Gradle - [Електронний ресурс] - Режим доступу:
https://www.postman.com/product/tools/
30 What is REST API? - [Електронний ресурс] - Режим доступу:
https://restfulapi.net/
31 DTO - [Електронний ресурс] - Режим доступу:
https://snyk.io/blog/how-to-use-java-dtos/
32 Swagger - [Електронний доступ] - Режим доступу:
https://swagger.io/specification/
33 JUnit 5 User Guide - [Електронний ресурс] - Режим доступу:
https://junit.org/junit5/docs/current/user-guide
34 JaCoCo - [Електронний доступ] - Режим доступу:
https://www.jacoco.org/jacoco/trunk/index.html#:~:text=JaCoCo%20is%20
a%20free%20Java,under%20the%20Eclipse%20Public%20License.
35 Testcontainers for Java - [Електронний доступ] - Режим доступу:
https://java.testcontainers.org/
36 Docker - [Електронний доступ] - Режим доступу:
https://docs.docker.com/get-started/docker-overview/
37 Vladimir Khorikov. Unit-testing. (2020) Language: English 320 pages
ISBN: 9781617296277.
38 AWS ECS - [Електронний доступ] - Режим доступу:
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.
html
39 Dockerfile - [Електронний доступ] - Режим доступу:
https://docs.docker.com/reference/dockerfile/
110
ЧДТУ 242322.009 ПЗ
40 Implementing Custom Tasks - [Електронний доступ] - Режим доступу:
https://docs.gradle.org/current/userguide/implementing_custom_tasks.html
111
ДОДАТОК А
ЗАТВЕРДЖЕНО:
Зав. кафедри ПЗАС
проф. Голуб С. В.
___________________________
ОПТИМІЗАЦІЯ ПРОДУКТИВНОСТІ РОБОТИ З БАЗОЮ ДАНИХ
ВИСОКОНАВАНТАЖЕНОГО ДОДАТКУ НА ПРИКЛАДІ
ІНТЕРНЕТ-МАГАЗИНУ ТЕХНІКИ
Специфікація
ЧДТУ.242322 ПЗ
Листів 2
Розробник ________________ Чоповенко В. М.
Керівник ________________ Півень О.Б.
Н.Контроль ________________ Півень О.Б.
Черкаси 2024
Позначення Найменування Примітка
Документація
482.ЧДТУ.242322 12-01 Лістинг програми
482.ЧДТУ.242322 34-01 Інструкція користувача
482.ЧДТУ.242322 90-01 Графічні матеріали
113
ДОДАТОК Б
ОПТИМІЗАЦІЯ ПРОДУКТИВНОСТІ РОБОТИ З БАЗОЮ ДАНИХ
ВИСОКОНАВАНТАЖЕНОГО ДОДАТКУ НА ПРИКЛАДІ
ІНТЕРНЕТ-МАГАЗИНУ ТЕХНІКИ
Лістинг програми
482.ЧДТУ.2412322 12-01
Листів 24
Розробник ________________ Чоповенко В. М.
Черкаси 2024
482.ЧДТУ.242322 12-01
backend.src.main.java.org.cstu.diploma.config.SwaggerConfig
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("TechTalk (Online Store) Project API")
.version("1.0.0")
.description("API documentation for the Diploma project"));
}
}
backend.src.main.java.org.cstu.diploma.controller.CategoryController
@Tag(name = "Category", description = "Category API")
@RestController
@RequestMapping("/categories")
public class CategoryController {
private final CategoryService categoryService;
@Autowired
public CategoryController(CategoryService categoryService) {
this.categoryService = categoryService;
}
@Operation(summary = "Get all categories")
@ApiResponse(responseCode = "200", description = "Categories retrieved
successfully")
@GetMapping
public ResponseEntity<List<CategoryDTO>> getAll() {
List<CategoryDTO> categories = categoryService.getAll();
return ResponseEntity.ok(categories);
}
@Operation(summary = "Get all categories (Paged)")
@ApiResponse(responseCode = "200", description = "Categories retrieved
successfully")
@PostMapping("/paged")
public ResponseEntity<PageResponseDTO<CategoryDTO>> getAll(@Valid @RequestBody
PageDTO pageDTO) {
return ResponseEntity.ok(categoryService.getAll(pageDTO));
}
@Operation(summary = "Create a new category")
@ApiResponse(responseCode = "201", description = "Category created successfully")
@PostMapping
public ResponseEntity<CategoryDTO> create(@Valid @RequestBody CategoryCreateDTO
categoryDTO) {
return ResponseEntity.status(201).body(categoryService.create(categoryDTO));
}
@Operation(summary = "Get a category by ID")
@ApiResponse(responseCode = "200", description = "Category retrieved
successfully")
@GetMapping("/{id}")
public ResponseEntity<CategoryDTO> getById(@PathVariable UUID id) {
return ResponseEntity.ok(categoryService.getById(id));
}
115
482.ЧДТУ.242322 12-01
@Operation(summary = "Update a category by ID")
@ApiResponse(responseCode = "200", description = "Category updated successfully")
@PutMapping("/{id}")
public ResponseEntity<CategoryDTO> update(@PathVariable UUID id, @Valid
@RequestBody CategoryCreateDTO categoryDTO) {
return ResponseEntity.ok(categoryService.update(id, categoryDTO));
}
@Operation(summary = "Delete a category by ID")
@ApiResponse(responseCode = "204", description = "Category deleted successfully")
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable UUID id) {
categoryService.delete(id);
return ResponseEntity.noContent().build();
}
}
backend.src.main.java.org.cstu.diploma.controller.CustomerController
@Tag(name = "Customer", description = "Customer API")
@RestController
@RequestMapping("/customers")
public class CustomerController {
private final CustomerService customerService;
@Autowired
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
@Operation(summary = "Get all customers")
@ApiResponse(responseCode = "200", description = "Customers retrieved
successfully")
@GetMapping
public ResponseEntity<List<CustomerDTO>> getAll() {
List<CustomerDTO> customers = customerService.getAll();
return ResponseEntity.ok(customers);
}
@Operation(summary = "Get all customers (Paged)")
@ApiResponse(responseCode = "200", description = "Customers retrieved
successfully")
@PostMapping("/paged")
public ResponseEntity<PageResponseDTO<CustomerDTO>> getAll(@Valid @RequestBody
PageDTO pageDTO,
@RequestParam(required
= false) String firstName,
@RequestParam(required
= false) String lastName,
@RequestParam(required
= false) String email,
@RequestParam(required
= false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime createdAtMin,
@RequestParam(required
= false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime createdAtMax,
@RequestParam(required
= false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime updatedAtMin,
@RequestParam(required
= false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime updatedAtMax) {
116
482.ЧДТУ.242322 12-01
return ResponseEntity.ok(customerService.getAll(pageDTO, firstName, lastName,
email, createdAtMin, createdAtMax, updatedAtMin, updatedAtMax));
}
@Operation(summary = "Create a new customer")
@ApiResponse(responseCode = "201", description = "Customer created successfully")
@PostMapping
public ResponseEntity<CustomerDTO> create(@Valid @RequestBody CustomerCreateDTO
customerDTO) {
return ResponseEntity.status(201).body(customerService.create(customerDTO));
}
@Operation(summary = "Get a customer by ID")
@ApiResponse(responseCode = "200", description = "Customer retrieved
successfully")
@GetMapping("/{id}")
public ResponseEntity<CustomerDTO> getById(@PathVariable UUID id) {
return ResponseEntity.ok(customerService.getById(id));
}
@Operation(summary = "Update a customer by ID")
@ApiResponse(responseCode = "200", description = "Customer updated successfully")
@PutMapping("/{id}")
public ResponseEntity<CustomerDTO> update(@PathVariable UUID id, @Valid
@RequestBody CustomerCreateDTO customerDTO) {
return ResponseEntity.ok(customerService.update(id, customerDTO));
}
@Operation(summary = "Delete a customer by ID")
@ApiResponse(responseCode = "204", description = "Customer deleted successfully")
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable UUID id) {
customerService.delete(id);
return ResponseEntity.noContent().build();
}
}
backend.src.main.java.org.cstu.diploma.controller.OrderController
@Tag(name = "Order", description = "Order API")
@RestController
@RequestMapping("/orders")
public class OrderController {
private final OrderService orderService;
@Autowired
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@Operation(summary = "Get all orders (Paged)")
@ApiResponse(responseCode = "200", description = "Orders retrieved successfully")
@PostMapping("/paged")
public ResponseEntity<PageResponseDTO<OrderDTO>> getAll(@Valid @RequestBody
PageDTO pageDTO,
@RequestParam(required =
false) OrderStatus status,
@RequestParam(required =
false) UUID customerId,
@RequestParam(required =
false) UUID productId,
117
482.ЧДТУ.242322 12-01
@RequestParam(required =
false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime createdAtMin,
@RequestParam(required =
false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime createdAtMax,
@RequestParam(required =
false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime updatedAtMin,
@RequestParam(required =
false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime updatedAtMax) {
return ResponseEntity.ok(orderService.getAll(pageDTO, status, customerId,
productId, createdAtMin, createdAtMax, updatedAtMin, updatedAtMax));
}
@Operation(summary = "Create a new order")
@ApiResponse(responseCode = "201", description = "Order created successfully")
@PostMapping
public ResponseEntity<OrderDTO> create(@Valid @RequestBody OrderCreateDTO
orderDTO) {
return ResponseEntity.status(201).body(orderService.create(orderDTO));
}
@Operation(summary = "Get an order by ID")
@ApiResponse(responseCode = "200", description = "Order retrieved successfully")
@GetMapping("/{id}")
public ResponseEntity<OrderDTO> getById(@PathVariable UUID id) {
return ResponseEntity.ok(orderService.getById(id));
}
@Operation(summary = "Update an order by ID")
@ApiResponse(responseCode = "200", description = "Order updated successfully")
@PutMapping("/{id}")
public ResponseEntity<OrderDTO> update(@PathVariable UUID id, @Valid @RequestBody
OrderUpdateDTO orderDTO) {
return ResponseEntity.ok(orderService.update(id, orderDTO));
}
@Operation(summary = "Delete an order by ID")
@ApiResponse(responseCode = "204", description = "Order deleted successfully")
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable UUID id) {
orderService.delete(id);
return ResponseEntity.noContent().build();
}
@Operation(summary = "Update an order status by ID")
@ApiResponse(responseCode = "204", description = "Order status updated
successfully")
@PutMapping("/{id}/status")
public ResponseEntity<Void> updateStatus(@PathVariable UUID id, @Valid
@RequestParam() OrderStatus status) {
orderService.updateStatus(id, status);
return ResponseEntity.noContent().build();
}
}
backend.src.main.java.org.cstu.diploma.controller.ProductController
@Tag(name = "Product", description = "Product API")
@RestController
@RequestMapping("/products")
public class ProductController {
118
482.ЧДТУ.242322 12-01
private final ProductService productService;
@Autowired
public ProductController(ProductService productService) {
this.productService = productService;
}
@Operation(summary = "Get all products (Paged)")
@ApiResponse(responseCode = "200", description = "Products retrieved
successfully")
@PostMapping("/paged")
public ResponseEntity<PageResponseDTO<ProductDTO>> getAll(@Valid @RequestBody
PageDTO pageDTO,
@RequestParam(required
= false) UUID categoryId,
@RequestParam(required
= false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime createdAtMin,
@RequestParam(required
= false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime createdAtMax,
@RequestParam(required
= false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime updatedAtMin,
@RequestParam(required
= false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime updatedAtMax,
@RequestParam(required
= false) String name,
@RequestParam(required
= false) Integer minPrice,
@RequestParam(required
= false) Integer maxPrice,
@RequestParam(required
= false) Integer minQuantity,
@RequestParam(required
= false) Integer maxQuantity) {
return ResponseEntity.ok(productService.getAll(pageDTO,
categoryId,
createdAtMin,
createdAtMax,
updatedAtMin,
updatedAtMax,
name,
minPrice,
maxPrice,
minQuantity,
maxQuantity));
}
@Operation(summary = "Create a new product")
@ApiResponse(responseCode = "201", description = "Product created successfully")
@PostMapping
public ResponseEntity<ProductDTO> create(@Valid @RequestBody ProductCreateDTO
productDTO) {
return ResponseEntity.status(201).body(productService.create(productDTO));
}
@Operation(summary = "Get a product by ID")
@ApiResponse(responseCode = "200", description = "Product retrieved
successfully")
@GetMapping("/{id}")
public ResponseEntity<ProductDTO> getById(@PathVariable UUID id) {
return ResponseEntity.ok(productService.getById(id));
119
482.ЧДТУ.242322 12-01
}
@Operation(summary = "Update a product by ID")
@ApiResponse(responseCode = "200", description = "Product updated successfully")
@PutMapping("/{id}")
public ResponseEntity<ProductDTO> update(@PathVariable UUID id, @Valid
@RequestBody ProductCreateDTO productDTO) {
return ResponseEntity.ok(productService.update(id, productDTO));
}
@Operation(summary = "Delete a product by ID")
@ApiResponse(responseCode = "204", description = "Product deleted successfully")
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable UUID id) {
productService.delete(id);
return ResponseEntity.noContent().build();
}
}
backend.src.main.java.org.cstu.diploma.entity.BaseAuditEntity
@Getter
@Setter
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@MappedSuperclass
public class BaseAuditEntity extends BaseEntity {
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
backend.src.main.java.org.cstu.diploma.entity.BaseEntity
@Getter
@Setter
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@MappedSuperclass
public class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
}
backend.src.main.java.org.cstu.diploma.entity.Category
@Getter
@Setter
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "category")
public class Category extends BaseAuditEntity {
@Id
private UUID id;
@Column
private String name;
120
482.ЧДТУ.242322 12-01
}
backend.src.main.java.org.cstu.diploma.entity.Customer
@Getter
@Setter
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "customer")
public class Customer extends BaseAuditEntity {
@Column
private String firstName;
@Column
private String lastName;
@Column
private String email;
}
backend.src.main.java.org.cstu.diploma.entity.Order
@Getter
@Setter
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "\"order\"")
public class Order extends BaseAuditEntity {
@ManyToOne
@JoinColumn(name = "customer_id", nullable = false)
private Customer customer;
@Column
@Enumerated(EnumType.STRING)
private OrderStatus status;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true,
fetch = FetchType.EAGER)
private List<OrderItem> items;
}
backend.src.main.java.org.cstu.diploma.entity.OrderItem
@Getter
@Setter
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "order_item")
public class OrderItem extends BaseAuditEntity {
@ManyToOne
@JoinColumn(name = "order_id", nullable = false)
private Order order;
@ManyToOne
@JoinColumn(name = "product_id", nullable = false)
121
482.ЧДТУ.242322 12-01
private Product product;
@Column
private int quantity;
@Column
private BigDecimal price;
}
backend.src.main.java.org.cstu.diploma.entity.Product
@Getter
@Setter
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "product")
public class Product extends BaseAuditEntity {
@Column
private String name;
@Column
private String description;
@Column
private int quantity;
@Column
private BigDecimal price;
@ManyToOne
@JoinColumn(name = "category_id")
private Category category;
}
backend.src.main.java.org.cstu.diploma.enumeration.OrderStatus
public enum OrderStatus {
PENDING,
DELIVERED,
CANCELLED
}
backend.src.main.java.org.cstu.diploma.exception.GlobalExceptionHandler
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest
request) {
Map<String, Object> body = new HashMap<>();
body.put("Timestamp", LocalDateTime.now());
body.put("Message", "An unexpected error occurred");
body.put("Details", ex.getMessage());
return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(DataIntegrityViolationException.class)
public ResponseEntity<Object> handleException(DataIntegrityViolationException ex,
WebRequest request) {
122
482.ЧДТУ.242322 12-01
Map<String, Object> body = new HashMap<>();
if (ex.getCause() instanceof ConstraintViolationException) {
body.put("Message", "Constraint violation");
body.put("Details", ex.getCause().getMessage());
} else {
body.put("Message", "An unexpected error occurred");
body.put("Details", ex.getMessage());
}
body.put("Timestamp", LocalDateTime.now());
return new ResponseEntity<>(body, HttpStatus.UNPROCESSABLE_ENTITY);
}
@Override
protected ResponseEntity<Object>
handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers,
HttpStatusCode status, WebRequest request) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
Map<String, Object> body = new HashMap<>();
body.put("RequestErrors", errors);
return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(UnprocessableEntityException.class)
public ResponseEntity<Object>
handleUnprocessableEntityException(UnprocessableEntityException ex, WebRequest request) {
return new ResponseEntity<>(ex.getMessage(),
HttpStatus.UNPROCESSABLE_ENTITY);
}
@ExceptionHandler(NotFoundException.class)
public ResponseEntity<Object> handleNotFoundException(NotFoundException ex,
WebRequest request) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
}
backend.src.main.java.org.cstu.diploma.exception.NotFoundException
@ResponseStatus(HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException {
public NotFoundException(String message) {
super(message);
}
}
backend.src.main.java.org.cstu.diploma.exception.UnprocessableEntityException
public class UnprocessableEntityException extends RuntimeException {
public UnprocessableEntityException(String message) {
super(message);
}
}
backend.src.main.java.org.cstu.diploma.mapper.CategoryMapper
123
482.ЧДТУ.242322 12-01
@Mapper(componentModel = "spring")
public interface CategoryMapper {
List<CategoryDTO> toDTOs(List<Category> categories);
@Mapping(target = "id", ignore = true)
@Mapping(target = "createdAt", ignore = true)
@Mapping(target = "updatedAt", ignore = true)
Category toEntity(CategoryCreateDTO categoryDTO);
CategoryDTO toDTO(Category category);
}
backend.src.main.java.org.cstu.diploma.mapper.CustomerMapper
@Mapper(componentModel = "spring")
public interface CustomerMapper {
List<CustomerDTO> toDTOs(List<Customer> customers);
CustomerDTO toDTO(Customer customer);
@Mapping(target = "id", ignore = true)
@Mapping(target = "createdAt", ignore = true)
@Mapping(target = "updatedAt", ignore = true)
Customer toEntity(CustomerCreateDTO customerDTO);
@Mapping(target = "id", ignore = true)
@Mapping(target = "createdAt", ignore = true)
@Mapping(target = "updatedAt", ignore = true)
void updateEntity(CustomerCreateDTO customerDTO, @MappingTarget Customer
customer);
}
backend.src.main.java.org.cstu.diploma.mapper.OrderItemMapper
@Mapper(componentModel = "spring")
public interface OrderItemMapper {
OrderItemDTO toDTO(OrderItem orderItem);
List<OrderItemDTO> toDTOs(List<OrderItem> orderItems);
}
backend.src.main.java.org.cstu.diploma.mapper.OrderMapper
@Mapper(componentModel = "spring")
public interface OrderMapper {
@Mapping(target = "id", ignore = true)
@Mapping(target = "customer", ignore = true)
@Mapping(target = "items", ignore = true)
@Mapping(target = "createdAt", ignore = true)
@Mapping(target = "updatedAt", ignore = true)
@Mapping(target = "status", ignore = true)
Order toEntity(OrderCreateDTO orderDTO);
OrderDTO toDTO(Order order);
}
backend.src.main.java.org.cstu.diploma.mapper.ProductMapper
@Mapper(componentModel = "spring")
public interface ProductMapper {
@Mapping(target = "category", ignore = true)
124
482.ЧДТУ.242322 12-01
@Mapping(target = "id", ignore = true)
@Mapping(target = "createdAt", ignore = true)
@Mapping(target = "updatedAt", ignore = true)
Product toEntity(ProductCreateDTO productDTO);
@Mapping(target = "category.id", source = "category.id")
@Mapping(target = "category.name", source = "category.name")
ProductDTO toDTO(Product product);
List<ProductDTO> toDTOs(List<Product> products);
@Mapping(target = "id", ignore = true)
@Mapping(target = "createdAt", ignore = true)
@Mapping(target = "updatedAt", ignore = true)
@Mapping(target = "category", ignore = true)
void updateEntity(ProductCreateDTO productDTO, @MappingTarget Product product);
}
backend.src.main.java.org.cstu.diploma.repository.CategoryRepo
public interface CategoryRepo extends JpaRepository<Category, UUID> {
}
backend.src.main.java.org.cstu.diploma.repository.CustomerRepo
@Repository
public interface CustomerRepo extends JpaRepository<Customer, UUID>,
JpaSpecificationExecutor<Customer> {
}
backend.src.main.java.org.cstu.diploma.repository.OrderItemRepo
@Repository
public interface OrderItemRepo extends JpaRepository<OrderItem, UUID> {
}
backend.src.main.java.org.cstu.diploma.repository.OrderRepo
public interface OrderRepo extends JpaRepository<Order, UUID>,
JpaSpecificationExecutor<Order> {
}
backend.src.main.java.org.cstu.diploma.repository.ProductRepo
public interface ProductRepo extends JpaRepository<Product, UUID>,
JpaSpecificationExecutor<Product> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
List<Product> findAllById(Iterable<UUID> ids);
}
backend.src.main.java.org.cstu.diploma.service.CategoryService
@Service
public class CategoryService {
private final CategoryRepo categoryRepo;
private final CategoryMapper categoryMapper;
@Autowired
public CategoryService(CategoryRepo categoryRepo, CategoryMapper categoryMapper)
{
this.categoryRepo = categoryRepo;
this.categoryMapper = categoryMapper;
}
125
482.ЧДТУ.242322 12-01
public List<CategoryDTO> getAll() {
List<Category> categories = categoryRepo.findAll();
return categoryMapper.toDTOs(categories);
}
public PageResponseDTO<CategoryDTO> getAll(PageDTO pageDTO) {
Pageable pageable = PageUtil.getPageable(pageDTO);
Page<Category> page = categoryRepo.findAll(pageable);
return PageUtil.getPageResponseDTO(page,
categoryMapper.toDTOs(page.getContent()));
}
public CategoryDTO create(CategoryCreateDTO categoryDTO) {
LocalDateTime now = LocalDateTime.now();
Category category = categoryMapper.toEntity(categoryDTO);
category.setCreatedAt(now);
category.setUpdatedAt(now);
Category savedCategory = categoryRepo.save(category);
return categoryMapper.toDTO(savedCategory);
}
public Category findById(UUID id) {
return categoryRepo.findById(id).orElseThrow(() -> new
NotFoundException("Category not found"));
}
public CategoryDTO getById(UUID id) {
Category category = findById(id);
return categoryMapper.toDTO(category);
}
public CategoryDTO update(UUID id, CategoryCreateDTO categoryDTO) {
Category category = findById(id);
category.setUpdatedAt(LocalDateTime.now());
category.setName(categoryDTO.getName());
Category updatedCategory = categoryRepo.save(category);
return categoryMapper.toDTO(updatedCategory);
}
public void delete(UUID id) {
Category category = findById(id);
categoryRepo.delete(category);
}
}
backend.src.main.java.org.cstu.diploma.service.CustomerService
@Service
public class CustomerService {
private final CustomerRepo customerRepo;
private final CustomerMapper customerMapper;
@Autowired
public CustomerService(CustomerRepo customerRepo, CustomerMapper customerMapper)
{
this.customerRepo = customerRepo;
this.customerMapper = customerMapper;
}
public List<CustomerDTO> getAll() {
List<Customer> customers = customerRepo.findAll();
126
482.ЧДТУ.242322 12-01
return customerMapper.toDTOs(customers);
}
public PageResponseDTO<CustomerDTO> getAll(PageDTO pageDTO,
String firstName,
String lastName,
String email,
LocalDateTime createdAtMin,
LocalDateTime createdAtMax,
LocalDateTime updatedAtMin,
LocalDateTime updatedAtMax) {
Pageable pageable = PageUtil.getPageable(pageDTO);
Specification<Customer> specification = (root, query, criteriaBuilder) -> {
Predicate predicate = criteriaBuilder.conjunction();
if (Objects.nonNull(firstName))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.like(root.get("firstName"), "%" + firstName + "%"));
if (Objects.nonNull(lastName))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.like(root.get("lastName"), "%" + lastName + "%"));
if (Objects.nonNull(email))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.like(root.get("email"), "%" + email + "%"));
if (Objects.nonNull(createdAtMin))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.greaterThanOrEqualTo(root.get("createdAt"), createdAtMin));
if (Objects.nonNull(createdAtMax))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.lessThanOrEqualTo(root.get("createdAt"), createdAtMax));
if (Objects.nonNull(updatedAtMin))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.greaterThanOrEqualTo(root.get("updatedAt"), updatedAtMin));
if (Objects.nonNull(updatedAtMax))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.lessThanOrEqualTo(root.get("updatedAt"), updatedAtMax));
return predicate;
};
Page<Customer> page = customerRepo.findAll(specification, pageable);
return PageUtil.getPageResponseDTO(page,
customerMapper.toDTOs(page.getContent()));
}
public CustomerDTO create(CustomerCreateDTO customerDTO) {
LocalDateTime now = LocalDateTime.now();
Customer customer = customerMapper.toEntity(customerDTO);
customer.setCreatedAt(now);
customer.setUpdatedAt(now);
Customer savedCustomer = customerRepo.save(customer);
return customerMapper.toDTO(savedCustomer);
}
127
482.ЧДТУ.242322 12-01
public Customer findById(UUID id) {
return customerRepo.findById(id).orElseThrow(() -> new
NotFoundException("Customer not found"));
}
public CustomerDTO getById(UUID id) {
Customer customer = findById(id);
return customerMapper.toDTO(customer);
}
public CustomerDTO update(UUID id, CustomerCreateDTO customerDTO) {
Customer customer = findById(id);
customer.setUpdatedAt(LocalDateTime.now());
customerMapper.updateEntity(customerDTO, customer);
Customer updatedCustomer = customerRepo.save(customer);
return customerMapper.toDTO(updatedCustomer);
}
public void delete(UUID id) {
customerRepo.deleteById(id);
}
}
backend.src.main.java.org.cstu.diploma.service.OrderService
@Service
public class OrderService {
private final OrderRepo orderRepo;
private final ProductRepo productRepo;
private final CustomerService customerService;
private final ProductService productService;
private final OrderMapper orderMapper;
private final OrderItemMapper orderItemMapper;
@Autowired
public OrderService(OrderRepo orderRepo,
OrderMapper orderMapper,
OrderItemMapper orderItemMapper,
CustomerService customerService,
ProductRepo productRepo,
ProductService productService) {
this.orderRepo = orderRepo;
this.orderMapper = orderMapper;
this.orderItemMapper = orderItemMapper;
this.customerService = customerService;
this.productRepo = productRepo;
this.productService = productService;
}
@Transactional
public PageResponseDTO<OrderDTO> getAll(PageDTO pageDTO,
OrderStatus status,
UUID customerId,
UUID productId,
LocalDateTime createdAtMin,
LocalDateTime createdAtMax,
LocalDateTime updatedAtMin,
128
482.ЧДТУ.242322 12-01
LocalDateTime updatedAtMax) {
Pageable pageable = PageUtil.getPageable(pageDTO);
Specification<Order> specification = (root, query, criteriaBuilder) -> {
Predicate predicate = criteriaBuilder.conjunction();
if (Objects.nonNull(status))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.equal(root.get("status"), status));
if (Objects.nonNull(customerId))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.equal(root.get("customer").get("id"), customerId));
if (Objects.nonNull(productId)) {
Join<Order, OrderItem> orderItemJoin = root.join("items");
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.equal(orderItemJoin.get("product").get("id"), productId));
}
if (Objects.nonNull(createdAtMin))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.greaterThanOrEqualTo(root.get("createdAt"), createdAtMin));
if (Objects.nonNull(createdAtMax))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.lessThanOrEqualTo(root.get("createdAt"), createdAtMax));
if (Objects.nonNull(updatedAtMin))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.greaterThanOrEqualTo(root.get("updatedAt"), updatedAtMin));
if (Objects.nonNull(updatedAtMax))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.lessThanOrEqualTo(root.get("updatedAt"), updatedAtMax));
return predicate;
};
Page<Order> page = orderRepo.findAll(specification, pageable);
List<OrderDTO> orderDTOs = new ArrayList<>();
for (Order order : page.getContent()) {
OrderDTO orderDTO = orderMapper.toDTO(order);
orderDTO.setItems(orderItemMapper.toDTOs(order.getItems()));
orderDTOs.add(orderDTO);
}
return PageUtil.getPageResponseDTO(page, orderDTOs);
}
@Transactional(Transactional.TxType.REQUIRES_NEW)
public OrderDTO create(OrderCreateDTO orderCreateDTO) {
checkIfProductIdsAreUnique(orderCreateDTO.getItems());
Set<UUID> productIds = orderCreateDTO.getItems().stream()
.map(OrderItemCreateDTO::getProductId)
.collect(Collectors.toSet());
129
482.ЧДТУ.242322 12-01
Map<UUID, Product> products = productRepo.findAllById(productIds)
.stream()
.collect(Collectors.toMap(Product::getId, Function.identity()));
if (products.size() != orderCreateDTO.getItems().size()) {
throw new UnprocessableEntityException("One or more products not found");
}
Order order = orderMapper.toEntity(orderCreateDTO);
order.setCustomer(customerService.findById(orderCreateDTO.getCustomerId()));
List<OrderItem> orderItems = new ArrayList<>();
LocalDateTime now = LocalDateTime.now();
for (OrderItemCreateDTO item : orderCreateDTO.getItems()) {
Product product = products.get(item.getProductId());
if (product.getQuantity() < item.getQuantity()) {
throw new UnprocessableEntityException("Not enough " +
product.getName() + " in stock");
}
OrderItem orderItem = OrderItem.builder()
.order(order)
.product(product)
.price(product.getPrice())
.quantity(item.getQuantity())
.createdAt(now)
.updatedAt(now)
.build();
orderItems.add(orderItem);
product.setQuantity(product.getQuantity() - item.getQuantity());
productRepo.save(product);
}
order.setStatus(OrderStatus.PENDING);
order.setItems(orderItems);
order.setCreatedAt(now);
order.setUpdatedAt(now);
Order savedOrder = orderRepo.save(order);
List<OrderItemDTO> orderItemDTOs =
orderItemMapper.toDTOs(savedOrder.getItems());
OrderDTO orderDTO = orderMapper.toDTO(savedOrder);
orderDTO.setItems(orderItemDTOs);
return orderDTO;
}
private Order findById(UUID id) {
return orderRepo.findById(id)
.orElseThrow(() -> new NotFoundException("Order not found"));
}
@Transactional
public OrderDTO getById(UUID id) {
Order order = findById(id);
return orderMapper.toDTO(order);
}
@Transactional
130
482.ЧДТУ.242322 12-01
public OrderDTO update(UUID id, OrderUpdateDTO orderDTO) {
checkIfProductIdsAreUnique(orderDTO.getItems());
Order order = findById(id);
checkIfOrderCanBeUpdated(order);
List<UUID> productIdsInDTO = orderDTO.getItems().stream()
.map(OrderItemCreateDTO::getProductId)
.collect(Collectors.toList());
Map<UUID, Product> productsByIdFromDB =
productRepo.findAllById(productIdsInDTO)
.stream()
.collect(Collectors.toMap(Product::getId, Function.identity()));
if (productsByIdFromDB.size() != orderDTO.getItems().size()) {
throw new UnprocessableEntityException("One or more products not found");
}
Map<UUID, OrderItemCreateDTO> productIdItemFromDTOMap =
orderDTO.getItems().stream()
.collect(Collectors.toMap(OrderItemCreateDTO::getProductId,
Function.identity()));
Iterator<OrderItem> iterator = order.getItems().iterator();
while (iterator.hasNext()) {
OrderItem existingItem = iterator.next();
if
(!productIdItemFromDTOMap.containsKey(existingItem.getProduct().getId())) {
Product product = existingItem.getProduct();
product.setQuantity(product.getQuantity() +
existingItem.getQuantity());
productRepo.save(product);
iterator.remove();
}
}
Map<UUID, OrderItem> productIdOrderItemMap = order.getItems().stream()
.collect(Collectors.toMap(item -> item.getProduct().getId(),
Function.identity()));
LocalDateTime now = LocalDateTime.now();
for (OrderItemCreateDTO itemDTO : orderDTO.getItems()) {
Product product = productsByIdFromDB.get(itemDTO.getProductId());
OrderItem existingItem =
productIdOrderItemMap.get(itemDTO.getProductId());
if (existingItem != null) {
int quantityDifference = itemDTO.getQuantity() -
existingItem.getQuantity();
existingItem.setQuantity(existingItem.getQuantity() +
quantityDifference);
existingItem.setPrice(product.getPrice());
existingItem.setUpdatedAt(now);
int newProductQuantity = product.getQuantity() - quantityDifference;
if (newProductQuantity < 0) {
throw new UnprocessableEntityException("Not enough " +
product.getName() + " in stock");
}
131
482.ЧДТУ.242322 12-01
product.setQuantity(newProductQuantity);
} else {
OrderItem newItem = OrderItem.builder()
.order(order)
.product(product)
.quantity(itemDTO.getQuantity())
.price(product.getPrice())
.createdAt(now)
.updatedAt(now)
.build();
order.getItems().add(newItem);
int newProductQuantity = product.getQuantity() -
itemDTO.getQuantity();
if (newProductQuantity < 0) {
throw new UnprocessableEntityException("Not enough " +
product.getName() + " in stock");
}
product.setQuantity(newProductQuantity);
}
product.setUpdatedAt(now);
productRepo.save(product);
}
order.setUpdatedAt(now);
Order updatedOrder = orderRepo.save(order);
return orderMapper.toDTO(updatedOrder);
}
@Transactional
public void updateStatus(UUID id, OrderStatus status) {
Order order = findById(id);
if (order.getStatus() == status) {
throw new UnprocessableEntityException("Order already has this status");
}
checkIfOrderCanBeUpdated(order);
if (status == OrderStatus.CANCELLED) {
order.getItems().forEach(item -> {
Product product = item.getProduct();
product.setQuantity(product.getQuantity() + item.getQuantity());
productRepo.save(product);
});
}
order.setStatus(status);
order.setUpdatedAt(LocalDateTime.now());
orderRepo.save(order);
}
@Transactional
public void delete(UUID id) {
Order order = findById(id);
orderRepo.delete(order);
}
132
482.ЧДТУ.242322 12-01
private void checkIfProductIdsAreUnique(List<OrderItemCreateDTO> items) {
Set<UUID> productIds = new HashSet<>();
for (OrderItemCreateDTO item : items) {
if (!productIds.add(item.getProductId())) {
throw new UnprocessableEntityException("Product IDs must be unique
within an order");
}
}
}
private void checkIfOrderCanBeUpdated(Order order) {
if (order.getStatus() == OrderStatus.DELIVERED) {
throw new UnprocessableEntityException("Order already delivered");
}
if (order.getStatus() == OrderStatus.CANCELLED) {
throw new UnprocessableEntityException("Order already cancelled");
}
}
}
backend.src.main.java.org.cstu.diploma.service.ProductService
@Service
@Slf4j
public class ProductService {
private final ProductRepo productRepo;
private final ProductMapper productMapper;
private final CategoryService categoryService;
private final CacheManager cacheManager;
@Autowired
public ProductService(ProductRepo productRepo,
ProductMapper productMapper,
CategoryService categoryService,
CacheManager cacheManager) {
this.productRepo = productRepo;
this.productMapper = productMapper;
this.categoryService = categoryService;
this.cacheManager = cacheManager;
}
// @CacheEvict(value = "products", allEntries = true)
public ProductDTO create(ProductCreateDTO productDTO) {
LocalDateTime now = LocalDateTime.now();
Product product = productMapper.toEntity(productDTO);
product.setCreatedAt(now);
product.setUpdatedAt(now);
product.setCategory(categoryService.findById(productDTO.getCategoryId()));
Product savedProduct = productRepo.save(product);
if (Objects.nonNull(cacheManager.getCache("products"))) {
cacheManager.getCache("products").put(savedProduct.getId(),
productMapper.toDTO(savedProduct));
}
return productMapper.toDTO(savedProduct);
}
133
482.ЧДТУ.242322 12-01
public PageResponseDTO<ProductDTO> getAll(PageDTO pageDTO,
UUID categoryId,
LocalDateTime createdAtMin,
LocalDateTime createdAtMax,
LocalDateTime updatedAtMin,
LocalDateTime updatedAtMax,
String name,
Integer minPrice,
Integer maxPrice,
Integer minQuantity,
Integer maxQuantity) {
Pageable pageable = PageUtil.getPageable(pageDTO);
Page<Product> page;
Specification<Product> specification = (root, query, criteriaBuilder) -> {
Predicate predicate = criteriaBuilder.conjunction();
if (Objects.nonNull(categoryId))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.equal(root.get("category").get("id"), categoryId));
if (Objects.nonNull(createdAtMin))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.greaterThanOrEqualTo(root.get("createdAt"), createdAtMin));
if (Objects.nonNull(createdAtMax))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.lessThanOrEqualTo(root.get("createdAt"), createdAtMax));
if (Objects.nonNull(updatedAtMin))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.greaterThanOrEqualTo(root.get("updatedAt"), updatedAtMin));
if (Objects.nonNull(updatedAtMax))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.lessThanOrEqualTo(root.get("updatedAt"), updatedAtMax));
if (Objects.nonNull(name))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.like(root.get("name"), "%" + name + "%"));
if (Objects.nonNull(minPrice))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.greaterThanOrEqualTo(root.get("price"), minPrice));
if (Objects.nonNull(maxPrice))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.lessThanOrEqualTo(root.get("price"), maxPrice));
if (Objects.nonNull(minQuantity))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.greaterThanOrEqualTo(root.get("quantity"), minQuantity));
if (Objects.nonNull(maxQuantity))
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.lessThanOrEqualTo(root.get("quantity"), maxQuantity));
return predicate;
};
134
482.ЧДТУ.242322 12-01
page = productRepo.findAll(specification, pageable);
return PageUtil.getPageResponseDTO(page,
productMapper.toDTOs(page.getContent()));
}
public Product findById(UUID id) {
return productRepo.findById(id).orElseThrow(() -> new
NotFoundException("Product not found"));
}
@Cacheable(value = "products", key = "#id")
public ProductDTO getById(UUID id) {
Product product = findById(id);
return productMapper.toDTO(product);
}
@CachePut(value = "products", key = "#id")
public ProductDTO update(UUID id, ProductCreateDTO productDTO) {
Product product = findById(id);
product.setUpdatedAt(LocalDateTime.now());
if (productDTO.getCategoryId() != product.getCategory().getId()) {
product.setCategory(categoryService.findById(productDTO.getCategoryId()));
}
productMapper.updateEntity(productDTO, product);
Product updatedProduct = productRepo.save(product);
return productMapper.toDTO(updatedProduct);
}
@CacheEvict(value = "products", key = "#id")
public void delete(UUID id) {
Product product = findById(id);
productRepo.delete(product);
}
}
backend.src.main.java.org.cstu.diploma.util.PageUtil
public class PageUtil {
public static Pageable getPageable(PageDTO pageDTO) {
return PageRequest.of(
pageDTO.getPage() - 1,
pageDTO.getSize(),
getSort(pageDTO)
);
}
public static Sort getSort(PageDTO pageDTO) {
return Sort.by(
Sort.Direction.fromString(pageDTO.getSortDirection()),
pageDTO.getSortBy()
);
}
public static <T, D> PageResponseDTO<D> getPageResponseDTO(Page<T> page, List<D>
content) {
return new PageResponseDTO<>(
135
482.ЧДТУ.242322 12-01
content,
page.getNumber() + 1,
page.getSize(),
page.getTotalElements(),
page.getTotalPages()
);
}
}
backend.src.main.resources.application.yaml
spring:
datasource:
url: ${DB_URL:jdbc:postgresql://localhost:5432/techtalk}
username: ${DB_USERNAME:vasya_chopovenko}
password: ${DB_PASSWORD:admin}
hikari:
maximum-pool-size: 10
minimum-idle: 5
idle-timeout: 30000
connection-timeout: 20000
max-lifetime: 1800000
jpa:
show-sql: false
open-in-view: false
hibernate:
ddl-auto: none
properties:
hibernate:
format_sql: true
flyway:
enabled: true
validate-on-migrate: true
validate-migration-naming: true
baseline-on-migrate: true
springdoc:
api-docs:
enabled: true
swagger-ui:
enabled: true
management:
endpoints:
web:
exposure:
include: ["prometheus"]
endpoint:
prometheus:
enabled: true
server:
port: ${PORT:8080}
backend.src.build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.5'
id 'io.spring.dependency-management' version '1.1.6'
id 'jacoco'
}
group = 'org.cstu'
version = '1.0.0'
136
482.ЧДТУ.242322 12-01
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'
implementation 'io.micrometer:micrometer-registry-prometheus'
implementation 'org.flywaydb:flyway-core'
implementation 'org.flywaydb:flyway-database-postgresql'
implementation 'org.postgresql:postgresql'
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
implementation 'com.google.code.gson:gson:2.10.1'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'org.postgresql:postgresql'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.mockito:mockito-core:5.0.0'
testImplementation 'org.mockito:mockito-junit-jupiter:5.0.0'
testImplementation 'org.assertj:assertj-core:3.22.0'
testImplementation "org.testcontainers:testcontainers:1.20.3"
testImplementation "org.testcontainers:junit-jupiter:1.20.3"
testImplementation "org.testcontainers:postgresql:1.20.3"
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
annotationProcessor 'org.projectlombok:lombok'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'
}
test {
useJUnitPlatform()
}
tasks.register('buildImage') {
dependsOn('build')
doLast {
exec {
commandLine('docker', 'build', '-t', 'tech-talk:1.0.0', '.')
}
}
}
backend.src.Dockerfile
137
482.ЧДТУ.242322 12-01
FROM openjdk:17-jdk-slim
COPY ./build/libs/backend-1.0.0.jar /app/backend.jar
ENTRYPOINT ["java", "-jar", "/app/backend.jar"]
backend.src.docker-compose.yaml
version: '3.8'
services:
backend:
build:
context: .
dockerfile: Dockerfile
environment:
DB_URL: jdbc:postgresql://postgres:5432/techtalk
DB_USERNAME: vasya_chopovenko
DB_PASSWORD: admin
ports:
- 8080:8080
postgres:
image: postgres:16.1
environment:
POSTGRES_USER: vasya_chopovenko
POSTGRES_PASSWORD: admin
POSTGRES_DB: techtalk
ports:
- 5432:5432
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
default:
name: techtalk
volumes:
postgres_data:
138
ДОДАТОК В
ОПТИМІЗАЦІЯ ПРОДУКТИВНОСТІ РОБОТИ З БАЗОЮ ДАНИХ
ВИСОКОНАВАНТАЖЕНОГО ДОДАТКУ НА ПРИКЛАДІ
ІНТЕРНЕТ-МАГАЗИНУ ТЕХНІКИ
Інструкція користувача
482.ЧДТУ.242322 34-01
Листів 7
Розробник ________________ Чоповенко В. М.
Черкаси 2024
482.ЧДТУ.242322 34-01
Запуск проекту. Побудова docker image
Код проекту знаходиться на GitHub. Для того, щоб завантажити код на
пристрій, необхідно виконати наступну команду в терміналі:
– git clone [email protected]:VasyaChopovenko/TechTalk---Online-Shop.git
В проекті присутній Dockerfile, який дозволяє побудувати docker image для
подальшого запуску на локальному пристрої. Для зручності також було додано
docker compose, який дозволяє запустити сервер разом з базою даних в docker.
Для початку необхідно скачати сам Docker за наступним посиланням:
https://www.docker.com/products/docker-desktop/. Встановлюємо завантажений
файл.
Після встановлення Docker можемо почати запуск проекту. Для цього
переходимо в папку проекту, потім в папку backend і виконуємо в терміналі
команду docker-compose up (Рис. В.1). Ця команда створить необхідні образи
сервісів і запустить відповідні контейнери з правильними конфігураціями.
Рис. В.1 - Docker Compose файл
140
482.ЧДТУ.242322 34-01
Далі відкриваємо браузер і вводимо в пошуковому рядку
localhost:8080/swagger-ui. Відкривається вікно Swagger, яке дозволяє виконувати
будь які запити.
Для того, щоб просто збудувати образ серверного додатку можна виконати
команду ./gradlew buildImage в кореневій папці проекту, або перейти в папку з
Dockerfile і виконати команду docker build -t <назва образу> . (крапку також
необхідно додати в команду через пробіл після назви образу, так як вона виступає
контекстом для файлу).
Запуск навантажувальних тестів
Для того, щоб запустити навантажувальні тести, необхідно виконати
наступну команду:
./gradlew gatlingRun –simulation <імʼя класу з необхідним сценарцієм>
Порядок запуску та налаштувань:
– відкриваємо таблицю з категоріями, копіюємо id і вставляємо їх у файл
category.csv;
– запускаємо симуляцію ProductCreateLoadTest;
– копіюємо id та name з таблиці продуктів в product.csv;
– запускаємо симуляцію CustomerCreateLoadTest;
– копіюємо id з таблиці customer в customer.csv;
– далі можна запускати будь-які симуляції, так як налаштування файлів csv
виконано.
Опис API
Модуль замовлень
Endpoint: POST /orders/paged
Опис: Отримує всі замовлення з можливістю пагінації та фільтрації.
Параметри:
– PageDTO pageDTO: Об'єкт для пагінації, що містить номер сторінки та
розмір сторінки.
– OrderStatus status: Фільтр за статусом замовлення.
– UUID customerId: Фільтр за ідентифікатором клієнта.
141
482.ЧДТУ.242322 34-01
– UUID productId: Фільтр за ідентифікатором продукту.
– LocalDateTime createdAtMin, createdAtMax: Фільтри за датою створення.
– LocalDateTime updatedAtMin, updatedAtMax: Фільтри за датою
оновлення.
Endpoint: POST /orders
Опис: Створює нове замовлення.
Параметри:
– OrderCreateDTO orderDTO: Дані для створення замовлення.
Endpoint: GET /orders/{id}
Опис: Отримує замовлення за його ідентифікатором.
Параметри:
– UUID id: Ідентифікатор замовлення.
Endpoint: PUT /orders/{id}
Опис: Оновлює замовлення за його ідентифікатором.
Параметри:
– UUID id: Ідентифікатор замовлення.
– OrderUpdateDTO orderDTO: Дані для оновлення замовлення.
Endpoint: DELETE /orders/{id}
Опис: Видаляє замовлення за його ідентифікатором.
Параметри:
– UUID id: Ідентифікатор замовлення.
Endpoint: PUT /orders/{id}/status
Опис: Оновлює статус замовлення за його ідентифікатором.
Параметри:
– UUID id: Ідентифікатор замовлення.
142
482.ЧДТУ.242322 34-01
– OrderStatus status: Новий статус замовлення.
Модуль продуктів
Endpoint: POST /products/paged
Опис: Отримує всі продукти з можливістю пагінації та фільтрації.
Параметри:
– PageDTO pageDTO: Об'єкт для пагінації.
– UUID categoryId: Фільтр за категорією.
– LocalDateTime createdAtMin, createdAtMax: Фільтри за датою створення.
– LocalDateTime updatedAtMin, updatedAtMax: Фільтри за датою
оновлення.
– String name: Фільтр за назвою продукту.
– Integer minPrice, maxPrice: Фільтри за ціною.
– Integer minQuantity, maxQuantity: Фільтри за кількістю.
Endpoint: POST /products
Опис: Створює новий продукт.
Параметри:
– ProductCreateDTO productDTO: Дані для створення продукту.
Endpoint: GET /products/{id}
Опис: Отримує продукт за його ідентифікатором.
Параметри:
– UUID id: Ідентифікатор продукту.
Endpoint: PUT /products/{id}
Опис: Оновлює продукт за його ідентифікатором.
Параметри:
– UUID id: Ідентифікатор продукту.
– ProductCreateDTO productDTO: Дані для оновлення продукту.
143
482.ЧДТУ.242322 34-01
Endpoint: DELETE /products/{id}
Опис: Видаляє продукт за його ідентифікатором.
Параметри:
– UUID id: Ідентифікатор продукту.
Модуль клієнтів
Endpoint: GET /customers
Опис: Отримує всі клієнти.
Параметри: Немає.
Endpoint: POST /customers/paged
Опис: Отримує всі клієнти з можливістю пагінації та фільтрації.
Параметри:
– PageDTO pageDTO: Об'єкт для пагінації.
– String firstName, lastName, email: Фільтри за ім'ям, прізвищем та
електронною поштою.
– LocalDateTime createdAtMin, createdAtMax: Фільтри за датою створення.
– LocalDateTime updatedAtMin, updatedAtMax: Фільтри за датою
оновлення.
Endpoint: POST /customers
Опис: Створює нового клієнта.
Параметри:
– CustomerCreateDTO customerDTO: Дані для створення клієнта.
Endpoint: GET /customers/{id}
Опис: Отримує клієнта за його ідентифікатором.
Параметри:
– UUID id: Ідентифікатор клієнта.
Endpoint: PUT /customers/{id}
144
482.ЧДТУ.242322 34-01
Опис: Оновлює клієнта за його ідентифікатором.
Параметри:
– UUID id: Ідентифікатор клієнта.
– CustomerCreateDTO customerDTO: Дані для оновлення клієнта.
Endpoint: DELETE /customers/{id}
Опис: Видаляє клієнта за його ідентифікатором.
Параметри:
– UUID id: Ідентифікатор клієнта.
Модуль категорій
Endpoint: GET /categories
Опис: Отримує всі категорії.
Параметри: Немає.
Endpoint: POST /categories/paged
Опис: Отримує всі категорії з можливістю пагінації.
Параметри:
– PageDTO pageDTO: Об'єкт для пагінації.
Endpoint: POST /categories
Опис: Створює нову категорію.
Параметри:
– CategoryCreateDTO categoryDTO: Дані для створення категорії.
Endpoint: GET /categories/{id}
Опис: Отримує категорію за її ідентифікатором.
Параметри:
– UUID id: Ідентифікатор категорії.
145
482.ЧДТУ.242322 34-01
Endpoint: PUT /categories/{id}
Опис: Оновлює категорію за її ідентифікатором.
Параметри:
– UUID id: Ідентифікатор категорії.
– CategoryCreateDTO categoryDTO: Дані для оновлення категорії.
Endpoint: DELETE /categories/{id}
Опис: Видаляє категорію за її ідентифікатором.
Параметри:
– UUID id: Ідентифікатор категорії.
146
ДОДАТОК Г
ОПТИМІЗАЦІЯ ПРОДУКТИВНОСТІ РОБОТИ З БАЗОЮ ДАНИХ
ВИСОКОНАВАНТАЖЕНОГО ДОДАТКУ НА ПРИКЛАДІ
ІНТЕРНЕТ-МАГАЗИНУ ТЕХНІКИ
Графічні матеріали
482.ЧДТУ.242322 90-01
Листів 13
Розробник ________________ Чоповенко В. М.
Черкаси 2024
482.ЧДТУ.242322 90-01
Рис. Г.1 - Титульний слайд
Рис. Г.2 - Слайд “Вступ”
148
482.ЧДТУ.242322 90-01
Рис. Г.3 - Слайд “Формалізація завдання”
Рис. Г.4 - Слайд “Гіпотеза”
149
482.ЧДТУ.242322 90-01
Рис. Г.5 - Слайд “Проведені експерименти”
Рис. Г.6 - Слайд “Проведені експерименти - продовження”
150
482.ЧДТУ.242322 90-01
Рис. Г.7 - Слайд “Результати експериментів”
Рис. Г.8 - Слайд “Результати експериментів - продовження”
151
482.ЧДТУ.242322 90-01
Рис. Г.9 - Слайд “Діаграма прецедентів”
Рис. Г.10 - Слайд “Проектування логічної структури (діаграма класів)”
152
482.ЧДТУ.242322 90-01
Рис. Г.11 - Слайд “Проектування логічної структури (діаграма класів -
продовження)”
Рис. Г.12 - Слайд “Проектування логічної структури (діаграма пакетів)”
153
482.ЧДТУ.242322 90-01
Рис. Г.13 - Слайд “Архітектурне проектування (діаграма компонентів)”
Рис. Г.14 - Слайд “Архітектурне проектування (діаграма розгортання)”
154
482.ЧДТУ.242322 90-01
Рис. Г.15 - Слайд “Моделювання поведінки системи (діаграма діяльності)”
Рис. Г.16 - Слайд “Моделювання поведінки системи (діаграма послідовності)”
155
482.ЧДТУ.242322 90-01
Рис. Г.17 - Слайд “Моделювання поведінки системи (діаграма послідовності -
продовження)”
Рис. Г.18 - Слайд “Моделювання поведінки системи (діаграма комунікації)”
156
482.ЧДТУ.242322 90-01
Рис. Г.19 - Слайд “Моделювання поведінки системи (діаграма скінченного
автомату)”
Рис. Г.20 - Слайд “Вибір засобів реалізації”
157
482.ЧДТУ.242322 90-01
Рис. Г.21 - Слайд “Функціональна схема системи”
Рис. Г.22 - Слайд “Логічна схема системи”
158
482.ЧДТУ.242322 90-01
Рис. Г.23 - Слайд “Інтерфейс користувача”
Рис. Г.24 - Слайд “Тестування”
159
482.ЧДТУ.242322 90-01
Рис. Г.25 - Слайд “Дякую за увагу”
160