2.1 Arquitectura general
En esta etapa vamos a diseñar la arquitectura de una aplicación para la gestión de tareas. Vamos a definir cómo interactuarán el frontend, el backend y la base de datos, así como qué componentes estarán incluidos en cada uno de ellos.
La aplicación se compondrá de tres componentes principales:
- Frontend (ReactJS): la parte del cliente, que garantiza la interacción del usuario con el sistema.
- Backend (Flask): la parte del servidor, que procesa las solicitudes del frontend e interactúa con la base de datos.
- Database (PostgreSQL): el almacenamiento de datos para los usuarios y las tareas.
La arquitectura se verá de la siguiente manera:
+-------------+ +-------------+ +--------------+
| | | | | |
| Frontend +------->+ Backend +------->+ Database |
| (ReactJS) | | (Flask) | | (PostgreSQL) |
| | | | | |
+-------------+ +-------------+ +--------------+
Interacción entre los componentes
- Frontend: envía solicitudes HTTP al backend para realizar operaciones CRUD (crear, leer, actualizar, eliminar tareas).
- Backend: procesa las solicitudes HTTP del frontend, realiza la lógica de negocio e interactúa con la base de datos.
- Database: guarda y proporciona datos según las solicitudes del backend.
2.2 Descripción de cada componente
1. Frontend (ReactJS):
- Componentes de la interfaz: componentes para registro y autorización de usuarios, creación y edición de tareas, visualización de la lista de tareas.
- Interacción con API: uso de la biblioteca Axios para enviar solicitudes HTTP al backend.
2. Backend (Flask):
- REST API: implementación de endpoints para la gestión de usuarios y tareas.
- Modelos de datos: definición de modelos de datos para usuarios y tareas usando SQLAlchemy.
- Lógica de negocio: procesamiento de la lógica de la aplicación, incluyendo validación de datos y gestión de sesiones de usuario.
3. Database (PostgreSQL):
- Tablas: tablas para almacenar información sobre usuarios y tareas.
- Relaciones entre tablas: definición de relaciones entre las tablas de usuarios y tareas (por ejemplo, un usuario puede tener muchas tareas).
4. Interacción en red
Toda la comunicación entre los componentes se realizará a través del protocolo HTTP. El frontend interactuará con el backend a través de REST API, y el backend interactuará con la base de datos mediante consultas SQL.
- Frontend: puerto 3000 para desarrollo y pruebas.
- Backend: puerto 5000 para interactuar con el frontend.
- Database: puerto 5432 para interactuar con el backend.
2.3 Descripción detallada de cada componente
1. Estructura básica de datos
Para almacenar datos sobre usuarios y tareas en la base de datos PostgreSQL, crearemos dos tablas: users
y tasks
.
Tabla users
:
-
id (int, primary key)
: identificador único del usuario. username (varchar, unique)
: nombre del usuario.password (varchar)
: hash de la contraseña del usuario.
Tabla tasks
:
-
id (int, primary key)
: identificador único de la tarea. title (varchar)
: título de la tarea.description (text)
: descripción de la tarea.-
owner_id (int, foreign key)
: identificador del usuario asignado a la tarea. -
status (varchar)
: estado de la tarea (por ejemplo, completada/no completada).
2. Diseño del API
El backend proporcionará un RESTful API para interactuar con el frontend. Lista aproximada de endpoints:
- Usuarios:
- POST /users: creación de un nuevo usuario.
- GET /users: obtención de la lista de todos los usuarios.
- GET /users/:id: obtención de información sobre un usuario específico.
- PUT /users/:id: actualización de la información de un usuario.
- DELETE /users/:id: eliminación de un usuario.
- Tareas:
- POST /tasks: creación de una nueva tarea.
- GET /tasks: obtención de la lista de todas las tareas.
- GET /tasks/:id: obtención de información sobre una tarea específica.
- PUT /tasks/:id: actualización de la información de una tarea.
- DELETE /tasks/:id: eliminación de una tarea.
2.4 Modelos de datos
Así se verá el código en Python para trabajar con tablas de una base de datos:
Modelo User:
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
}
Modelo Task:
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="no completada")
def to_dict(self):
return {
"id": self.id,
"title": self.title,
"description": self.description,
"owner_id": self.owner_id,
"status": self.status
}
2.5 Rutas y controladores
Ejemplo de implementación de API en el lado del servidor:
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', "no completada")
)
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 Ejemplo de solicitud al servidor desde el frontend
Ejemplo de un componente React para mostrar una lista de tareas:
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('¡Hubo un error al obtener las tareas!', error);
});
}, []);
return (
<div>
<h1>Lista de Tareas</h1>
<ul>
{tasks.map(task => (
<li key={task.id}>{task.title} - {task.status}</li>
))}
</ul>
</div>
);
};
export default TaskList;
GO TO FULL VERSION