database added
This commit is contained in:
@@ -0,0 +1,486 @@
|
||||
\section{DTO}
|
||||
|
||||
\begin{frame}{Mi az a DTO?}
|
||||
\begin{block}{Data Transfer Object}
|
||||
\begin{itemize}
|
||||
\item Adatok szállítására szolgáló objektum
|
||||
\item Elválasztja az adatbázis sémát a kliens felőli interfésztől
|
||||
\item Kontroll az átadott adatok felett
|
||||
\item Validáció és transzformáció helye
|
||||
\end{itemize}
|
||||
\end{block}
|
||||
|
||||
\begin{exampleblock}{Példa}
|
||||
Felhasználó entitás tartalmaz jelszó hash-t, de a DTO nem adja ki.
|
||||
\end{exampleblock}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}{Miért használjunk DTO-t?}
|
||||
\begin{block}{Előnyök}
|
||||
\begin{itemize}
|
||||
\item \textbf{Biztonság} - Érzékeny mezők elrejtése (pl. jelszó)
|
||||
\item \textbf{Verziókezelés} - API változhat a DB séma változtatása nélkül
|
||||
\item \textbf{Validáció} - Bejövő adatok ellenőrzése
|
||||
\item \textbf{Dokumentáció} - Világos szerződés a kliens és szerver között
|
||||
\item \textbf{Transzformáció} - Adatok átalakítása (pl. dátum formázás)
|
||||
\end{itemize}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Prisma modell vs DTO}
|
||||
\begin{columns}
|
||||
\begin{column}{0.48\textwidth}
|
||||
\begin{block}{Prisma Model}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
model User {
|
||||
id Int @id
|
||||
@default(autoincrement())
|
||||
email String @unique
|
||||
password String
|
||||
name String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{column}
|
||||
\begin{column}{0.48\textwidth}
|
||||
\begin{block}{UserDTO (kimenet)}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
{
|
||||
id: 1,
|
||||
email: "john@example.com",
|
||||
name: "John Doe"
|
||||
// password nincs benne!
|
||||
// createdAt, updatedAt
|
||||
// opcionális
|
||||
}
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{column}
|
||||
\end{columns}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Input DTO - Létrehozás}
|
||||
\begin{block}{CreateUserDTO - Constructor}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
class CreateUserDTO {
|
||||
constructor(data) {
|
||||
this.email = data.email;
|
||||
this.password = data.password;
|
||||
this.name = data.name;
|
||||
}
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Input DTO - Validáció}
|
||||
\begin{block}{CreateUserDTO - Validate módszer}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
class CreateUserDTO {
|
||||
// ...
|
||||
|
||||
validate() {
|
||||
if (!this.email || !this.email.includes('@')) {
|
||||
throw new Error('Érvénytelen email cím');
|
||||
}
|
||||
if (!this.password || this.password.length < 8) {
|
||||
throw new Error('Jelszó túl rövid (min. 8 karakter)');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Output DTO - Válasz}
|
||||
\begin{block}{UserResponseDTO}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
class UserResponseDTO {
|
||||
constructor(user) {
|
||||
this.id = user.id;
|
||||
this.email = user.email;
|
||||
this.name = user.name;
|
||||
// password nincs másolva!
|
||||
}
|
||||
static fromPrismaUser(user) {
|
||||
return new UserResponseDTO(user);
|
||||
}
|
||||
static fromManyPrismaUsers(users) {
|
||||
return users.map(u => new UserResponseDTO(u));
|
||||
}
|
||||
}
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{DTO használata service-ben}
|
||||
\begin{block}{UserService példa}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
const prisma = require('./prisma');
|
||||
const bcrypt = require('bcrypt');
|
||||
class UserService {
|
||||
async createUser(createUserDTO) {
|
||||
createUserDTO.validate(); // Validálás
|
||||
const hashedPassword = await bcrypt.hash(
|
||||
createUserDTO.password, 10
|
||||
); // Jelszó hash-elés
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
email: createUserDTO.email,
|
||||
password: hashedPassword,
|
||||
name: createUserDTO.name
|
||||
}// Mentés Prisma-val
|
||||
});
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{DTO használata service-ben (folyt.)}
|
||||
\begin{block}{UserService példa folytatás}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
// DTO-vá alakítás visszaadás előtt
|
||||
return UserResponseDTO.fromPrismaUser(user);
|
||||
}
|
||||
async getAllUsers() {
|
||||
const users = await prisma.user.findMany();
|
||||
return UserResponseDTO.fromManyPrismaUsers(users);
|
||||
}
|
||||
async getUserById(id) {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id }
|
||||
});
|
||||
if (!user) return null;
|
||||
return UserResponseDTO.fromPrismaUser(user);
|
||||
}
|
||||
}
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Express integráció DTO-val}
|
||||
\begin{block}{REST endpoint}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
const express = require('express');
|
||||
const userService = new UserService();
|
||||
const app = express();
|
||||
app.post('/api/users', async (req, res) => {
|
||||
try {
|
||||
const createDTO = new CreateUserDTO(req.body);
|
||||
const userDTO = await userService.createUser(createDTO);
|
||||
res.status(201).json(userDTO);
|
||||
} catch (error) {
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
app.get('/api/users', async (req, res) => {
|
||||
const usersDTO = await userService.getAllUsers();
|
||||
res.json(usersDTO);
|
||||
});
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Update DTO - Constructor}
|
||||
\begin{block}{UpdateUserDTO}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
class UpdateUserDTO {
|
||||
constructor(data) {
|
||||
// Csak a megadott mezők kerülnek be
|
||||
if (data.email !== undefined)
|
||||
this.email = data.email;
|
||||
if (data.name !== undefined)
|
||||
this.name = data.name;
|
||||
if (data.password !== undefined)
|
||||
this.password = data.password;
|
||||
}
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Update DTO - Validáció}
|
||||
\begin{block}{UpdateUserDTO - Validate}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
class UpdateUserDTO {
|
||||
// ...
|
||||
|
||||
validate() {
|
||||
if (this.email && !this.email.includes('@')) {
|
||||
throw new Error('Érvénytelen email');
|
||||
}
|
||||
if (this.password && this.password.length < 8) {
|
||||
throw new Error('Jelszó túl rövid');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Nested DTO - Relációkkal}
|
||||
\begin{block}{PostWithAuthorDTO}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
class PostWithAuthorDTO {
|
||||
constructor(post) {
|
||||
this.id = post.id;
|
||||
this.title = post.title;
|
||||
this.content = post.content;
|
||||
this.createdAt = post.createdAt;
|
||||
// Nested DTO az author-hoz
|
||||
if (post.author) {
|
||||
this.author = new UserResponseDTO(post.author);
|
||||
}
|
||||
}
|
||||
static fromPrismaPost(post) {
|
||||
return new PostWithAuthorDTO(post);
|
||||
}
|
||||
}
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Nested DTO használata}
|
||||
\begin{block}{Service metódus}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
class PostService {
|
||||
async getPostWithAuthor(postId) {
|
||||
const post = await prisma.post.findUnique({
|
||||
where: { id: postId },
|
||||
include: {
|
||||
author: true
|
||||
}
|
||||
});
|
||||
|
||||
if (!post) return null;
|
||||
|
||||
return PostWithAuthorDTO.fromPrismaPost(post);
|
||||
}
|
||||
}
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Validációs könyvtár - Joi}
|
||||
\begin{block}{Joi telepítése}
|
||||
\begin{lstlisting}[language=bash]
|
||||
npm install joi
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
|
||||
\begin{block}{Validációs séma}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
const Joi = require('joi');
|
||||
|
||||
const createUserSchema = Joi.object({
|
||||
email: Joi.string().email().required(),
|
||||
password: Joi.string().min(8).required(),
|
||||
name: Joi.string().optional()
|
||||
});
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{DTO Joi validációval}
|
||||
\begin{block}{CreateUserDTO sémával}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
class CreateUserDTO {
|
||||
constructor(data) {
|
||||
const { error, value } =
|
||||
createUserSchema.validate(data);
|
||||
if (error)
|
||||
throw new Error(error.details[0].message);
|
||||
Object.assign(this, value);
|
||||
}
|
||||
}
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Pagináció DTO}
|
||||
\begin{block}{PaginatedResponseDTO}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
class PaginatedResponseDTO {
|
||||
constructor(data, total, page, pageSize) {
|
||||
this.data = data;
|
||||
this.pagination = {
|
||||
total, page, pageSize,
|
||||
totalPages: Math.ceil(total / pageSize)
|
||||
};
|
||||
}
|
||||
}
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Pagináció DTO használata}
|
||||
\begin{block}{PaginatedResponseDTOUsage}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
// Használat
|
||||
const users = await prisma.user.findMany({
|
||||
skip: (page - 1) * pageSize, take: pageSize
|
||||
});
|
||||
const total = await prisma.user.count();
|
||||
return new PaginatedResponseDTO(
|
||||
UserResponseDTO.fromManyPrismaUsers(users),
|
||||
total, page, pageSize
|
||||
);
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Szűrési DTO - Constructor}
|
||||
\begin{block}{UserFilterDTO}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
class UserFilterDTO {
|
||||
constructor(query) {
|
||||
this.email = query.email;
|
||||
this.name = query.name;
|
||||
this.page = parseInt(query.page) || 1;
|
||||
this.pageSize = parseInt(query.pageSize) || 10;
|
||||
}
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Szűrési DTO - Prisma konverzió}
|
||||
\begin{block}{toPrismaWhere módszer}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
class UserFilterDTO {
|
||||
// ...
|
||||
|
||||
toPrismaWhere() {
|
||||
const where = {};
|
||||
if (this.email)
|
||||
where.email = { contains: this.email };
|
||||
if (this.name)
|
||||
where.name = { contains: this.name };
|
||||
return where;
|
||||
}
|
||||
}
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Szűrési DTO használata}
|
||||
\begin{block}{Express endpoint szűréssel}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
app.get('/api/users', async (req, res) => {
|
||||
const filterDTO = new UserFilterDTO(req.query);
|
||||
const where = filterDTO.toPrismaWhere();
|
||||
const users = await prisma.user.findMany({
|
||||
where,
|
||||
skip: (filterDTO.page - 1) * filterDTO.pageSize,
|
||||
take: filterDTO.pageSize
|
||||
});
|
||||
const total = await prisma.user.count({ where });
|
||||
const result = new PaginatedResponseDTO(
|
||||
UserResponseDTO.fromManyPrismaUsers(users),
|
||||
total, filterDTO.page, filterDTO.pageSize
|
||||
);
|
||||
res.json(result);
|
||||
});
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Mapper függvények}
|
||||
\begin{block}{Alternatív megközelítés - Pure functions // dto/user.mapper.js}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
function toUserResponseDTO(user) {
|
||||
return {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name
|
||||
};
|
||||
}
|
||||
function toUserResponseDTOs(users) {
|
||||
return users.map(toUserResponseDTO);
|
||||
}
|
||||
module.exports = {
|
||||
toUserResponseDTO,
|
||||
toUserResponseDTOs
|
||||
};
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Mapper használata}
|
||||
\begin{block}{Service-ben}
|
||||
\begin{lstlisting}[language=JavaScript]
|
||||
const { toUserResponseDTO, toUserResponseDTOs }
|
||||
= require('./dto/user.mapper');
|
||||
class UserService {
|
||||
async getAllUsers() {
|
||||
const users = await prisma.user.findMany();
|
||||
return toUserResponseDTOs(users);
|
||||
}
|
||||
async getUserById(id) {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id }
|
||||
});
|
||||
if (!user) return null;
|
||||
return toUserResponseDTO(user);
|
||||
}
|
||||
}
|
||||
\end{lstlisting}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}{DTO Best Practices}
|
||||
\begin{block}{Ajánlások}
|
||||
\begin{itemize}
|
||||
\item Külön DTO minden use case-hez (Create, Update, Response)
|
||||
\item Ne add vissza közvetlenül a Prisma modellt
|
||||
\item Validálj minden bejövő adatot
|
||||
\item Használj validációs könyvtárat (Joi, Yup, Zod)
|
||||
\item Tartsd egyszerűnek - ne túlbonyolítsd
|
||||
\item Dokumentáld a DTO mezőket
|
||||
\end{itemize}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}{DTO vs Prisma Select}
|
||||
\begin{columns}
|
||||
\begin{column}{0.48\textwidth}
|
||||
\begin{block}{Prisma Select}
|
||||
\begin{itemize}
|
||||
\item Egyszerű mezők kiválasztása
|
||||
\item Kevesebb kód
|
||||
\item Nincs validáció
|
||||
\item DB szintű szűrés
|
||||
\end{itemize}
|
||||
\end{block}
|
||||
\end{column}
|
||||
\begin{column}{0.48\textwidth}
|
||||
\begin{block}{DTO}
|
||||
\begin{itemize}
|
||||
\item Komplex transzformációk
|
||||
\item Validáció és logika
|
||||
\item Típusbiztonság
|
||||
\item API verziókezelés
|
||||
\end{itemize}
|
||||
\end{block}
|
||||
\end{column}
|
||||
\end{columns}
|
||||
|
||||
\vspace{0.5cm}
|
||||
|
||||
\begin{alertblock}{Kombináld őket!}
|
||||
Használd a Prisma select-et a DB szinten, és DTO-t a transzformációhoz.
|
||||
\end{alertblock}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}{Összefoglalás}
|
||||
\begin{block}{DTO előnyei}
|
||||
\begin{itemize}
|
||||
\item \textbf{Biztonság} - Érzékeny adatok elrejtése
|
||||
\item \textbf{Validáció} - Adatok ellenőrzése
|
||||
\item \textbf{Transzformáció} - Adatok átalakítása
|
||||
\item \textbf{Dokumentáció} - Világos API szerződés
|
||||
\item \textbf{Karbantarthatóság} - Könnyebb változtatások
|
||||
\item \textbf{Tesztelhetőség} - Egyszerűbb unit tesztek
|
||||
\end{itemize}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
Reference in New Issue
Block a user