docker
This commit is contained in:
@@ -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.
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 Primary–Replica (master–slave) 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{Primary–Replica}: 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 Primary–Replica 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}
|
||||
Reference in New Issue
Block a user