2.1 Загальна архітектура
На цьому етапі ми спроєктуємо архітектуру застосунку для управління задачами. Ми визначимо, як буде взаємодіяти frontend, backend і база даних, а також які компоненти увійдуть у кожен із них.
Застосунок складатиметься з трьох основних компонентів:
- Frontend (ReactJS): клієнтська частина, що забезпечує взаємодію користувача із системою.
- Backend (Flask): серверна частина, яка обробляє запити від фронтенду і взаємодіє з базою даних.
- Database (PostgreSQL): сховище даних для користувачів і задач.
Архітектура виглядатиме наступним чином:
+-------------+ +-------------+ +--------------+
| | | | | |
| Frontend +------->+ Backend +------->+ Database |
| (ReactJS) | | (Flask) | | (PostgreSQL) |
| | | | | |
+-------------+ +-------------+ +--------------+
Взаємодія між компонентами
- Frontend: відправляє HTTP-запити до бекенду для виконання CRUD-операцій (створення, читання, оновлення, видалення задач).
- Backend: обробляє HTTP-запити від фронтенду, виконує бізнес-логіку і взаємодіє з базою даних.
- Database: зберігає та надає дані за запитами від бекенду.
2.2 Опис кожного компонента
1. Frontend (ReactJS):
- Компоненти інтерфейсу: компоненти для реєстрації та авторизації користувачів, створення й редагування завдань, перегляду списку завдань.
- Взаємодія з API: використання бібліотеки Axios для відправки HTTP-запитів до бекенду.
2. Backend (Flask):
- REST API: реалізація ендпоінтів для управління користувачами та завданнями.
- Моделі даних: визначення моделей даних для користувачів і завдань з використанням SQLAlchemy.
- Бізнес-логіка: обробка логіки застосунку, включаючи валідацію даних та управління сесіями користувачів.
3. Database (PostgreSQL):
- Таблиці: таблиці для зберігання інформації про користувачів і завдання.
- Зв’язки між таблицями: визначення зв'язків між таблицями користувачів і завдань (наприклад, один користувач може мати багато завдань).
4. Мережна взаємодія
Уся комунікація між компонентами відбуватиметься через HTTP-протокол. Фронтенд буде взаємодіяти з бекендом через REST API, а бекенд — з базою даних через SQL-запити.
- Frontend: порт 3000 для розробки та тестування.
- Backend: порт 5000 для взаємодії з фронтендом.
- Database: порт 5432 для взаємодії з бекендом.
2.3 Детальний опис кожного компонента
1. Базова структура даних
Для зберігання даних про користувачів і задачі у базі даних PostgreSQL створимо дві таблиці: users і tasks.
Таблиця users:
-
id (int, primary key): унікальний ідентифікатор користувача. username (varchar, unique): ім'я користувача.password (varchar): хеш пароля користувача.
Таблиця tasks:
-
id (int, primary key): унікальний ідентифікатор задачі. title (varchar): заголовок задачі.description (text): опис задачі.-
owner_id (int, foreign key): ідентифікатор користувача, якому призначена задача. -
status (varchar): статус задачі (наприклад, виконана/невиконана).
2. API дизайн
Бекенд надаватиме RESTful API для взаємодії із фронтендом. Приблизний список ендпоінтів:
- Користувачі:
- POST /users: створення нового користувача.
- GET /users: отримання списку всіх користувачів.
- GET /users/:id: отримання інформації про конкретного користувача.
- PUT /users/:id: оновлення інформації про користувача.
- DELETE /users/:id: видалення користувача.
- Задачі:
- POST /tasks: створення нової задачі.
- GET /tasks: отримання списку всіх задач.
- GET /tasks/:id: отримання інформації про конкретну задачу.
- PUT /tasks/:id: оновлення інформації про задачу.
- DELETE /tasks/:id: видалення задачі.
2.4 Моделі даних
Ось як виглядатиме код на Python для роботи з таблицями бази даних:
User Model:
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password = db.Column(db.String(120), nullable=False)
tasks = db.relationship('Task', backref='owner', lazy=True)
def to_dict(self):
return {
"id": self.id,
"username": self.username
}
Task Model:
from app import db
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(120), nullable=False)
description = db.Column(db.Text, nullable=True)
owner_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
status = db.Column(db.String(20), nullable=False, default="невиконана")
def to_dict(self):
return {
"id": self.id,
"title": self.title,
"description": self.description,
"owner_id": self.owner_id,
"status": self.status
}
2.5 Маршрути і контролери
Приклад реалізації API на стороні сервера:
from app import app, db
from app.models import Task, User
from flask import request, jsonify
@app.route('/tasks', methods=['GET'])
def get_tasks():
tasks = Task.query.all()
return jsonify([task.to_dict() for task in tasks])
@app.route('/tasks', methods=['POST'])
def create_task():
data = request.get_json()
new_task = Task(
title=data['title'],
description=data.get('description'),
owner_id=data['owner_id'],
status=data.get('status', "невиконана")
)
db.session.add(new_task)
db.session.commit()
return jsonify(new_task.to_dict()), 201
@app.route('/tasks/<int:id>', methods=['GET'])
def get_task(id):
task = Task.query.get_or_404(id)
return jsonify(task.to_dict())
@app.route('/tasks/<int:id>', methods=['PUT'])
def update_task(id):
data = request.get_json()
task = Task.query.get_or_404(id)
task.title = data['title']
task.description = data.get('description')
task.status = data.get('status', task.status)
task.owner_id = data['owner_id']
db.session.commit()
return jsonify(task.to_dict())
@app.route('/tasks/<int:id>', methods=['DELETE'])
def delete_task(id):
task = Task.query.get_or_404(id)
db.session.delete(task)
db.session.commit()
return '', 204
2.6 Приклад звернення до сервера з боку фронтенда
Приклад компонента React для відображення списку задач:
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const TaskList = () => {
const [tasks, setTasks] = useState([]);
useEffect(() => {
axios.get('http://localhost:5000/tasks')
.then(response => {
setTasks(response.data);
})
.catch(error => {
console.error('Сталася помилка під час отримання задач!', error);
});
}, []);
return (
<div>
<h1>Список задач</h1>
<ul>
{tasks.map(task => (
<li key={task.id}>{task.title} - {task.status}</li>
))}
</ul>
</div>
);
};
export default TaskList;
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ