CodeGym /Kursy /Docker SELF /Projektowanie architektury aplikacji

Projektowanie architektury aplikacji

Docker SELF
Poziom 23 , Lekcja 1
Dostępny

2.1 Ogólna architektura

Na tym etapie zaprojektujemy architekturę aplikacji do zarządzania zadaniami. Określimy, jak będą współdziałać frontend, backend i baza danych oraz jakie komponenty będą zawarte w każdym z nich.

Aplikacja będzie składała się z trzech głównych komponentów:

  • Frontend (ReactJS): część kliencka, zapewniająca interakcję użytkownika z systemem.
  • Backend (Flask): część serwerowa, przetwarzająca żądania od frontendu i współdziałająca z bazą danych.
  • Database (PostgreSQL): magazyn danych dla użytkowników i zadań.

Architektura będzie wyglądała następująco:

Terminal

+-------------+       +-------------+       +--------------+
|             |       |             |       |              |
|  Frontend   +------->+   Backend   +------->+   Database   |
|  (ReactJS)  |       |   (Flask)   |       | (PostgreSQL) |
|             |       |             |       |              |
+-------------+       +-------------+       +--------------+

Współpraca między komponentami

  • Frontend: wysyła żądania HTTP do backendu w celu wykonania operacji CRUD (tworzenie, odczyt, aktualizacja, usuwanie zadań).
  • Backend: przetwarza żądania HTTP od frontendu, wykonuje logikę biznesową i współdziała z bazą danych.
  • Database: przechowuje i dostarcza dane na żądanie od backendu.

2.2 Opis każdego komponentu

1. Frontend (ReactJS):

  • Komponenty interfejsu: komponenty do rejestracji i logowania użytkowników, tworzenia i edytowania zadań, przeglądania listy zadań.
  • Interakcja z API: użycie biblioteki Axios do wysyłania HTTP requestów do backendu.

2. Backend (Flask):

  • REST API: implementacja endpointów do zarządzania użytkownikami i zadaniami.
  • Modele danych: definiowanie modeli danych dla użytkowników i zadań z użyciem SQLAlchemy.
  • Logika biznesowa: obsługa logiki aplikacji, w tym walidacja danych i zarządzanie sesjami użytkowników.

3. Database (PostgreSQL):

  • Tabele: tabele do przechowywania informacji o użytkownikach i zadaniach.
  • Relacje między tabelami: definiowanie relacji między tabelami użytkowników i zadań (np. jeden użytkownik może mieć wiele zadań).

4. Sieciowa interakcja

Cała komunikacja między komponentami będzie odbywać się przez protokół HTTP. Frontend będzie komunikować się z backendem przez REST API, a backend — z bazą danych przez zapytania SQL.

  • Frontend: port 3000 do rozwoju i testowania.
  • Backend: port 5000 do interakcji z frontendem.
  • Database: port 5432 do interakcji z backendem.

2.3 Szczegółowy opis każdego komponentu

1. Podstawowa struktura danych

Do przechowywania danych o użytkownikach i zadaniach w bazie danych PostgreSQL stworzymy dwie tabele: users i tasks.

Tablica users:

  • id (int, primary key): unikalny identyfikator użytkownika.
  • username (varchar, unique): nazwa użytkownika.
  • password (varchar): hash hasła użytkownika.

Tablica tasks:

  • id (int, primary key): unikalny identyfikator zadania.
  • title (varchar): tytuł zadania.
  • description (text): opis zadania.
  • owner_id (int, foreign key): identyfikator użytkownika, któremu zadanie jest przypisane.
  • status (varchar): status zadania (na przykład, wykonane/niewykonane).

2. Projektowanie API

Backend będzie dostarczać RESTful API do interakcji z frontendem. Przykładowa lista endpointów:

  • Użytkownicy:
    • POST /users: tworzenie nowego użytkownika.
    • GET /users: uzyskanie listy wszystkich użytkowników.
    • GET /users/:id: uzyskanie informacji o konkretnym użytkowniku.
    • PUT /users/:id: aktualizacja informacji o użytkowniku.
    • DELETE /users/:id: usunięcie użytkownika.
  • Zadania:
    • POST /tasks: tworzenie nowego zadania.
    • GET /tasks: uzyskanie listy wszystkich zadań.
    • GET /tasks/:id: uzyskanie informacji o konkretnym zadaniu.
    • PUT /tasks/:id: aktualizacja informacji o zadaniu.
    • DELETE /tasks/:id: usunięcie zadania.

2.4 Modele danych

Tak będzie wyglądał kod w Python do pracy z tabelami bazy danych:

Model Użytkownika:

Python

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
        }

Model Zadania:

Python

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="niewykonana")
    def to_dict(self):
        return {
            "id": self.id,
            "title": self.title,
            "description": self.description,
            "owner_id": self.owner_id,
            "status": self.status
        }

2.5 Trasy i kontrolery

Przykład implementacji API po stronie serwera:

Python

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', "niewykonane")
    )
    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 Przykład komunikacji z serwerem od strony frontendu

Przykład komponentu React do wyświetlania listy zadań:

Javascript

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('Wystąpił błąd podczas pobierania zadań!', error);
      });
  }, []);

  return (
    <div>
      <h1>Lista zadań</h1>
      <ul>
        {tasks.map(task => (
          <li key={task.id}>{task.title} - {task.status}</li>
        ))}
      </ul>
    </div>
  );
};

export default TaskList;
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION