This commit is contained in:
magdo
2026-02-17 21:17:42 +01:00
parent d90a6ed735
commit af57733506
17 changed files with 4194 additions and 59 deletions
+22 -6
View File
@@ -18,7 +18,7 @@
\end{frame}
\begin{frame}[fragile]{Példa séma}
\begin{block}{schema.prisma}
\begin{block}{schema.prisma - Product}
\begin{lstlisting}[language=JavaScript]
model Product {
id Int @id @default(autoincrement())
@@ -30,6 +30,13 @@ model Product {
references: [id])
createdAt DateTime @default(now())
}
\end{lstlisting}
\end{block}
\end{frame}
\begin{frame}[fragile]{Példa séma (folyt.)}
\begin{block}{schema.prisma - Category}
\begin{lstlisting}[language=JavaScript]
model Category {
id Int @id @default(autoincrement())
name String
@@ -47,7 +54,13 @@ async function countProducts() {
const count = await prisma.product.count();
console.log(`Összes termék: ${count}`);
}
// Szűrt számlálás
\end{lstlisting}
\end{block}
\end{frame}
\begin{frame}[fragile]{Count - Számlálás (szűréssel)}
\begin{block}{Szűrt számlálás}
\begin{lstlisting}[language=JavaScript]
async function countExpensiveProducts() {
const count = await prisma.product.count({
where: {
@@ -69,14 +82,17 @@ async function getProductStats() {
const stats = await prisma.product.aggregate({
_count: true,
_avg: {price: true},
_sum: {stock: true},
_min: {price: true},
_max: {price: true}
});
\end{lstlisting}
\end{block}
\end{frame}
\begin{frame}[fragile]{Aggregate - Alapok (folyt.)}
\begin{block}{Egyszerű aggregáció}
\begin{lstlisting}[language=JavaScript]
console.log(stats);
}
\end{lstlisting}
Binary file not shown.
+3 -2
View File
@@ -4,8 +4,8 @@
% Automatikus frame törés engedélyezése túl hosszú tartalomnál
\setbeamertemplate{frametitle continuation}[from second][\insertcontinuationcountroman]
\title[Webtechnológia és webalkalmazás-fejlesztés - Rest API]{Webtechnológia és webalkalmazás-fejlesztés - Rest API}
\subtitle{Rest API}
\title[Webtechnológia és webalkalmazás-fejlesztés - Databse]{Webtechnológia és webalkalmazás-fejlesztés - Adatbázis}
\subtitle{Adatbázis}
\begin{document}
@@ -18,6 +18,7 @@
\input{orm.tex}
\input{prisma.tex}
\input{database_connection.tex}
\input{replication.tex}
\input{aggregates.tex}
\input{repository.tex}
\input{dto.tex}
+55 -41
View File
@@ -11,56 +11,70 @@
\end{frame}
\begin{frame}{Kapcsolati paraméterek}
\begin{itemize}
\item Host, port, adatbázis név
\item Felhasználónév, jelszó
\item SSL/TLS beállítások
\item Timeout és pool méretek
\end{itemize}
\begin{block}{Alapvető beállítások}
\begin{itemize}
\item \textbf{Host:} Szerver címe (localhost, IP, domain)
\item \textbf{Port:} Adatbázis portja\\
(PostgreSQL: 5432, MySQL: 3306, MongoDB: 27017)
\item \textbf{Database:} Adatbázis neve
\item \textbf{User/Password:} Hitelesítési adatok
\item \textbf{SSL/TLS:} Titkosított kapcsolat (production kötelező!)
\end{itemize}
\end{block}
\end{frame}
\begin{exampleblock}{Connection string példa}
\texttt{postgres://user:pass@localhost:5432/appdb}
\begin{frame}[fragile]{Connection String formátumok}
\begin{exampleblock}{PostgreSQL}
\begin{lstlisting}[language=bash]
postgresql://user:password@localhost:5432/mydb?schema=public
\end{lstlisting}
\end{exampleblock}
\begin{exampleblock}{MySQL}
\begin{lstlisting}[language=bash]
mysql://user:password@localhost:3306/mydb
\end{lstlisting}
\end{exampleblock}
\begin{exampleblock}{MongoDB}
\begin{lstlisting}[language=bash]
mongodb://user:password@localhost:27017/mydb
\end{lstlisting}
\end{exampleblock}
\end{frame}
\begin{frame}{Connection pooling}
\begin{block}{Miért kell?}
\begin{itemize}
\item A kapcsolat fel- és leépítése drága
\item Pool újrahasznosítja a kapcsolatokat
\item Stabilabb teljesítmény csúcsterhelésen
\end{itemize}
\end{block}
\begin{alertblock}{Beállítási irányelv}
Túl kicsi pool lassít, túl nagy pool túlterheli a szervert.
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{Konfiguráció alkalmazásban}
\begin{block}{Környezeti változók}
\begin{block}{Környezeti változók (.env fájl)}
\begin{lstlisting}[language=bash]
DATABASE_URL="postgres://user:pass@localhost:5432/appdb"
# Alapvető kapcsolat
DATABASE_URL="postgresql://user:pass@localhost:5432/appdb"
\end{lstlisting}
\end{block}
\begin{block}{Függőség kezelés}
A kapcsolat beállításait ne kódold a forráskódba, használj env fájlt.
\begin{alertblock}{SOHA ne commitold a .env fájlt!}
Használj .env.example sablont, a valódi értékeket .gitignore-ba!
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{Prisma specifikus konfiguráció}
\begin{block}{schema.prisma fájl}
\begin{lstlisting}[language=JavaScript]
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
\end{lstlisting}
\end{block}
\begin{block}{Connection URL extended példa}
\begin{lstlisting}[language=bash]
DATABASE_URL="postgresql://user:pass@localhost:5432/mydb?schema=public\
&connection_limit=10&pool_timeout=20"
\end{lstlisting}
\end{block}
\end{frame}
\begin{frame}{Prisma megjegyzés}
\begin{itemize}
\item Prisma-val a kapcsolat a \texttt{DATABASE\_URL}-on keresztül jön létre
\item A koncepció ugyanaz, csak a keretrendszer kezeli a részleteket
\item Ebben a részben nem feltétel a Prisma használata
\end{itemize}
\end{frame}
\begin{frame}{Hibakezelés és stabilitás}
\begin{itemize}
\item Használj újracsatlakozást ideiglenes hibákra
\item Naplózd a kapcsolat hibákat, de ne írj ki jelszavakat
\item Tranzakcióknál kezeld a visszagörgetést
\end{itemize}
\end{frame}
+54 -10
View File
@@ -4,14 +4,16 @@
\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 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.
Felhasználó entitás tartalmaz jelszó hash-t,\\
de a DTO nem adja ki.
\end{exampleblock}
\end{frame}
@@ -116,7 +118,7 @@ class UserResponseDTO {
\end{frame}
\begin{frame}[fragile]{DTO használata service-ben}
\begin{block}{UserService példa}
\begin{block}{UserService példa (1)}
\begin{lstlisting}[language=JavaScript]
const prisma = require('./prisma');
const bcrypt = require('bcrypt');
@@ -126,19 +128,26 @@ class UserService {
const hashedPassword = await bcrypt.hash(
createUserDTO.password, 10
); // Jelszó hash-elés
\end{lstlisting}
\end{block}
\end{frame}
\begin{frame}[fragile]{DTO használata service-ben (folyt.)}
\begin{block}{UserService példa (2)}
\begin{lstlisting}[language=JavaScript]
const user = await prisma.user.create({
data: {
email: createUserDTO.email,
password: hashedPassword,
name: createUserDTO.name
}// Mentés Prisma-val
} // 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{block}{UserService példa (3)}
\begin{lstlisting}[language=JavaScript]
// DTO-vá alakítás visszaadás előtt
return UserResponseDTO.fromPrismaUser(user);
@@ -147,6 +156,13 @@ class UserService {
const users = await prisma.user.findMany();
return UserResponseDTO.fromManyPrismaUsers(users);
}
\end{lstlisting}
\end{block}
\end{frame}
\begin{frame}[fragile]{DTO használata service-ben (folyt.)}
\begin{block}{UserService példa (4)}
\begin{lstlisting}[language=JavaScript]
async getUserById(id) {
const user = await prisma.user.findUnique({
where: { id }
@@ -160,7 +176,7 @@ class UserService {
\end{frame}
\begin{frame}[fragile]{Express integráció DTO-val}
\begin{block}{REST endpoint}
\begin{block}{REST endpoint (POST)}
\begin{lstlisting}[language=JavaScript]
const express = require('express');
const userService = new UserService();
@@ -174,6 +190,13 @@ app.post('/api/users', async (req, res) => {
res.status(400).json({ error: error.message });
}
});
\end{lstlisting}
\end{block}
\end{frame}
\begin{frame}[fragile]{Express integráció DTO-val (folyt.)}
\begin{block}{REST endpoint (GET)}
\begin{lstlisting}[language=JavaScript]
app.get('/api/users', async (req, res) => {
const usersDTO = await userService.getAllUsers();
res.json(usersDTO);
@@ -364,7 +387,7 @@ class UserFilterDTO {
\end{frame}
\begin{frame}[fragile]{Szűrési DTO használata}
\begin{block}{Express endpoint szűréssel}
\begin{block}{Express endpoint szűréssel (1)}
\begin{lstlisting}[language=JavaScript]
app.get('/api/users', async (req, res) => {
const filterDTO = new UserFilterDTO(req.query);
@@ -374,6 +397,13 @@ app.get('/api/users', async (req, res) => {
skip: (filterDTO.page - 1) * filterDTO.pageSize,
take: filterDTO.pageSize
});
\end{lstlisting}
\end{block}
\end{frame}
\begin{frame}[fragile]{Szűrési DTO használata (folyt.)}
\begin{block}{Express endpoint szűréssel (2)}
\begin{lstlisting}[language=JavaScript]
const total = await prisma.user.count({ where });
const result = new PaginatedResponseDTO(
UserResponseDTO.fromManyPrismaUsers(users),
@@ -386,7 +416,7 @@ app.get('/api/users', async (req, res) => {
\end{frame}
\begin{frame}[fragile]{Mapper függvények}
\begin{block}{Alternatív megközelítés - Pure functions // dto/user.mapper.js}
\begin{block}{Alternatív megközelítés (1)}
\begin{lstlisting}[language=JavaScript]
function toUserResponseDTO(user) {
return {
@@ -398,6 +428,13 @@ function toUserResponseDTO(user) {
function toUserResponseDTOs(users) {
return users.map(toUserResponseDTO);
}
\end{lstlisting}
\end{block}
\end{frame}
\begin{frame}[fragile]{Mapper függvények (folyt.)}
\begin{block}{Alternatív megközelítés (2)}
\begin{lstlisting}[language=JavaScript]
module.exports = {
toUserResponseDTO,
toUserResponseDTOs
@@ -407,15 +444,22 @@ module.exports = {
\end{frame}
\begin{frame}[fragile]{Mapper használata}
\begin{block}{Service-ben}
\begin{block}{Service-ben (1)}
\begin{lstlisting}[language=JavaScript]
const { toUserResponseDTO, toUserResponseDTOs }
const { toUserResponseDTO, toUserResponseDTOs }
= require('./dto/user.mapper');
class UserService {
async getAllUsers() {
const users = await prisma.user.findMany();
return toUserResponseDTOs(users);
}
\end{lstlisting}
\end{block}
\end{frame}
\begin{frame}[fragile]{Mapper használata (folyt.)}
\begin{block}{Service-ben (2)}
\begin{lstlisting}[language=JavaScript]
async getUserById(id) {
const user = await prisma.user.findUnique({
where: { id }
+136
View File
@@ -0,0 +1,136 @@
\section{Adatbázis replikáció}
\begin{frame}{Mi az adatbázis replikáció?}
\begin{block}{Definíció}
Az adatok \textbf{több adatbázis példány között} vannak másolva annak érdekében,
hogy növekedjen a \textbf{rendelkezésre állás}, \textbf{skálázhatóság} és
\textbf{hibatűrés}.
\end{block}
\begin{itemize}
\item PrimaryReplica (masterslave) felépítés a leggyakoribb
\item A replikáció lehet \textbf{szinkron} vagy \textbf{aszinkron}
\item Read-heavy alkalmazásoknál különösen hasznos
\end{itemize}
\end{frame}
\begin{frame}{Miért jó a replikáció?}
\begin{block}{Főbb előnyök}
\begin{itemize}
\item \textbf{Nagyobb rendelkezésre állás}: hiba esetén átváltás (failover)
\item \textbf{Skálázás olvasásra}: több replica olvasási terhelést visz el
\item \textbf{Biztonsági másolat}: adatvesztés kockázatának csökkentése
\item \textbf{Földrajzi közelség}: alacsonyabb késleltetés regionális replikákkal
\end{itemize}
\end{block}
\end{frame}
\begin{frame}{Replikációs topológiák}
\begin{itemize}
\item \textbf{PrimaryReplica}: egy írható csomópont, több olvasó
\item \textbf{Multi-primary}: több írható csomópont (konfliktuskezelés szükséges)
\item \textbf{Chain/Cascade}: láncolt replikák nagy földrajzi területre
\end{itemize}
\begin{alertblock}{Gyakorlatban}
A legtöbb backend szolgáltatásnál a PrimaryReplica modell az alapértelmezett.
\end{alertblock}
\end{frame}
\begin{frame}{Szinkron replikáció}
\begin{block}{Hogyan működik?}
\begin{itemize}
\item Az írás \textbf{csak akkor sikeres}, ha a replika is visszaigazolta
\item Erős \textbf{konzisztencia} (nincs adatvesztés írás után)
\item A primary \textbf{vár} a replikákra → nagyobb latency
\end{itemize}
\end{block}
\begin{exampleblock}{Mikor jó?}
Pénzügyi, tranzakciós rendszerek, ahol az adatvesztés nem elfogadható.
\end{exampleblock}
\end{frame}
\begin{frame}{Szinkron replikáció előnyök / hátrányok}
\begin{columns}
\begin{column}{0.48\textwidth}
\begin{block}{Előnyök}
\begin{itemize}
\item Erős konzisztencia
\item Minimális adatvesztés
\item Egyszerűbb olvasási modell
\end{itemize}
\end{block}
\end{column}
\begin{column}{0.48\textwidth}
\begin{alertblock}{Hátrányok}
\begin{itemize}
\item Nagyobb válaszidő írásnál
\item Érzékenyebb hálózati késleltetésre
\item Replikák kiesése lassítja az írást
\end{itemize}
\end{alertblock}
\end{column}
\end{columns}
\end{frame}
\begin{frame}{Aszinkron replikáció}
\begin{block}{Hogyan működik?}
\begin{itemize}
\item Az írás \textbf{azonnal sikeres}, a replika később frissül
\item Jobb \textbf{teljesítmény} és alacsonyabb latency
\item \textbf{Időbeli eltérés} lehetséges (eventual consistency)
\end{itemize}
\end{block}
\begin{exampleblock}{Mikor jó?}
Nagy forgalmú rendszerek, ahol az olvasási skálázás fontosabb, mint a tökéletes
konzisztencia (pl. hírportál, analytics).
\end{exampleblock}
\end{frame}
\begin{frame}{Aszinkron replikáció előnyök / hátrányok}
\begin{columns}
\begin{column}{0.48\textwidth}
\begin{block}{Előnyök}
\begin{itemize}
\item Alacsonyabb írási késleltetés
\item Jobb skálázhatóság
\item Replikák nem lassítják az írást
\end{itemize}
\end{block}
\end{column}
\begin{column}{0.48\textwidth}
\begin{alertblock}{Hátrányok}
\begin{itemize}
\item Ideiglenes inkonzisztencia
\item Failover esetén adatvesztés kockázata
\item Komplexebb olvasási logika
\end{itemize}
\end{alertblock}
\end{column}
\end{columns}
\end{frame}
\begin{frame}{Szinkron vs. aszinkron döntési szempontok}
\begin{itemize}
\item \textbf{Konzisztencia igény}: kritikus adatok → szinkron
\item \textbf{Teljesítmény igény}: magas throughput → aszinkron
\item \textbf{Hálózat megbízhatósága}: nagy latency → aszinkron
\item \textbf{Üzleti kockázat}: adatvesztés megengedett? → aszinkron
\end{itemize}
\begin{alertblock}{Ökölszabály}
Ha az írások számítanak jobban, válaszd a szinkront. Ha az olvasási skálázás
és az alacsony latency fontos, válaszd az aszinkront.
\end{alertblock}
\end{frame}
\begin{frame}{Hogyan használjuk a gyakorlatban?}
\begin{block}{Tipikus architektúra}
\begin{itemize}
\item \textbf{Primary} kezeli az írásokat
\item \textbf{Replica} kiszolgálja az olvasásokat
\item \textbf{Load balancer} irányítja az olvasást a replikákhoz
\end{itemize}
\end{block}
\begin{exampleblock}{ORM/Prisma oldal}
A legtöbb ORM-ben (így Prismában is) külön connection stringet használsz az
író és az olvasó adatbázisokhoz, vagy read-replica routert konfigurálsz.
\end{exampleblock}
\end{frame}