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