authn_z
This commit is contained in:
@@ -0,0 +1,671 @@
|
||||
\section{Service Layer}
|
||||
|
||||
\begin{frame}[shrink=5]{Mi az a Service Layer?}
|
||||
\begin{block}{Definíció}
|
||||
A \textbf{Service Layer} (szolgáltatási réteg) egy tervezési minta, amely elválasztja az üzleti logikát a controller-ektől és a data access layer-től.
|
||||
\end{block}
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{Separation of Concerns:} Felelősségek szétválasztása
|
||||
\item \textbf{Reusability:} Újrafelhasználható üzleti logika
|
||||
\item \textbf{Testability:} Könnyebb tesztelhetőség
|
||||
\item \textbf{Maintainability:} Karbantarthatóság
|
||||
\end{itemize}
|
||||
|
||||
\begin{exampleblock}{MVC architektúrában}
|
||||
Controller → \textbf{Service Layer} → Repository/Model → Database
|
||||
\end{exampleblock}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[shrink=5]{Háromrétegű architektúra}
|
||||
\begin{columns}
|
||||
\begin{column}{0.3\textwidth}
|
||||
\begin{block}{Presentation Layer}
|
||||
\begin{itemize}
|
||||
\item Controllers
|
||||
\item Routes
|
||||
\item HTTP kérés/válasz
|
||||
\item Validáció
|
||||
\end{itemize}
|
||||
\end{block}
|
||||
\end{column}
|
||||
|
||||
\begin{column}{0.35\textwidth}
|
||||
\begin{block}{Business Logic Layer}
|
||||
\begin{itemize}
|
||||
\item \textbf{Services}
|
||||
\item Üzleti szabályok
|
||||
\item Adatmanipuláció
|
||||
\item Orchestration
|
||||
\end{itemize}
|
||||
\end{block}
|
||||
\end{column}
|
||||
|
||||
\begin{column}{0.3\textwidth}
|
||||
\begin{block}{Data Access Layer}
|
||||
\begin{itemize}
|
||||
\item Repository
|
||||
\item Models
|
||||
\item ORM
|
||||
\item Database
|
||||
\end{itemize}
|
||||
\end{block}
|
||||
\end{column}
|
||||
\end{columns}
|
||||
|
||||
\begin{alertblock}{Fontos!}
|
||||
A controller \textbf{NEM} tartalmaz üzleti logikát, csak delegál a service-eknek!
|
||||
\end{alertblock}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[shrink=10]{Service Layer előnyei}
|
||||
\begin{enumerate}
|
||||
\item \textbf{Separation of Concerns}
|
||||
\begin{itemize}
|
||||
\item Tiszta felelősségi körök
|
||||
\item Controller: HTTP kezelés
|
||||
\item Service: Üzleti logika
|
||||
\item Repository: Adatelérés
|
||||
\end{itemize}
|
||||
|
||||
\item \textbf{Reusability}
|
||||
\begin{itemize}
|
||||
\item Több controller használhatja ugyanazt a service-t
|
||||
\item Különböző kontextusokban (API, CLI, Background job)
|
||||
\end{itemize}
|
||||
|
||||
\item \textbf{Testability}
|
||||
\begin{itemize}
|
||||
\item Service-ek könnyebben unit tesztelhetők
|
||||
\item Mock-olható függőségek
|
||||
\end{itemize}
|
||||
|
||||
\item \textbf{Maintainability}
|
||||
\begin{itemize}
|
||||
\item Változások izoláltak
|
||||
\item Könnyebb hibakeresés
|
||||
\end{itemize}
|
||||
\end{enumerate}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile,shrink=5]{AuthService - Authentikációs szolgáltatás}
|
||||
\begin{block}{AuthService felelősségei}
|
||||
\begin{itemize}
|
||||
\item Felhasználó regisztráció
|
||||
\item Bejelentkezés (login)
|
||||
\item Token generálás és validálás
|
||||
\item Jelszó hash-elés
|
||||
\item Kijelentkezés (logout)
|
||||
\end{itemize}
|
||||
\end{block}
|
||||
|
||||
\begin{exampleblock}{AuthService példa}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
class AuthService {
|
||||
async register(userData) { ... }
|
||||
async login(email, password) { ... }
|
||||
async logout(userId) { ... }
|
||||
async refreshToken(refreshToken) { ... }
|
||||
async verifyToken(token) { ... }
|
||||
async resetPassword(email) { ... }
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{exampleblock}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile,shrink=10]{AuthService implementáció - Register}
|
||||
\begin{block}{Regisztráció üzleti logikája}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
class AuthService {
|
||||
async register(userData) {
|
||||
const existing = await this.userRepository
|
||||
.findByEmail(userData.email);
|
||||
if (existing) throw new Error('User exists');
|
||||
|
||||
const hashed = await bcrypt.hash(userData.password, 10);
|
||||
const user = await this.userRepository.create({
|
||||
...userData,
|
||||
password: hashed
|
||||
});
|
||||
|
||||
return { id: user.id, email: user.email };
|
||||
}
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{AuthService implementáció - Login}
|
||||
\begin{block}{Bejelentkezés üzleti logikája}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
async login(email, password) {
|
||||
const user = await this.userRepository.findByEmail(email);
|
||||
if (!user) throw new Error('Invalid credentials');
|
||||
|
||||
const valid = await bcrypt.compare(password, user.password);
|
||||
if (!valid) throw new Error('Invalid credentials');
|
||||
|
||||
const accessToken = jwt.sign(
|
||||
{ userId: user.id, email: user.email },
|
||||
process.env.JWT_SECRET, { expiresIn: '15m' }
|
||||
);
|
||||
|
||||
const refreshToken = jwt.sign(
|
||||
{ userId: user.id },
|
||||
process.env.REFRESH_SECRET, { expiresIn: '7d' }
|
||||
);
|
||||
|
||||
return { accessToken, refreshToken };
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Controller használja az AuthService-t}
|
||||
\begin{block}{Thin Controller - Fat Service}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
app.post('/api/auth/register', async (req, res) => {
|
||||
try {
|
||||
const user = await authService.register(req.body);
|
||||
res.status(201).json({ user });
|
||||
} catch (error) {
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/auth/login', async (req, res) => {
|
||||
try {
|
||||
const result = await authService.login(
|
||||
req.body.email, req.body.password
|
||||
);
|
||||
res.cookie('refreshToken', result.refreshToken,
|
||||
{ httpOnly: true, secure: true });
|
||||
res.json({ accessToken: result.accessToken });
|
||||
} catch (error) {
|
||||
res.status(401).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
\end{verbatim}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{UserService - Felhasználó kezelés}
|
||||
\begin{block}{UserService felelősségei}
|
||||
\begin{itemize}
|
||||
\item Felhasználói profil lekérdezés
|
||||
\item Profil módosítás
|
||||
\item Jelszó változtatás
|
||||
\item Felhasználó törlés
|
||||
\item Felhasználó lista (admin)
|
||||
\end{itemize}
|
||||
\end{block}
|
||||
|
||||
\vspace{0.3cm}
|
||||
|
||||
\begin{exampleblock}{UserService példa}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
class UserService {
|
||||
constructor(userRepository) {
|
||||
this.userRepository = userRepository;
|
||||
}
|
||||
|
||||
async getUserProfile(userId) { ... }
|
||||
async updateProfile(userId, data) { ... }
|
||||
async changePassword(userId, oldPassword, newPassword) { ... }
|
||||
async deleteUser(userId) { ... }
|
||||
async getAllUsers(filters) { ... }
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{exampleblock}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile,shrink=5]{UserService implementáció}
|
||||
\begin{block}{Jelszó változtatás üzleti logikával}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
class UserService {
|
||||
async changePassword(userId, oldPass, newPass) {
|
||||
const user = await this.userRepository.findById(userId);
|
||||
if (!user) throw new Error('User not found');
|
||||
|
||||
const valid = await bcrypt.compare(oldPass, user.password);
|
||||
if (!valid) throw new Error('Incorrect password');
|
||||
|
||||
const hashed = await bcrypt.hash(newPass, 10);
|
||||
await this.userRepository.update(userId,
|
||||
{ password: hashed });
|
||||
await this.tokenService.revokeAllTokens(userId);
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{TokenService - Token kezelés}
|
||||
\begin{block}{TokenService felelősségei}
|
||||
\begin{itemize}
|
||||
\item Access token generálás
|
||||
\item Refresh token generálás
|
||||
\item Token validálás
|
||||
\item Token megújítás (refresh)
|
||||
\item Token visszavonás (revoke)
|
||||
\end{itemize}
|
||||
\end{block}
|
||||
|
||||
\vspace{0.3cm}
|
||||
|
||||
\begin{exampleblock}{TokenService példa}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
class TokenService {
|
||||
constructor(tokenRepository) {
|
||||
this.tokenRepository = tokenRepository;
|
||||
}
|
||||
|
||||
generateAccessToken(payload) { ... }
|
||||
generateRefreshToken(userId) { ... }
|
||||
async verifyAccessToken(token) { ... }
|
||||
async refreshAccessToken(refreshToken) { ... }
|
||||
async revokeToken(token) { ... }
|
||||
async revokeAllTokens(userId) { ... }
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{exampleblock}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile,shrink=5]{TokenService implementáció}
|
||||
\begin{block}{Token refresh implementáció}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
class TokenService {
|
||||
async refreshAccessToken(refreshToken) {
|
||||
const decoded = jwt.verify(
|
||||
refreshToken, process.env.REFRESH_SECRET);
|
||||
|
||||
const stored = await this.tokenRepository
|
||||
.findByToken(refreshToken);
|
||||
if (!stored || stored.revoked)
|
||||
throw new Error('Token revoked');
|
||||
|
||||
const user = await this.userRepository
|
||||
.findById(decoded.userId);
|
||||
return {
|
||||
accessToken: this.generateAccessToken(user)
|
||||
};
|
||||
}
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{AuthorizationService - Autorizáció}
|
||||
\begin{block}{AuthorizationService felelősségei}
|
||||
\begin{itemize}
|
||||
\item Jogosultság ellenőrzés
|
||||
\item Role-based access control (RBAC)
|
||||
\item Permission-based access control (PBAC)
|
||||
\item Resource ownership ellenőrzés
|
||||
\end{itemize}
|
||||
\end{block}
|
||||
|
||||
\vspace{0.3cm}
|
||||
|
||||
\begin{exampleblock}{AuthorizationService példa}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
class AuthorizationService {
|
||||
async hasRole(userId, role) { ... }
|
||||
async hasPermission(userId, permission) { ... }
|
||||
async canAccessResource(userId, resourceId, action) { ... }
|
||||
async isOwner(userId, resourceId) { ... }
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{exampleblock}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{AuthorizationService implementáció}
|
||||
\begin{block}{Jogosultság ellenőrzés}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
class AuthorizationService {
|
||||
constructor(userRepository, permissionRepository) {
|
||||
this.userRepository = userRepository;
|
||||
this.permissionRepository = permissionRepository;
|
||||
}
|
||||
|
||||
async canAccessResource(userId, resourceId, action) {
|
||||
const user = await this.userRepository.findById(userId);
|
||||
|
||||
// 1. Admin mindent csinálhat
|
||||
if (user.role === 'admin') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. Ownership ellenőrzés
|
||||
const resource = await this.resourceRepository.findById(resourceId);
|
||||
if (resource.ownerId === userId && action === 'read') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. Permission alapú ellenőrzés
|
||||
const permissions = await this.permissionRepository.getByUserId(userId);
|
||||
return permissions.some(p => p.action === action && p.resource === resourceId);
|
||||
}
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Dependency Injection}
|
||||
\begin{block}{Service-ek kapcsolata}
|
||||
A service-ek más service-eket használnak. Dependency Injection segít a függőségek kezelésében.
|
||||
\end{block}
|
||||
|
||||
\vspace{0.3cm}
|
||||
|
||||
\begin{columns}
|
||||
\begin{column}{0.48\textwidth}
|
||||
\textbf{Rossz példa:}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
class AuthService {
|
||||
constructor() {
|
||||
this.userRepo =
|
||||
new UserRepository();
|
||||
}
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{column}
|
||||
|
||||
\begin{column}{0.48\textwidth}
|
||||
\textbf{Jó példa (DI):}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
class AuthService {
|
||||
constructor(userRepo) {
|
||||
this.userRepo = userRepo;
|
||||
}
|
||||
}
|
||||
|
||||
const authService =
|
||||
new AuthService(userRepo);
|
||||
\end{verbatim}
|
||||
\end{column}
|
||||
\end{columns}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Service Container / DI Container}
|
||||
\begin{block}{Automatikus Dependency Injection}
|
||||
DI container-ek automatikusan kezelik a szolgáltatások létrehozását.
|
||||
\end{block}
|
||||
|
||||
\vspace{0.3cm}
|
||||
|
||||
\begin{exampleblock}{Awilix library használata}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
const { createContainer, asClass } = require('awilix');
|
||||
|
||||
const container = createContainer();
|
||||
container.register({
|
||||
userRepository: asClass(UserRepository).singleton(),
|
||||
tokenRepository: asClass(TokenRepository).singleton(),
|
||||
authService: asClass(AuthService).singleton(),
|
||||
userService: asClass(UserService).singleton(),
|
||||
tokenService: asClass(TokenService).singleton()
|
||||
});
|
||||
|
||||
const authService = container.resolve('authService');
|
||||
\end{verbatim}
|
||||
\end{exampleblock}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Error Handling a Service Layer-ben}
|
||||
\begin{block}{Custom Error osztályok}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
class AuthenticationError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.statusCode = 401;
|
||||
}
|
||||
}
|
||||
|
||||
class AuthorizationError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.statusCode = 403;
|
||||
}
|
||||
}
|
||||
|
||||
class NotFoundError extends Error {
|
||||
constructor(resource) {
|
||||
super(`${resource} not found`);
|
||||
this.statusCode = 404;
|
||||
}
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Error Handling - Service használat}
|
||||
\begin{block}{Service dobja a custom error-t}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
class AuthService {
|
||||
async login(email, password) {
|
||||
const user = await this.userRepository
|
||||
.findByEmail(email);
|
||||
if (!user) {
|
||||
throw new AuthenticationError('Invalid credentials');
|
||||
}
|
||||
const valid = await bcrypt.compare(
|
||||
password, user.password
|
||||
);
|
||||
if (!valid) {
|
||||
throw new AuthenticationError('Invalid credentials');
|
||||
}
|
||||
// ...
|
||||
}
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{block}
|
||||
|
||||
\vspace{0.2cm}
|
||||
|
||||
\begin{block}{Controller kezeli}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
app.post('/api/auth/login', async (req, res, next) => {
|
||||
try {
|
||||
const result = await authService.login(
|
||||
req.body.email, req.body.password);
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
\end{verbatim}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]{Global Error Handler Middleware}
|
||||
\begin{block}{Központi hibaüzenet kezelés}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
app.use((error, req, res, next) => {
|
||||
console.error(error);
|
||||
|
||||
if (error.statusCode) {
|
||||
return res.status(error.statusCode)
|
||||
.json({ error: error.message });
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
error: 'Internal Server Error'
|
||||
});
|
||||
});
|
||||
\end{verbatim}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}{Service Layer Testing}
|
||||
\begin{block}{Unit Testing}
|
||||
A service-ek izoláltan tesztelhetők mock repository-kkal és service-ekkel.
|
||||
\end{block}
|
||||
|
||||
\vspace{0.3cm}
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{Előnyök:}
|
||||
\begin{itemize}
|
||||
\item Gyors tesztek (nincs adatbázis)
|
||||
\item Üzleti logika fókusz
|
||||
\item Mock-olható függőségek
|
||||
\end{itemize}
|
||||
|
||||
\vspace{0.3cm}
|
||||
|
||||
\item \textbf{Test framework-ök:}
|
||||
\begin{itemize}
|
||||
\item Jest
|
||||
\item Mocha + Chai
|
||||
\item Vitest
|
||||
\end{itemize}
|
||||
|
||||
\vspace{0.3cm}
|
||||
|
||||
\item \textbf{Mocking library-k:}
|
||||
\begin{itemize}
|
||||
\item Sinon.js
|
||||
\item Jest built-in mocks
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile,shrink=5]{Service Unit Test példa}
|
||||
\begin{block}{AuthService.login() teszt}
|
||||
\small
|
||||
\begin{verbatim}
|
||||
describe('AuthService', () => {
|
||||
let authService, mockUserRepo;
|
||||
|
||||
beforeEach(() => {
|
||||
mockUserRepo = { findByEmail: jest.fn() };
|
||||
authService = new AuthService(mockUserRepo);
|
||||
});
|
||||
|
||||
test('login fails for invalid user', async () => {
|
||||
mockUserRepo.findByEmail.mockResolvedValue(null);
|
||||
await expect(
|
||||
authService.login('test@test.com', 'pass')
|
||||
).rejects.toThrow('Invalid credentials');
|
||||
});
|
||||
});
|
||||
\end{verbatim}
|
||||
\end{block}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}{Service Layer Best Practices}
|
||||
\begin{enumerate}
|
||||
\item \textbf{Single Responsibility:} Egy service egy felelősségi kör
|
||||
\item \textbf{Dependency Injection:} Konstruktorban injektált függőségek
|
||||
\item \textbf{Thin Controllers:} Controller csak HTTP kezel
|
||||
\item \textbf{Error Handling:} Custom error osztályok használata
|
||||
\item \textbf{Async/Await:} Tiszta aszinkron kód
|
||||
\item \textbf{Validation:} Input validáció a service-ben is
|
||||
\item \textbf{Transaction Management:} Adatbázis tranzakciók
|
||||
\item \textbf{Logging:} Strukturált log-olás
|
||||
\end{enumerate}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}{Projekt struktúra Service Layer-rel}
|
||||
\begin{columns}
|
||||
\begin{column}{0.48\textwidth}
|
||||
\textbf{Fájl struktúra:}
|
||||
\small
|
||||
\begin{itemize}
|
||||
\item \texttt{src/}
|
||||
\begin{itemize}
|
||||
\item \texttt{controllers/}
|
||||
\begin{itemize}
|
||||
\item \texttt{auth.controller.js}
|
||||
\item \texttt{user.controller.js}
|
||||
\end{itemize}
|
||||
\item \texttt{services/}
|
||||
\begin{itemize}
|
||||
\item \texttt{auth.service.js}
|
||||
\item \texttt{user.service.js}
|
||||
\item \texttt{token.service.js}
|
||||
\end{itemize}
|
||||
\item \texttt{repositories/}
|
||||
\begin{itemize}
|
||||
\item \texttt{user.repository.js}
|
||||
\item \texttt{token.repository.js}
|
||||
\end{itemize}
|
||||
\item \texttt{models/}
|
||||
\item \texttt{middlewares/}
|
||||
\item \texttt{utils/}
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
\end{column}
|
||||
|
||||
\begin{column}{0.48\textwidth}
|
||||
\textbf{Rétegek felelősségei:}
|
||||
\begin{itemize}
|
||||
\item \textbf{Controller:}
|
||||
\begin{itemize}
|
||||
\item HTTP kérés/válasz
|
||||
\item Validáció (input)
|
||||
\item Service hívás
|
||||
\end{itemize}
|
||||
|
||||
\vspace{0.3cm}
|
||||
|
||||
\item \textbf{Service:}
|
||||
\begin{itemize}
|
||||
\item Üzleti logika
|
||||
\item Adatmanipuláció
|
||||
\item Orchestration
|
||||
\end{itemize}
|
||||
|
||||
\vspace{0.3cm}
|
||||
|
||||
\item \textbf{Repository:}
|
||||
\begin{itemize}
|
||||
\item Adatbázis műveletek
|
||||
\item Query-k
|
||||
\item ORM interaction
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
\end{column}
|
||||
\end{columns}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}{Összefoglalás}
|
||||
\begin{itemize}
|
||||
\item \textbf{Service Layer} = Üzleti logika réteg
|
||||
\item \textbf{Separation of Concerns:} Controller, Service, Repository
|
||||
\item \textbf{AuthService:} Regisztráció, login, token kezelés
|
||||
\item \textbf{UserService:} Felhasználó kezelés, profil, jelszó
|
||||
\item \textbf{TokenService:} Token generálás, validálás, refresh
|
||||
\item \textbf{AuthorizationService:} Jogosultság ellenőrzés
|
||||
\item \textbf{Dependency Injection:} Konstruktor alapú DI
|
||||
\item \textbf{Error Handling:} Custom error osztályok
|
||||
\item \textbf{Testing:} Unit teszt mock-okkal
|
||||
\item \textbf{Best Practices:} Thin controller, fat service
|
||||
\end{itemize}
|
||||
|
||||
\vspace{0.3cm}
|
||||
|
||||
\begin{exampleblock}{Miért használjuk?}
|
||||
Tiszta kód, újrafelhasználhatóság, tesztelhetőség, karbantarthatóság
|
||||
\end{exampleblock}
|
||||
\end{frame}
|
||||
Reference in New Issue
Block a user