315 lines
10 KiB
TeX
315 lines
10 KiB
TeX
% REST API architektúra
|
|
|
|
\section{REST API Architektúra}
|
|
|
|
\begin{frame}{Miért van szükség architektúrára?}
|
|
\begin{block}{A probléma: "Spagetti kód"}
|
|
Kezdő fejlesztők gyakran mindent egy helyre írnak:
|
|
\begin{itemize}
|
|
\item Adatbázis lekérdezések a route-okban
|
|
\item Üzleti logika a controller-ekben
|
|
\item Validáció szétszórva mindenhol
|
|
\end{itemize}
|
|
|
|
\textbf{Eredmény:} Áttekinthetetlen, nehezen karbantartható kód
|
|
\end{block}
|
|
|
|
\begin{exampleblock}{A megoldás: Rétegezett architektúra}
|
|
Minden funkciónak megvan a saját helye és felelőssége.
|
|
\end{exampleblock}
|
|
\end{frame}
|
|
|
|
\begin{frame}{5-rétegű architektúra}
|
|
\begin{block}{Separation of Concerns - Felelősségek szétválasztása}
|
|
\begin{itemize}
|
|
\item \textbf{Karbantarthatóság} - Könnyebb módosítás, hibakeresés
|
|
\item \textbf{Tesztelhetőség} - Rétegek külön tesztelhetők
|
|
\item \textbf{Skálázhatóság (Scalability)} - Független fejlesztés és skálázás
|
|
\item \textbf{Újrafelhasználhatóság} - Rétegek több helyen használhatók
|
|
\end{itemize}
|
|
\end{block}
|
|
|
|
\begin{alertblock}{Alapelv}
|
|
Minden réteg csak a saját felelősségével foglalkozik!
|
|
\end{alertblock}
|
|
\end{frame}
|
|
|
|
\begin{frame}{Az 5 réteg}
|
|
\begin{enumerate}
|
|
\item \textbf{API Layer (Presentation Layer)}
|
|
\begin{itemize}
|
|
\item HTTP kérések fogadása
|
|
\item Route-ok, Controller-ek
|
|
\end{itemize}
|
|
|
|
\item \textbf{Application Layer (Service Layer)}
|
|
\begin{itemize}
|
|
\item Üzleti logika koordinálása
|
|
\item Service-ek
|
|
\end{itemize}
|
|
|
|
\item \textbf{Domain Layer (Business Logic)}
|
|
\begin{itemize}
|
|
\item Üzleti szabályok
|
|
\item Model-ek, Entity-k
|
|
\end{itemize}
|
|
\end{enumerate}
|
|
\end{frame}
|
|
|
|
\begin{frame}{Az 5 réteg (folyt.)}
|
|
\begin{enumerate}
|
|
\setcounter{enumi}{3}
|
|
\item \textbf{Infrastructure Layer}
|
|
\begin{itemize}
|
|
\item Adatbázis hozzáférés
|
|
\item Repository-k, ORM
|
|
\end{itemize}
|
|
|
|
\item \textbf{Communication Layer}
|
|
\begin{itemize}
|
|
\item Adatok átalakítása
|
|
\item DTO-k, Validáció
|
|
\end{itemize}
|
|
\end{enumerate}
|
|
\end{frame}
|
|
|
|
\begin{frame}[shrink=10]{Rétegek áttekintése - Diagram}
|
|
\begin{center}
|
|
\begin{tikzpicture}[node distance=0.3cm]
|
|
\node[draw, rectangle, fill=blue!20, minimum width=9cm, minimum height=1cm, align=center] (api) {\textbf{API réteg (API Layer)}\\Útvonalak, Vezérlők (Routes, Controllers)};
|
|
|
|
\node[draw, rectangle, fill=green!20, minimum width=9cm, minimum height=1cm, below=of api, align=center] (app) {\textbf{Alkalmazás réteg (Application Layer)}\\Szolgáltatások (Services)};
|
|
|
|
\node[draw, rectangle, fill=yellow!20, minimum width=9cm, minimum height=1cm, below=of app, align=center] (domain) {\textbf{Tartomány réteg (Domain Layer)}\\Modellek, Entitások (Models, Entities)};
|
|
|
|
\node[draw, rectangle, fill=red!20, minimum width=9cm, minimum height=1cm, below=of domain, align=center] (infra) {\textbf{Infrastruktúra réteg (Infrastructure Layer)}\\Tárolók, Adatbázis (Repository, DB)};
|
|
|
|
\node[draw, rectangle, fill=purple!20, minimum width=9cm, minimum height=1cm, below=of infra, align=center] (comm) {\textbf{Kommunikációs réteg (Communication Layer)}\\Adatátviteli objektumok, Ellenőrzés (DTOs, Validation)};
|
|
|
|
\draw[->, thick] (api.south) -- (app.north);
|
|
\draw[->, thick] (app.south) -- (domain.north);
|
|
\draw[->, thick] (domain.south) -- (infra.north);
|
|
\draw[<->, thick] (api.east) -- ++(1,0) |- (comm.east);
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
\end{frame}
|
|
|
|
\begin{frame}[fragile,shrink=15]{API Layer - Route és Controller}
|
|
\begin{exampleblock}{Route példa}
|
|
\tiny
|
|
\begin{verbatim}
|
|
// routes/user.routes.js
|
|
const express = require('express');
|
|
const router = express.Router();
|
|
const userController = require('../controllers/user.controller');
|
|
|
|
router.get('/', userController.getAllUsers);
|
|
router.get('/:id', userController.getUserById);
|
|
router.post('/', userController.createUser);
|
|
|
|
module.exports = router;
|
|
\end{verbatim}
|
|
\end{exampleblock}
|
|
\end{frame}
|
|
|
|
\begin{frame}[fragile,shrink=15]{API Layer - Controller}
|
|
\begin{exampleblock}{Controller példa}
|
|
\tiny
|
|
\begin{verbatim}
|
|
// controllers/user.controller.js
|
|
const userService = require('../services/user.service');
|
|
|
|
exports.getAllUsers = async (req, res) => {
|
|
try {
|
|
const users = await userService.getAllUsers();
|
|
res.json(users);
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
};
|
|
|
|
exports.getUserById = async (req, res) => {
|
|
try {
|
|
const user = await userService.getUserById(req.params.id);
|
|
if (!user) return res.status(404).json({ error: 'Not found' });
|
|
res.json(user);
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
};
|
|
\end{verbatim}
|
|
\end{exampleblock}
|
|
\end{frame}
|
|
|
|
\begin{frame}[fragile,shrink=15]{Application Layer - Service}
|
|
\begin{exampleblock}{Service példa}
|
|
\tiny
|
|
\begin{verbatim}
|
|
// services/user.service.js
|
|
const userRepository = require('../repositories/user.repository');
|
|
|
|
exports.getAllUsers = async () => {
|
|
return await userRepository.findAll();
|
|
};
|
|
|
|
exports.getUserById = async (id) => {
|
|
return await userRepository.findById(id);
|
|
};
|
|
|
|
exports.createUser = async (userData) => {
|
|
// Üzleti logika validálás
|
|
if (!userData.email || !userData.name) {
|
|
throw new Error('Email and name are required');
|
|
}
|
|
return await userRepository.create(userData);
|
|
};
|
|
\end{verbatim}
|
|
\end{exampleblock}
|
|
\end{frame}
|
|
|
|
\begin{frame}[fragile,shrink=15]{Domain Layer - Model/Entity}
|
|
\begin{exampleblock}{User Model}
|
|
\tiny
|
|
\begin{verbatim}
|
|
// models/user.model.js
|
|
class User {
|
|
constructor(id, name, email, createdAt) {
|
|
this.id = id;
|
|
this.name = name;
|
|
this.email = email;
|
|
this.createdAt = createdAt || new Date();
|
|
}
|
|
|
|
// Üzleti logika metódusok
|
|
isActive() {
|
|
return this.active === true;
|
|
}
|
|
|
|
hasRole(role) {
|
|
return this.roles.includes(role);
|
|
}
|
|
}
|
|
|
|
module.exports = User;
|
|
\end{verbatim}
|
|
\end{exampleblock}
|
|
\end{frame}
|
|
|
|
\begin{frame}[fragile,shrink=15]{Infrastructure Layer - Repository}
|
|
\begin{exampleblock}{Repository példa}
|
|
\tiny
|
|
\begin{verbatim}
|
|
// repositories/user.repository.js
|
|
const db = require('../config/database');
|
|
|
|
exports.findAll = async () => {
|
|
return await db.query('SELECT * FROM users');
|
|
};
|
|
|
|
exports.findById = async (id) => {
|
|
const result = await db.query('SELECT * FROM users WHERE id = ?', [id]);
|
|
return result[0];
|
|
};
|
|
|
|
exports.create = async (userData) => {
|
|
const result = await db.query(
|
|
'INSERT INTO users (name, email) VALUES (?, ?)',
|
|
[userData.name, userData.email]
|
|
);
|
|
return { id: result.insertId, ...userData };
|
|
};
|
|
\end{verbatim}
|
|
\end{exampleblock}
|
|
\end{frame}
|
|
|
|
\begin{frame}[fragile,shrink=15]{Communication Layer - DTO}
|
|
\begin{exampleblock}{Data Transfer Object}
|
|
\tiny
|
|
\begin{verbatim}
|
|
// dtos/user.dto.js
|
|
class UserDTO {
|
|
constructor(user) {
|
|
this.id = user.id;
|
|
this.name = user.name;
|
|
this.email = user.email;
|
|
// Érzékeny mezők nem kerülnek át (pl. password)
|
|
}
|
|
}
|
|
|
|
class CreateUserDTO {
|
|
constructor(data) {
|
|
this.name = data.name;
|
|
this.email = data.email;
|
|
this.password = data.password;
|
|
}
|
|
|
|
validate() {
|
|
if (!this.email || !this.name || !this.password) {
|
|
throw new Error('All fields required');
|
|
}
|
|
}
|
|
}
|
|
\end{verbatim}
|
|
\end{exampleblock}
|
|
\end{frame}
|
|
|
|
\begin{frame}{Projekt struktúra - Mappa felépítés}
|
|
\begin{block}{Az 5-rétegű architektúra a mappákban}
|
|
\small
|
|
\begin{itemize}
|
|
\item \textbf{\texttt{src/}} - Forráskód gyökér
|
|
\item \quad \texttt{routes/} - \textcolor{blue}{API réteg}: útvonalak definiálása
|
|
\item \quad \texttt{controllers/} - \textcolor{blue}{API réteg}: HTTP kérések kezelése
|
|
\item \quad \texttt{services/} - \textcolor{green}{Alkalmazás réteg}: üzleti logika
|
|
\item \quad \texttt{models/} - \textcolor{orange}{Tartomány réteg}: adatmodellek
|
|
\item \quad \texttt{repositories/} - \textcolor{red}{Infrastruktúra réteg}: DB műveletek
|
|
\item \quad \texttt{dtos/} - \textcolor{purple}{Kommunikáció réteg}: adatátviteli objektumok
|
|
\item \quad \texttt{middlewares/} - Köztes réteg (autentikáció, naplózás)
|
|
\item \quad \texttt{config/} - Konfigurációs fájlok
|
|
\end{itemize}
|
|
\end{block}
|
|
\end{frame}
|
|
|
|
\begin{frame}{Projekt struktúra - Függőségek}
|
|
\begin{block}{Kommunikáció a rétegek között}
|
|
\begin{itemize}
|
|
\item \textbf{Routes} $\rightarrow$ Controllers (útvonal hívja a vezérlőt)
|
|
\item \textbf{Controllers} $\rightarrow$ Services (vezérlő hívja a szolgáltatást)
|
|
\item \textbf{Services} $\rightarrow$ Models + Repositories (szolgáltatás használja az adatréteget)
|
|
\item \textbf{Repositories} $\rightarrow$ Database (tároló eléri az adatbázist)
|
|
\item \textbf{DTOs} $\leftrightarrow$ Minden réteg (minden réteg használhat DTO-kat)
|
|
\end{itemize}
|
|
\end{block}
|
|
|
|
\begin{alertblock}{Fontos szabály}
|
|
A belső rétegek NEM függhetnek a külső rétegektől! A függőségek mindig befelé mutatnak.
|
|
\end{alertblock}
|
|
\end{frame}
|
|
|
|
\begin{frame}{Clean Architecture}
|
|
\begin{block}{Dependency Rule}
|
|
Belső rétegek nem függhetnek külső rétegektől. A függőségek mindig befelé mutatnak.
|
|
\end{block}
|
|
|
|
\begin{exampleblock}{Előnyök}
|
|
\begin{itemize}
|
|
\item Független a framework-től
|
|
\item Tesztelhető
|
|
\item Független az UI-tól
|
|
\item Független az adatbázistól
|
|
\item Független külső szolgáltatásoktól
|
|
\end{itemize}
|
|
\end{exampleblock}
|
|
\end{frame}
|
|
|
|
\begin{frame}{Összefoglalás - Architektúra}
|
|
\begin{itemize}
|
|
\item 5-rétegű architektúra a clean code érdekében
|
|
\item API Layer (API réteg): Route-ok és Controller-ek (útvonalak, vezérlők)
|
|
\item Application Layer (Alkalmazás réteg): Service-ek (szolgáltatások - üzleti logika)
|
|
\item Domain Layer (Tartomány réteg): Model-ek és Entity-k (modellek, entitások)
|
|
\item Infrastructure Layer (Infrastruktúra réteg): Repository-k és DB (tárolók, adatbázis)
|
|
\item Communication Layer (Kommunikációs réteg): DTO-k és validáció (adatátvitel, ellenőrzés)
|
|
\end{itemize}
|
|
\end{frame}
|