authn_z
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
\section{Service Layer}
|
||||
|
||||
\begin{frame}[shrink=15]{Mi az a Service Layer?}
|
||||
\begin{block}{Definíció}
|
||||
Tervezési minta - üzleti logika elválasztása controller-ektől és data layer-től.
|
||||
\end{block}
|
||||
\begin{itemize}
|
||||
\item Separation of Concerns
|
||||
\item Reusability (újrafelhasználható logika)
|
||||
\item Testability (könnyebb tesztelés)
|
||||
\item Maintainability
|
||||
\end{itemize}
|
||||
\begin{exampleblock}{Architektúra}
|
||||
Controller → Service → Repository → Database
|
||||
\end{exampleblock}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[shrink=15]{Háromrétegű architektúra}
|
||||
\begin{columns}
|
||||
\begin{column}{0.32\textwidth}
|
||||
\begin{block}{Presentation}
|
||||
\begin{small}
|
||||
Controllers, Routes\\
|
||||
HTTP req/res\\
|
||||
Validáció
|
||||
\end{small}
|
||||
\end{block}
|
||||
\end{column}
|
||||
\begin{column}{0.32\textwidth}
|
||||
\begin{block}{Business Logic}
|
||||
\begin{small}
|
||||
\textbf{Services}\\
|
||||
Üzleti szabályok\\
|
||||
Orchestration
|
||||
\end{small}
|
||||
\end{block}
|
||||
\end{column}
|
||||
\begin{column}{0.32\textwidth}
|
||||
\begin{block}{Data Access}
|
||||
\begin{small}
|
||||
Repository\\
|
||||
ORM\\
|
||||
Database
|
||||
\end{small}
|
||||
\end{block}
|
||||
\end{column}
|
||||
\end{columns}
|
||||
\begin{alertblock}{Fontos}
|
||||
Controller \textbf{NEM} tartalmaz üzleti logikát, csak delegál!
|
||||
\end{alertblock}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[shrink=15]{Service Layer előnyei}
|
||||
\begin{enumerate}
|
||||
\item \textbf{Separation}: Tiszta felelősségi körök
|
||||
\item \textbf{Reusability}: Több controller használhatja
|
||||
\item \textbf{Testability}: Könnyen unit tesztelhető
|
||||
\item \textbf{Maintainability}: Változások izoláltak
|
||||
\end{enumerate}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile,shrink=20]{AuthService - Interface}
|
||||
\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{frame}
|
||||
|
||||
\begin{frame}[fragile,shrink=20]{AuthService - Register}
|
||||
\begin{verbatim}
|
||||
class AuthService {
|
||||
async register(userData) {
|
||||
const exists = await this.userRepo.findByEmail(userData.email);
|
||||
if (exists) throw new Error('User exists');
|
||||
|
||||
const hashed = await bcrypt.hash(userData.password, 10);
|
||||
const user = await this.userRepo.create({
|
||||
...userData,
|
||||
password: hashed
|
||||
});
|
||||
|
||||
return { id: user.id, email: user.email };
|
||||
}
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile,shrink=20]{AuthService - Login}
|
||||
\begin{verbatim}
|
||||
async login(email, password) {
|
||||
const user = await this.userRepo.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 token = jwt.sign(
|
||||
{ userId: user.id, role: user.role },
|
||||
process.env.JWT_SECRET,
|
||||
{ expiresIn: '1h' }
|
||||
);
|
||||
|
||||
return { token, user: { id: user.id, email: user.email } };
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile,shrink=20]{Controller használja Service-t}
|
||||
\begin{verbatim}
|
||||
class AuthController {
|
||||
constructor(authService) {
|
||||
this.authService = authService;
|
||||
}
|
||||
|
||||
async register(req, res) {
|
||||
try {
|
||||
const user = await this.authService.register(req.body);
|
||||
res.status(201).json(user);
|
||||
} catch (err) {
|
||||
res.status(400).json({ error: err.message });
|
||||
}
|
||||
}
|
||||
|
||||
async login(req, res) {
|
||||
try {
|
||||
const result = await this.authService.login(req.body.email, req.body.password);
|
||||
res.json(result);
|
||||
} catch (err) {
|
||||
res.status(401).json({ error: err.message });
|
||||
}
|
||||
}
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile,shrink=20]{UserService példa}
|
||||
\begin{verbatim}
|
||||
class UserService {
|
||||
constructor(userRepo) {
|
||||
this.userRepo = userRepo;
|
||||
}
|
||||
|
||||
async getById(id) {
|
||||
const user = await this.userRepo.findById(id);
|
||||
if (!user) throw new Error('User not found');
|
||||
return user;
|
||||
}
|
||||
|
||||
async update(id, data) {
|
||||
const user = await this.getById(id);
|
||||
return await this.userRepo.update(id, data);
|
||||
}
|
||||
|
||||
async delete(id) {
|
||||
await this.getById(id);
|
||||
return await this.userRepo.delete(id);
|
||||
}
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile,shrink=20]{Dependency Injection}
|
||||
\begin{verbatim}
|
||||
// Repository
|
||||
class UserRepository {
|
||||
async findById(id) { /* DB query */ }
|
||||
async create(data) { /* DB insert */ }
|
||||
}
|
||||
|
||||
// Service
|
||||
class UserService {
|
||||
constructor(userRepository) {
|
||||
this.userRepo = userRepository;
|
||||
}
|
||||
// ...
|
||||
}
|
||||
|
||||
// DI container
|
||||
const userRepo = new UserRepository();
|
||||
const userService = new UserService(userRepo);
|
||||
const userController = new UserController(userService);
|
||||
\end{verbatim}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[shrink=15]{Service Layer Best Practices}
|
||||
\begin{enumerate}
|
||||
\item Service-ek ne dependáljanak controller-ektől
|
||||
\item Egy service = egy domain (User, Auth, Order)
|
||||
\item Dependency Injection használata
|
||||
\item Service-ek ne ismerjék HTTP-t (req, res)
|
||||
\item Hibakezelés service-ben (throw Error)
|
||||
\item Transaction logika service-ben
|
||||
\item Async/await következetes használata
|
||||
\end{enumerate}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile,shrink=20]{Transaction példa Service-ben}
|
||||
\begin{verbatim}
|
||||
class OrderService {
|
||||
async createOrder(userId, items) {
|
||||
const transaction = await db.transaction();
|
||||
|
||||
try {
|
||||
const order = await this.orderRepo.create({ userId }, transaction);
|
||||
|
||||
for (const item of items) {
|
||||
await this.orderItemRepo.create({ orderId: order.id, ...item }, transaction);
|
||||
}
|
||||
|
||||
await transaction.commit();
|
||||
return order;
|
||||
} catch (err) {
|
||||
await transaction.rollback();
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[shrink=15]{Testing Service Layer}
|
||||
\begin{itemize}
|
||||
\item Service-ek unit tesztelése mock repository-val
|
||||
\item Ne kelljen DB a unit teszthez
|
||||
\item Integration teszt valódi DB-vel
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
Reference in New Issue
Block a user