% CQRS Pattern

\section{CQRS Pattern}

\begin{frame}{Mi a CQRS?}
  \begin{block}{Command Query Responsibility Segregation}
    \textbf{Magyarul:} Parancs-Lekérdezés Felelősség Szétválasztása
    
    \vspace{0.3cm}
    
    Az adatok \textbf{írásának} (módosítás) és \textbf{olvasásának} (lekérdezés) szétválasztása külön modellekre.
  \end{block}
  
  \begin{columns}
    \begin{column}{0.48\textwidth}
      \begin{exampleblock}{Command (Parancs)}
        \textbf{Írási műveletek:}
        \begin{itemize}
          \item POST, PUT, PATCH, DELETE
          \item Adatok módosítása
          \item Validáció, üzleti logika
          \item Pl: felhasználó létrehozása
        \end{itemize}
      \end{exampleblock}
    \end{column}
    
    \begin{column}{0.48\textwidth}
      \begin{exampleblock}{Query (Lekérdezés)}
        \textbf{Olvasási műveletek:}
        \begin{itemize}
          \item GET
          \item Adatok lekérdezése
          \item Optimalizált olvasás
          \item Pl: felhasználók listázása
        \end{itemize}
      \end{exampleblock}
    \end{column}
  \end{columns}
\end{frame}

\begin{frame}{Miért van szükség CQRS-re?}
  \begin{block}{A probléma}
    Hagyományos CRUD alkalmazásokban ugyanazt a modellt használjuk írásra és olvasásra. 
    Ez problémás lehet:
    \begin{itemize}
      \item Az írási és olvasási igények eltérőek
      \item Az olvasás gyakran bonyolultabb (join-ok, aggregációk)
      \item A teljesítmény-optimalizálás nehéz
    \end{itemize}
  \end{block}
  
  \begin{exampleblock}{A megoldás: CQRS}
    Külön modellek külön optimalizálással:
    \begin{itemize}
      \item \textbf{Write Model} (Író modell): validáció, üzleti szabályok
      \item \textbf{Read Model} (Olvasó modell): gyors lekérdezések, cache
    \end{itemize}
  \end{exampleblock}
\end{frame}

\begin{frame}{CQRS előnyei}
  \begin{block}{Előnyök}
    \begin{itemize}
      \item \textbf{Független optimalizálás} - Az írás és olvasás külön optimalizálható
      \begin{itemize}
        \item Íráshoz: tranzakciók, validáció, konzisztencia
        \item Olvasáshoz: cache, denormalizált adatok, gyors lekérdezések
      \end{itemize}
      
      \item \textbf{Skálázhatóság (Scalability)} - Read és Write modellek külön skálázhatók
      \begin{itemize}
        \item Több olvasó szerver, kevesebb író szerver
      \end{itemize}
      
      \item \textbf{Egyszerűség} - Komplexitás csökken
      \begin{itemize}
        \item Minden modell egy feladatra fókuszál
      \end{itemize}
      
      \item \textbf{Teljesítmény (Performance)} - Gyorsabb lekérdezések
    \end{itemize}
  \end{block}
\end{frame}

\begin{frame}{Mikor használjuk a CQRS-t?}
  \begin{exampleblock}{Jó választás, ha:}
    \begin{itemize}
      \item \textbf{Komplex domain logika} van az alkalmazásban
      \item \textbf{Nagy terhelés} várható (sok párhuzamos felhasználó)
      \item \textbf{Eltérő írás/olvasás igények}
      \begin{itemize}
        \item Pl: ritkán írunk, sokszor olvasunk
      \end{itemize}
      \item \textbf{Különböző adatformátumok} kellenek íráshoz és olvasáshoz
    \end{itemize}
  \end{exampleblock}
  
  \begin{alertblock}{NEM használjuk, ha:}
    \begin{itemize}
      \item Egyszerű CRUD alkalmazás
      \item Kis terhelés
      \item Az írás és olvasás hasonló
    \end{itemize}
  \end{alertblock}
\end{frame}

\begin{frame}[shrink=10]{CRUD vs CQRS - Architektúra összehasonlítás}
  \begin{center}
    \begin{tikzpicture}[node distance=1.5cm]
      % CRUD
      \node[draw, rectangle, fill=blue!20, minimum width=2.5cm, minimum height=0.9cm, align=center] (crud) {\small CRUD\\Modell};
      \node[above=0.3cm of crud, font=\bfseries] {Hagyományos};
      \node[draw, rectangle, fill=green!20, minimum width=2.5cm, minimum height=0.9cm, right=1.2cm of crud, align=center] (db1) {\small Adatbázis\\(Database)};
      \draw[<->, thick] (crud) -- (db1) node[midway, above, font=\tiny] {olvas/ír};
      
      % CQRS
      \node[draw, rectangle, fill=yellow!20, minimum width=2.5cm, minimum height=0.9cm, below=2cm of crud, align=center] (cmd) {\small Parancs\\(Command)};
      \node[draw, rectangle, fill=orange!20, minimum width=2.5cm, minimum height=0.9cm, below=0.6cm of cmd, align=center] (query) {\small Lekérdezés\\(Query)};
      \node[above=0.3cm of cmd, font=\bfseries] {CQRS};
      \node[draw, rectangle, fill=green!20, minimum width=2.5cm, minimum height=0.9cm, right=1.2cm of cmd, align=center] (db2) {\small Író DB\\(Write)};
      \node[draw, rectangle, fill=green!20, minimum width=2.5cm, minimum height=0.9cm, right=1.2cm of query, align=center] (db3) {\small Olvasó DB\\(Read)};
      
      \draw[->, thick] (cmd) -- (db2) node[midway, above, font=\tiny] {ír};
      \draw[<-, thick] (query) -- (db3) node[midway, above, font=\tiny] {olvas};
      \draw[->, dashed, thick] (db2) -- (db3) node[midway, right, font=\tiny] {szinkron};
    \end{tikzpicture}
  \end{center}
\end{frame}

\begin{frame}[fragile,shrink=15]{Command - Írás példa}
  \begin{exampleblock}{CreateUserCommand}
    \tiny
    \begin{verbatim}
// commands/create-user.command.js
class CreateUserCommand {
  constructor(name, email, password) {
    this.name = name;
    this.email = email;
    this.password = password;
  }

  validate() {
    if (!this.email || !this.name || !this.password) {
      throw new Error('All fields required');
    }
    if (this.password.length < 8) {
      throw new Error('Password too short');
    }
  }
}

module.exports = CreateUserCommand;
    \end{verbatim}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile,shrink=15]{Command Handler}
  \begin{exampleblock}{CreateUserCommandHandler}
    \tiny
    \begin{verbatim}
// handlers/create-user.handler.js
const userRepository = require('../repositories/user.repository');

class CreateUserCommandHandler {
  async handle(command) {
    command.validate();
    
    // Üzleti logika
    const existingUser = await userRepository.findByEmail(command.email);
    if (existingUser) {
      throw new Error('Email already exists');
    }
    
    // Password hash
    const hashedPassword = await bcrypt.hash(command.password, 10);
    
    // User létrehozás
    const user = await userRepository.create({
      name: command.name,
      email: command.email,
      password: hashedPassword
    });
    
    return user;
  }
}
    \end{verbatim}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile,shrink=15]{Query - Olvasás példa}
  \begin{exampleblock}{GetUsersQuery}
    \tiny
    \begin{verbatim}
// queries/get-users.query.js
class GetUsersQuery {
  constructor(filters = {}) {
    this.page = filters.page || 1;
    this.limit = filters.limit || 10;
    this.role = filters.role;
    this.active = filters.active;
  }
}

// handlers/get-users.handler.js
class GetUsersQueryHandler {
  async handle(query) {
    // Optimalizált lekérdezés
    const users = await userReadModel.find({
      role: query.role,
      active: query.active,
      skip: (query.page - 1) * query.limit,
      limit: query.limit
    });
    
    return users;
  }
}
    \end{verbatim}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile,shrink=15]{CQRS REST API-ban (1/2)}
  \begin{exampleblock}{Command végpontok}
    \tiny
    \begin{verbatim}
// routes/user.routes.js
const CreateUserCommand = require('../commands/create-user.command');
const createUserHandler = require('../handlers/create-user.handler');

router.post('/users', async (req, res) => {
  try {
    const command = new CreateUserCommand(
      req.body.name,
      req.body.email,
      req.body.password
    );
    const user = await createUserHandler.handle(command);
    res.status(201).json(user);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});
    \end{verbatim}
  \end{exampleblock}
\end{frame}

\begin{frame}[fragile,shrink=15]{CQRS REST API-ban (2/2)}
  \begin{exampleblock}{Query végpontok}
    \tiny
    \begin{verbatim}
const GetUsersQuery = require('../queries/get-users.query');
const getUsersHandler = require('../handlers/get-users.handler');

router.get('/users', async (req, res) => {
  try {
    const query = new GetUsersQuery({
      page: req.query.page,
      limit: req.query.limit,
      role: req.query.role,
      active: req.query.active
    });
    const users = await getUsersHandler.handle(query);
    res.json(users);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});
    \end{verbatim}
  \end{exampleblock}
\end{frame}

\begin{frame}{CQRS előnyei és hátrányai}
  \begin{columns}
    \begin{column}{0.48\textwidth}
      \begin{block}{Előnyök}
        \begin{itemize}
          \item Jobb teljesítmény
          \item Független skálázás
          \item Egyszerűbb modellek
          \item Optimalizált lekérdezések
        \end{itemize}
      \end{block}
    \end{column}
    
    \begin{column}{0.48\textwidth}
      \begin{alertblock}{Hátrányok}
        \begin{itemize}
          \item Komplexitás növekedés
          \item Több kód
          \item Szinkronizáció problémák
          \item Eventual consistency
        \end{itemize}
      \end{alertblock}
    \end{column}
  \end{columns}
\end{frame}

\begin{frame}{Event Sourcing + CQRS}
  \begin{block}{Event Sourcing}
    Az állapotváltozások eseményekként tárolása, nem a végállapot mentése.
  \end{block}
  
  \begin{exampleblock}{Események}
    \begin{itemize}
      \item UserCreatedEvent
      \item UserEmailChangedEvent
      \item UserDeactivatedEvent
    \end{itemize}
  \end{exampleblock}
  
  \begin{alertblock}{CQRS + ES kombinációja}
    Command-ok eseményeket generálnak, Query modellek eseményekből épülnek fel.
  \end{alertblock}
\end{frame}

\begin{frame}{Összefoglalás - CQRS}
  \begin{itemize}
    \item CQRS = Command és Query szétválasztása
    \item Command: adatok módosítása (POST, PUT, DELETE)
    \item Query: adatok lekérdezése (GET)
    \item Előnyök: teljesítmény, skálázhatóság
    \item Hátrányok: komplexitás
    \item Event Sourcing jól kombinálható CQRS-sel
  \end{itemize}
  
  \vspace{0.5cm}
  
\end{frame}
