Files
2026-01-28 02:29:38 +01:00

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}