\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}
