# User Management Rendszer - Feladat Leírás

## Projekt Áttekintés

Egy **User Management (felhasználó kezelési)** rendszer elkészítése Node.js és Express.js használatával, **Clean Architecture** elveket követve, **CQRS (Command Query Responsibility Segregation)** pattern alkalmazásával.

## Célkitűzés

Egy RESTful API létrehozása felhasználók kezeléséhez (CRUD műveletek), amely követi a modern szoftver architektúra elveket és jól strukturált, karbantartható kódot eredményez.

---

## Technológiai Stack

### Kötelező Technológiák
- **Node.js** (v18 vagy újabb)
- **Express.js** (v5.2.1 vagy újabb) - Web framework
- **ES6 Modules** - `import/export` szintaxis használata
- **JSON File Storage** - Adattárolás fájl alapú megoldással (későbbi adatbázis integrációhoz előkészítve)

### Fejlesztői Eszközök
- **nodemon** - Automatikus újraindítás fejlesztés közben

---

## Architektúra Áttekintés

A projekt **4 fő réteget** tartalmaz:

```
src/
├── API/                    # Prezentációs réteg (Web API)
├── Application/            # Üzleti logika réteg (CQRS)
├── Domain/                 # Domain modell és interfészek
└── Infrastructure/         # Adatelérési réteg és külső szolgáltatások
```

### Rétegek Részletes Leírása

#### 1. **Domain Layer** (Domain réteg)
- **Felelősség**: Az alkalmazás magját képező interfészek és domain modellek
- **Tartalma**: Repository interfészek (IUserRepository)
- **Függőség**: Nincs más rétegre való függősége

#### 2. **Infrastructure Layer** (Infrastruktúra réteg)
- **Felelősség**: Technikai implementációk (adatbázis, fájlkezelés, külső API-k)
- **Tartalma**: Repository implementációk, adattárolás
- **Függőség**: Domain layer-től függ (implementálja az interfészeket)

#### 3. **Application Layer** (Alkalmazás réteg)
- **Felelősség**: Üzleti logika, use case-ek implementálása
- **Tartalma**: Commands, CommandHandlers, Queries, QueryHandlers
- **Függőség**: Domain layer-től függ
- **CQRS Pattern**: Írás (Command) és olvasás (Query) műveletek szétválasztása

#### 4. **API Layer** (API réteg)
- **Felelősség**: HTTP kérések kezelése, routing, válaszok küldése
- **Tartalma**: Controllers, Routers, Server konfiguráció
- **Függőség**: Application és Infrastructure layer-től függ

---

## CQRS Pattern Magyarázat

### Mi az a CQRS?

**Command Query Responsibility Segregation** - Az írási és olvasási műveletek szétválasztása.

### Command (Parancs) - Írási műveletek
- Megváltoztatja a rendszer állapotát
- Nem ad vissza értéket (vagy csak a létrehozott entitást)
- Példák: CreateUser, UpdateUser, DeleteUser

**Struktúra:**
1. **Command osztály** - Tartalmazza a parancs adatait
2. **CommandHandler osztály** - Végrehajtja a parancsot, validálja az adatokat

### Query (Lekérdezés) - Olvasási műveletek
- Nem változtatja meg a rendszer állapotát
- Adatokat ad vissza
- Példák: GetAllUsers, GetUserById

**Struktúra:**
1. **Query osztály** - Tartalmazza a lekérdezés paramétereit
2. **QueryHandler osztály** - Végrehajtja a lekérdezést

---

## Részletes Implementációs Útmutató

### 1. Projekt Inicializálás

#### 1.1 Projekt struktúra létrehozása

```
Backend/
└── elso gyakorlat/
    ├── package.json
    └── src/
        ├── API/
        │   ├── server.js
        │   ├── controllers/
        │   │   └── userController.js
        │   └── routers/
        │       └── userRouter.js
        ├── Application/
        │   └── users/
        │       ├── command/
        │       │   ├── createUserCommand.js
        │       │   ├── createUserCommandHandler.js
        │       │   ├── updateUserCommand.js
        │       │   ├── updateUserCommandHandler.js
        │       │   ├── deleteUserCommand.js
        │       │   └── deleteUserCommandHandler.js
        │       └── query/
        │           ├── getAllUsersQuery.js
        │           ├── getAllUsersQueryHandler.js
        │           ├── getUserByIdQuery.js
        │           └── getUserByIdQueryHandler.js
        ├── Domain/
        │   └── IUserRepository.js
        └── Infrastructure/
            ├── user.json
            └── userRepository.js
```

#### 1.2 package.json létrehozása

**Cél**: Node.js projekt konfigurációs fájl létrehozása ES6 modul támogatással.

**Részletes leírás:**

A `package.json` az **npm projekt szíve** - tartalmazza a metaadatokat, függőségeket és script-eket.

**Kulcsfontosságú mezők:**

**1. Alapinformációk:**
- `name`: Projekt neve (lowercase, kötőjellel)
- `version`: Verzió szám (semantic versioning: MAJOR.MINOR.PATCH)
- `description`: Rövid leírás
- `main`: Belépési pont (itt nem használt)

**2. `"type": "module"` ⚠️ KRITIKUS!**
- Engedélyezi az ES6 modulok használatát (`import/export`)
- Nélküle csak `require()`/`module.exports` működne (CommonJS)
- Ez kötelező, hogy `import` szintaxist használhassunk!

**3. Scripts:**
- `start`: A szerver indítása nodemon-nal
  - `nodemon src/API/server.js` - automatikus újraindítás fájl módosításkor
  - Így nem kell minden változtatás után manuálisan újraindítani
- `test`: Placeholder teszt script (később Jest/Mocha tesztek jöhetnek ide)

**4. Dependencies (Futásidejű függőségek):**
- `express`: ^5.2.1 - Web framework
  - `^` (caret): Automatikus minor/patch update-ek engedélyezése
  - 5.x.x verzió tartományon belül
- `nodemon`: ^3.1.11 - Fejlesztői szerverAutomatikus újraindítás
  - Érdemes `devDependencies`-be tenni (csak fejlesztéshez kell)

**Létrehozási módok:**

**Manuális:**
Hozd létre a fájlt és másold be a tartalmat.

**npm init:**
```bash
npm init -y  # Gyors, alapértelmezett értékekkel
```
Utána add hozzá: `"type": "module"`

**Későbbi bővítések:**
```json
"devDependencies": {
  "nodemon": "^3.1.11",  // Ide kellene
  "jest": "^29.0.0"      // Teszteléshez
},
"engines": {
  "node": ">=18.0.0"     // Node verzió megkötés
}
```

<details>
<summary>📄 <strong>Teljes package.json tartalom</strong></summary>

```json
{
  "name": "elso-gyakorlat",
  "version": "1.0.0",
  "description": "User Management System",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "start": "nodemon src/API/server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^5.2.1",
    "nodemon": "^3.1.11"
  }
}
```

</details>

**FONTOS**: Ne felejtsed el hozzáadni a `"type": "module"` sort!

#### 1.3 Függőségek telepítése

```bash
npm install
```

---

### 2. Domain Layer Implementálás

#### 2.1 IUserRepository.js - Repository Interface

**Fájl**: `src/Domain/IUserRepository.js`

**Cél**: Egy interfész-szerű osztály létrehozása, amely definiálja a user repository alapvető műveleteit.

**Részletes leírás:**

A Domain Layer középpontjában az interfészek állnak. JavaScript-ben nincs natív interfész támogatás (mint pl. TypeScript-ben vagy Java-ban), de osztályokkal szimulálhatjuk ezt a viselkedést.

**Mit kell létrehozni:**

1. **IUserRepository osztály** - Ez lesz a "szerződés", amit minden repository implementációnak követnie kell

2. **5 metódus**, mind aszinkron (`async`):
   - `getAll()` - Összes felhasználó lekérése (paraméter nélkül)
   - `getById(id)` - Egy felhasználó lekérése ID alapján
   - `create(user)` - Új felhasználó létrehozása (user objektumot vár paraméterként)
   - `update(id, userData)` - Felhasználó módosítása (ID és új adatok)
   - `delete(id)` - Felhasználó törlése (ID alapján)

3. **Error dobás** - Minden metódus dobjon hibát alapértelmezetten (`throw new Error('Method not implemented')`)
   - Miért? Mert ez biztosítja, hogy ha valaki implementálja ezt az osztályt, de elfelejt egy metódust felülírni, egyből hibát kapjon

**Fontos elvek:**
- Ez az osztály NEM tartalmaz implementációt, csak az "interfészt" definiálja
- Minden metódus async, mert később fájlkezelés/adatbázis műveleteket fogunk végezni
- Ez a Dependency Inversion Principle része - a magasabb szintű kód ettől az interface-től függ, nem a konkrét implementációtól

<details>
<summary>📄 <strong>Teljes kód megtekintése</strong></summary>

```javascript
export class IUserRepository {
  async getAll() {
    throw new Error('Method not implemented');
  }

  async getById(id) {
    throw new Error('Method not implemented');
  }

  async create(user) {
    throw new Error('Method not implemented');
  }

  async update(id, userData) {
    throw new Error('Method not implemented');
  }

  async delete(id) {
    throw new Error('Method not implemented');
  }
}
```

</details>

---

### 3. Infrastructure Layer Implementálás

#### 3.1 user.json - Adat fájl

**Fájl**: `src/Infrastructure/user.json`

**Cél**: Egyszerű JSON fájl alapú adattárolás létrehozása.

**Részletes leírás:**

Ez lesz az "adatbázisunk" - egy egyszerű JSON fájl, amely a felhasználókat tárolja. Később ezt könnyedén lecserélhetjük valódi adatbázisra (MongoDB, PostgreSQL, stb.) anélkül, hogy az alkalmazás többi részét módosítanánk.

**Mit tartalmaz:**
- Kezdetben egy üres tömb (`[]`)
- A repository fog írni/olvasni ebből a fájlból
- Minden user objektumként lesz tárolva a tömbben

**Miért JSON fájl?**
- Egyszerű és gyors kezdéshez
- Nem kell adatbázist telepíteni
- Könnyen olvasható és debuggolható
- Demonstrálja a repository pattern használatát

<details>
<summary>📄 <strong>Fájl tartalma megtekintése</strong></summary>

```json
[]
```

</details>

#### 3.2 userRepository.js - Repository Implementáció

**Fájl**: `src/Infrastructure/userRepository.js`

**Cél**: Az IUserRepository interfész konkrét implementációja JSON fájl alapú tárolással.

**Részletes leírás:**

Ez az osztály valósítja meg a tényleges adatelérési logikát. Kiterjeszti (`extends`) az `IUserRepository` interfészt, tehát felül kell írnia minden metódust.

**Importok és inicializálás:**

1. **fs/promises** - Node.js fájlrendszer modul aszinkron verzió (promise-based)
2. **path** - Fájl útvonalak kezeléséhez
3. **fileURLToPath** - ES6 modulokban szükséges a `__dirname` eléréséhez
4. **IUserRepository** - A szülő interfész

**Fontos ES6 Modules trükk:**
```javascript
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
```
ES6 modulokban nem elérhető a `__dirname`, így ezt a workaround-ot kell használni.

**Metódusok részletes működése:**

**1. getAll() - Összes user lekérése**
- `fs.readFile()` - Beolvassa a JSON fájlt (async)
- `JSON.parse()` - String-ből objektummá alakítja
- Try-catch: ha a fájl nem létezik/üres, üres tömböt ad vissza
- **Fontos**: Ez a metódus lesz a többi metódus alapja is!

**2. getById(id) - Egy user keresése**
- Meghívja a `getAll()`-t
- `find()` - Tömb metódus, megkeresi az első egyező elemet
- Ha nincs találat, `undefined`-et ad vissza

**3. create(user) - Új user létrehozása**
- Lekéri az összes user-t
- **Új objektum létrehozása:**
  - `id: Date.now().toString()` - Timestamp alapú egyedi ID
  - `...user` - Spread operator, bemásol minden property-t
  - `createdAt` - ISO formátumú timestamp
- `users.push()` - Hozzáadja a tömb végéhez
- `fs.writeFile()` - Visszamenti a fájlba
  - `JSON.stringify(users, null, 2)` - Pretty print, 2 szóköz indentálás
- Visszaadja az új user objektumot

**4. update(id, userData) - User módosítása**
- `findIndex()` - Megkeresi az user indexét a tömbben
- Ha `index === -1`, nem létezik → `null` visszatérés
- **Merge művelet spread operator-ral:**
  - `...users[index]` - Régi adatok
  - `...userData` - Új adatok (felülírják a régieket)
  - `id: users[index].id` - ID megtartása (biztonsági okokból)
  - `updatedAt` - Új timestamp
- Fájl frissítése és módosított user visszaadása

**5. delete(id) - User törlése**
- `filter()` - Új tömb létrehozása az adott user nélkül
- Hossz összehasonlítás: ha egyenlő, nem volt mit törölni → `false`
- Fájl frissítése és `true` visszaadása

**Best practices:**
- Minden metódus `async` - konzisztens API
- Try-catch csak ahol szükséges (getAll)
- Spread operator használata immutábilis műveleteknél
- Pretty print a JSON fájlban - könnyebb debuggolni

<details>
<summary>📄 <strong>Teljes kód megtekintése</strong></summary>

```javascript
import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
import { IUserRepository } from '../Domain/IUserRepository.js';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const USER_FILE = path.join(__dirname, 'user.json');

export class UserRepository extends IUserRepository {
  async getAll() {
    try {
      const data = await fs.readFile(USER_FILE, 'utf-8');
      return JSON.parse(data);
    } catch (error) {
      return [];
    }
  }

  async getById(id) {
    const users = await this.getAll();
    return users.find(user => user.id === id);
  }

  async create(user) {
    const users = await this.getAll();
    const newUser = {
      id: Date.now().toString(),
      ...user,
      createdAt: new Date().toISOString()
    };
    users.push(newUser);
    await fs.writeFile(USER_FILE, JSON.stringify(users, null, 2));
    return newUser;
  }

  async update(id, userData) {
    const users = await this.getAll();
    const index = users.findIndex(user => user.id === id);
    if (index === -1) return null;
    
    users[index] = {
      ...users[index],
      ...userData,
      id: users[index].id,
      updatedAt: new Date().toISOString()
    };
    await fs.writeFile(USER_FILE, JSON.stringify(users, null, 2));
    return users[index];
  }

  async delete(id) {
    const users = await this.getAll();
    const filteredUsers = users.filter(user => user.id !== id);
    if (users.length === filteredUsers.length) return false;
    
    await fs.writeFile(USER_FILE, JSON.stringify(filteredUsers, null, 2));
    return true;
  }
}
```

</details>

---

### 4. Application Layer - Query Implementálás

#### 4.1 GetAllUsersQuery

**Fájl**: `src/Application/users/query/getAllUsersQuery.js`

**Cél**: Query objektum létrehozása az összes felhasználó lekérdezéséhez.

**Részletes leírás:**

A CQRS pattern-ben minden lekérdezésnek (Query) van egy saját Query osztálya, még akkor is, ha az üres. Ez a konzisztencia és a kód későbbi bővíthetősége szempontjából fontos.

**Miért üres ez az osztály?**
- Az összes user lekérdezéséhez nincs szükség paraméterekre (nincs szűrés, nincs ID, stb.)
- A konstruktor üres marad
- Később könnyedén bővíthető (pl. pagination: `page`, `limit` paraméterek)

**CQRS elv:** Minden use case-nek (ebben az esetben: "add meg az összes user-t") saját Query objektuma van, még ha paraméter nélküli is.

<details>
<summary>📄 <strong>Teljes kód megtekintése</strong></summary>

```javascript
export class GetAllUsersQuery {
  constructor() {}
}
```

</details>

#### 4.2 GetAllUsersQueryHandler

**Fájl**: `src/Application/users/query/getAllUsersQueryHandler.js`

**Cél**: A GetAllUsersQuery feldolgozása és végrehajtása.

**Részletes leírás:**

A Handler az, amely ténylegesen végrehajtja a műveletet. Ez a "use case" implementáció.

**Struktúra:**

1. **Konstruktor:**
   - Fogad egy `userRepository` paramétert (Dependency Injection)
   - Eltárolja azt instance változóban (`this.userRepository`)
   - **Miért DI?** Később könnyű tesztelni (mock repository-val), és nem függ konkrét implementációtól

2. **handle(query) metódus:**
   - Fogadja a Query objektumot (jelen esetben üres)
   - Meghívja a repository `getAll()` metódusát
   - Visszaadja az eredményt
   - `async/await` használata az aszinkron művelethez

**Ez a Handler felelőssége:**
- Query feldolgozása
- Repository meghívása
- Eredmény visszaadása
- (Itt lehetne üzleti logika, validáció, stb. - de ennél az egyszerű lekérdezésnél nincs rá szükség)

**Nem a Handler felelőssége:**
- HTTP kérések kezelése (ezt a Controller végzi)
- Adatbázis műveletek (ezt a Repository végzi)

<details>
<summary>📄 <strong>Teljes kód megtekintése</strong></summary>

```javascript
export class GetAllUsersQueryHandler {
  constructor(userRepository) {
    this.userRepository = userRepository;
  }

  async handle(query) {
    return await this.userRepository.getAll();
  }
}
```

</details>

#### 4.3 GetUserByIdQuery

**Fájl**: `src/Application/users/query/getUserByIdQuery.js`

**Cél**: Query objektum egyedi felhasználó lekérdezéséhez ID alapján.

**Részletes leírás:**

Ez a Query már tartalmaz paramétert - a keresendő user ID-t.

**Konstruktor paraméter:**
- `id` - A keresendő felhasználó egyedi azonosítója (string formátumban)
- Eltároljuk `this.id`-ben, hogy a Handler hozzáférjen

**Példa használat:**
```javascript
const query = new GetUserByIdQuery('1707825600000');
```

**CQRS elv:** A Query objektum tiszta adatstruktúra (Data Transfer Object - DTO), csak az adatokat hordozza, logika nélkül.

<details>
<summary>📄 <strong>Teljes kód megtekintése</strong></summary>

```javascript
export class GetUserByIdQuery {
  constructor(id) {
    this.id = id;
  }
}
```

</details>

#### 4.4 GetUserByIdQueryHandler

**Fájl**: `src/Application/users/query/getUserByIdQueryHandler.js`

**Cél**: Egyedi felhasználó lekérdezésének feldolgozása validációval.

**Részletes leírás:**

**Konstruktor:**
- Ugyanaz a DI pattern, mint a GetAllUsersQueryHandler-nél

**handle(query) metódus lépései:**

1. **Repository meghívása:**
   ```javascript
   const user = await this.userRepository.getById(query.id);
   ```
   - Lekéri a user-t a query-ben kapott ID alapján
   - A repository `undefined`-et ad vissza, ha nincs találat

2. **Validáció:**
   ```javascript
   if (!user) {
     throw new Error('User not found');
   }
   ```
   - Ha nincs találat, kivételt dob
   - Ez az **üzleti logika része**: egy használhatatlan Query-t hibának tekintünk

3. **Eredmény visszaadása:**
   - Ha megvan a user, visszaadjuk

**Error Handling:**
- A dobott hiba később a Controller-ben lesz elkapva
- A Controller alakítja át HTTP 404 válasszá
- Ez szépen szétválasztja a rétegeket: Application Layer nem tud HTTP-ről, csak üzleti hibákat dob

<details>
<summary>📄 <strong>Teljes kód megtekintése</strong></summary>

```javascript
export class GetUserByIdQueryHandler {
  constructor(userRepository) {
    this.userRepository = userRepository;
  }

  async handle(query) {
    const user = await this.userRepository.getById(query.id);
    if (!user) {
      throw new Error('User not found');
    }
    return user;
  }
}
```

</details>

---

### 5. Application Layer - Command Implementálás

#### 5.1 CreateUserCommand

**Fájl**: `src/Application/users/command/createUserCommand.js`

**Cél**: Command objektum új felhasználó létrehozásához.

**Részletes leírás:**

**CQRS: Command vs Query**
- A **Query** adatokat kér le (olvasás) - nem változtatja a rendszer állapotát
- A **Command** műveletet hajt végre (írás) - megváltoztatja a rendszer állapotát

**Konstruktor paraméterek:**
- `name` (string) - A felhasználó neve (kötelező)
- `email` (string) - A felhasználó email címe (kötelező)
- `age` (number) - A felhasználó életkora (opcionális)

**Fontos:**
- Az ID-t NEM adjuk meg - azt a Repository generálja automatikusan
- A `createdAt` timestamp-et sem - azt is a Repository adja hozzá
- A Command csak az "üzleti adatokat" tartalmazza

**Példa használat:**
```javascript
const command = new CreateUserCommand('John Doe', 'john@example.com', 30);
```

**Clean Architecture elv:** A Command egy egyszerű DTO (Data Transfer Object), tisztán adatok, logika nélkül.

<details>
<summary>📄 <strong>Teljes kód megtekintése</strong></summary>

```javascript
export class CreateUserCommand {
  constructor(name, email, age) {
    this.name = name;
    this.email = email;
    this.age = age;
  }
}
```

</details>

#### 5.2 CreateUserCommandHandler

**Fájl**: `src/Application/users/command/createUserCommandHandler.js`

**Cél**: Új felhasználó létrehozási parancs végrehajtása validációval.

**Részletes leírás:**

**Konstruktor:**
- DI pattern: userRepository injektálás

**handle(command) metódus lépései:**

**1. Validáció (Üzleti szabályok):**
```javascript
if (!command.name || !command.email) {
  throw new Error('Name and email are required');
}
```
- Name és email **kötelező mezők**
- Ez üzleti logika - az Application Layer felelőssége
- Ha valamelyik hiányzik, kivételt dobunk
- Later: Bővíthető email formátum ellenőrzéssel, egyediség ellenőrzéssel, stb.

**2. User objektum összeállítása:**
```javascript
const userData = {
  name: command.name,
  email: command.email,
  age: command.age
};
```
- A Command adataiból létrehozunk egy plain objektumot
- Csak a "tiszta" adatok mennek tovább a Repository-nak
- Az age lehet `undefined` ha nem adták meg - ez rendben van

**3. Repository meghívása:**
```javascript
return await this.userRepository.create(userData);
```
- A Repository hozzáadja az ID-t és a createdAt-et
- Visszaadja a teljes user objektumot (ID-val és timestamp-el együtt)

**CommandHandler felelősségei:**
- ✅ Validáció (üzleti szabályok)
- ✅ Adatok előkészítése
- ✅ Repository meghívása
- ❌ NEM HTTP kezelés (azt a Controller végzi)
- ❌ NEM adatbázis műveletek (azt a Repository végzi)

<details>
<summary>📄 <strong>Teljes kód megtekintése</strong></summary>

```javascript
export class CreateUserCommandHandler {
  constructor(userRepository) {
    this.userRepository = userRepository;
  }

  async handle(command) {
    if (!command.name || !command.email) {
      throw new Error('Name and email are required');
    }
    
    const userData = {
      name: command.name,
      email: command.email,
      age: command.age
    };
    
    return await this.userRepository.create(userData);
  }
}
```

</details>

#### 5.3 UpdateUserCommand

**Fájl**: `src/Application/users/command/updateUserCommand.js`

**Cél**: Command objektum felhasználó módosításához.

**Részletes leírás:**

**Konstruktor paraméterek:**
- `id` (string) - A módosítandó user egyedi azonosítója (kötelező!)
- `name` (string) - Az új/frissített név
- `email` (string) - Az új/frissített email
- `age` (number) - Az új/frissített életkor

**Különbség a CreateUserCommand-hoz képest:**
- Ez tartalmaz **ID-t** is - tudnunk kell, melyik user-t módosítjuk
- Az ID a URL-ből jön majd (pl. `PUT /users/123456`)

**Példa használat:**
```javascript
const command = new UpdateUserCommand(
  '1707825600000',
  'John Updated',
  'john.new@example.com',
  31
);
```

<details>
<summary>📄 <strong>Teljes kód megtekintése</strong></summary>

```javascript
export class UpdateUserCommand {
  constructor(id, name, email, age) {
    this.id = id;
    this.name = name;
    this.email = email;
    this.age = age;
  }
}
```

</details>

#### 5.4 UpdateUserCommandHandler

**Fájl**: `src/Application/users/command/updateUserCommandHandler.js`

**Cél**: Felhasználó módosítási parancs végrehajtása.

**Részletes leírás:**

**handle(command) metódus lépései:**

**1. Adatok előkészítése:**
```javascript
const userData = {
  name: command.name,
  email: command.email,
  age: command.age
};
```
- Összegyjük a módosítandó adatokat
- **Fontos**: Az ID-t NEM küldjük tovább a userData-ban!
- Az ID külön paraméterként megy a repository-nak (biztonság és tisztaság)

**2. Repository meghívása:**
```javascript
const updatedUser = await this.userRepository.update(command.id, userData);
```
- A repository `update()` metódusa várja az ID-t és az új adatokat
- Visszaad `null`-t ha nem található a user
- Visszaadja a frissített user objektumot (updatedAt timestamp-el) ha sikeres

**3. Validáció (létezés ellenőrzés):**
```javascript
if (!updatedUser) {
  throw new Error('User not found');
}
```
- Ha `null` jött vissza, nem létezett ilyen ID-jű user
- Kivételt dobunk, amit a Controller HTTP 404-re alakít

**4. Eredmény visszaadása:**
- A frissített user objektum megy vissza a Controller-nek

**Későbbi bővítési lehetőségek:**
- Validáció csak a megváltozott mezőkre (partial update)
- Verzió kontroll (optimistic locking)
- Jogosultság ellenőrzés (csak saját profil módosítható)

<details>
<summary>📄 <strong>Teljes kód megtekintése</strong></summary>

```javascript
export class UpdateUserCommandHandler {
  constructor(userRepository) {
    this.userRepository = userRepository;
  }

  async handle(command) {
    const userData = {
      name: command.name,
      email: command.email,
      age: command.age
    };
    
    const updatedUser = await this.userRepository.update(command.id, userData);
    if (!updatedUser) {
      throw new Error('User not found');
    }
    
    return updatedUser;
  }
}
```

</details>

#### 5.5 DeleteUserCommand

**Fájl**: `src/Application/users/command/deleteUserCommand.js`

**Cél**: Command objektum felhasználó törléséhez.

**Részletes leírás:**

**Konstruktor paraméterek:**
- `id` (string) - A törlendő user egyedi azonosítója

**Egyszerű struktúra:**
- Csak egy ID-t tartalmaz - ez az egyetlen információ, ami kell a törléshez
- Hasonló a GetUserByIdQuery-hez, de szándékban különbözik (írás vs olvasás)

**Példa használat:**
```javascript
const command = new DeleteUserCommand('1707825600000');
```

**CQRS szétválasztás:** Bár hasonló a Query-hez, külön osztály kell, mert ez Command (írási művelet, állapot változtatás).

<details>
<summary>📄 <strong>Teljes kód megtekintése</strong></summary>

```javascript
export class DeleteUserCommand {
  constructor(id) {
    this.id = id;
  }
}
```

</details>

#### 5.6 DeleteUserCommandHandler

**Fájl**: `src/Application/users/command/deleteUserCommandHandler.js`

**Cél**: Felhasználó törlési parancs végrehajtása.

**Részletes leírás:**

**handle(command) metódus lépései:**

**1. Repository meghívása:**
```javascript
const deleted = await this.userRepository.delete(command.id);
```
- A repository `delete()` metódusa boolean-t ad vissza
- `true` - sikeres törlés
- `false` - nem létezett ilyen ID-jű user

**2. Validáció:**
```javascript
if (!deleted) {
  throw new Error('User not found');
}
```
- Ha törlés sikertelen (false), kivételt dobunk
- Ez konzisztens a többi handler validációjával

**3. Visszatérési érték:**
- **Nincs explicit return!**
- DELETE művelet nem ad vissza adatot (ún. "void" műv elet)
- A Controller HTTP 204 No Content választ küld

**Fontos különbség:**
- Ez az egyetlen Handler, ami nem ad vissza user objektumot
- REST convention: DELETE művelet után nincs response body

**Későbbi bővítési lehetőségek:**
- Soft delete (csak megjelölés töröltként, fizikai törlés helyett)
- Cascade delete (kapcsolódó adatok törlése is)
- Audit log (ki, mikor törölte)

<details>
<summary>📄 <strong>Teljes kód megtekintése</strong></summary>

```javascript
export class DeleteUserCommandHandler {
  constructor(userRepository) {
    this.userRepository = userRepository;
  }

  async handle(command) {
    const deleted = await this.userRepository.delete(command.id);
    if (!deleted) {
      throw new Error('User not found');
    }
  }
}
```

</details>

---

### 6. API Layer Implementálás

#### 6.1 userController.js - Controller

**Fájl**: `src/API/controllers/userController.js`

**Cél**: HTTP kérések kezelése és koordináció az Application layer-rel.

**Részletes leírás:**

A Controller a **Presentation Layer** része - ő az, aki tudja, mi az a HTTP, mi az a request és response. Az Application Layer-t (Handler-ek) használja az üzleti logika végrehajtására.

**Fájl szerkezete:**

**1. Importok:**
- Repository és összes Query/Command osztály
- Összes QueryHandler és CommandHandler osztály
- Összesen 11 import (1 Repository + 5 Query/Command pár)

**2. Dependency Injection beállítása:**
```javascript
const userRepository = new UserRepository();
const getAllUsersQueryHandler = new GetAllUsersQueryHandler(userRepository);
// ... stb
```
- Egy közös repository példány minden handler-nek
- Handler példányok modul szinten (singleton pattern)
- Ez egyszerűsíti a kódot - nem kell minden metódusban létrehozni

**3. Controller metódusok (5 db):**

**getAll(req, res)** - Összes user lekérése
- Query létrehozása (paraméter nélkül)
- Handler meghívása
- Eredmény visszaküldése JSON formátumban
- Error handling: 500 (szerver hiba)

**getById(req, res)** - Egy user lekérése
- Query létrehozása `req.params.id`-val (URL paraméter)
- Handler meghívása
- Eredmény visszaküldése
- Error handling: 404 (nem található)

**create(req, res)** - Új user létrehozása
- Command létrehozása `req.body` adatokból (POST body)
- Handler meghívása
- Eredmény visszaküldése **201 Created** státusszal
- Error handling: 400 (validációs hiba)

**update(req, res)** - User módosítása
- Command létrehozása ID-val (URL) + adatok (POST body)
- Handler meghívása
- Eredmény visszaküldése
- Error handling: 404 (nem található)

**delete(req, res)** - User törlése
- Command létrehozása csak ID-val
- Handler meghívása
- **204 No Content** státusz, üres body
- Error handling: 404 (nem található)

**HTTP Státusz kódok:**
- **200 OK** - Sikeres GET, PUT (default Express-ben)
- **201 Created** - Sikeres POST (új resource)
- **204 No Content** - Sikeres DELETE (nincs response body)
- **400 Bad Request** - Validációs hiba (hiányzó/helytelen adatok)
- **404 Not Found** - Resource nem található
- **500 Internal Server Error** - Váratlan szerver hiba

**Felelősségek:**
- ✅ HTTP kérés/válasz kezelése
- ✅ URL paraméterek és body adatok kinyerése
- ✅ Command/Query objektumok létrehozása
- ✅ Handler-ek hívása
- ✅ HTTP státusz kódok beállítása
- ✅ Error handling és átkonvertálása HTTP válaszokra
- ❌ NINCS üzleti logika (azt a Handler végzi)
- ❌ NINCS adatbázis művelet (azt a Repository végzi)

<details>
<summary>📄 <strong>Teljes kód megtekintése</strong></summary>

```javascript
import { UserRepository } from '../../Infrastructure/userRepository.js';
import { GetAllUsersQuery } from '../../Application/users/query/getAllUsersQuery.js';
import { GetAllUsersQueryHandler } from '../../Application/users/query/getAllUsersQueryHandler.js';
import { GetUserByIdQuery } from '../../Application/users/query/getUserByIdQuery.js';
import { GetUserByIdQueryHandler } from '../../Application/users/query/getUserByIdQueryHandler.js';
import { CreateUserCommand } from '../../Application/users/command/createUserCommand.js';
import { CreateUserCommandHandler } from '../../Application/users/command/createUserCommandHandler.js';
import { UpdateUserCommand } from '../../Application/users/command/updateUserCommand.js';
import { UpdateUserCommandHandler } from '../../Application/users/command/updateUserCommandHandler.js';
import { DeleteUserCommand } from '../../Application/users/command/deleteUserCommand.js';
import { DeleteUserCommandHandler } from '../../Application/users/command/deleteUserCommandHandler.js';

const userRepository = new UserRepository();
const getAllUsersQueryHandler = new GetAllUsersQueryHandler(userRepository);
const getUserByIdQueryHandler = new GetUserByIdQueryHandler(userRepository);
const createUserCommandHandler = new CreateUserCommandHandler(userRepository);
const updateUserCommandHandler = new UpdateUserCommandHandler(userRepository);
const deleteUserCommandHandler = new DeleteUserCommandHandler(userRepository);

export class UserController {
  async getAll(req, res) {
    try {
      const query = new GetAllUsersQuery();
      const users = await getAllUsersQueryHandler.handle(query);
      res.json(users);
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  }

  async getById(req, res) {
    try {
      const query = new GetUserByIdQuery(req.params.id);
      const user = await getUserByIdQueryHandler.handle(query);
      res.json(user);
    } catch (error) {
      res.status(404).json({ error: error.message });
    }
  }

  async create(req, res) {
    try {
      const command = new CreateUserCommand(
        req.body.name,
        req.body.email,
        req.body.age
      );
      const user = await createUserCommandHandler.handle(command);
      res.status(201).json(user);
    } catch (error) {
      res.status(400).json({ error: error.message });
    }
  }

  async update(req, res) {
    try {
      const command = new UpdateUserCommand(
        req.params.id,
        req.body.name,
        req.body.email,
        req.body.age
      );
      const user = await updateUserCommandHandler.handle(command);
      res.json(user);
    } catch (error) {
      res.status(404).json({ error: error.message });
    }
  }

  async delete(req, res) {
    try {
      const command = new DeleteUserCommand(req.params.id);
      await deleteUserCommandHandler.handle(command);
      res.status(204).send();
    } catch (error) {
      res.status(404).json({ error: error.message });
    }
  }
}
```

</details>

#### 6.2 userRouter.js - Router

**Fájl**: `src/API/routers/userRouter.js`

**Cél**: RESTful route-ok definiálása és Controller metódusokhoz kötése.

**Részletes leírás:**

A Router az Express moduláris routing rendszere - külön fájlban definiálhatjuk a route-okat, majd a főalkalmazásba importálhatjuk.

**Fájl szerkezete:**

**1. Importok:**
- `express` - Express framework
- `UserController` - A korábban létrehozott controller

**2. Router és Controller példányosítás:**
```javascript
const router = express.Router();
const userController = new UserController();
```

**3. Route definíciók (5 db):**

**REST API konvenciók:**

| HTTP Metódus | URL Pattern | Controller Metódus | Leírás |
|--------------|-------------|-------------------|--------|
| GET | `/` | `getAll()` | Összes user listázása |
| GET | `/:id` | `getById()` | Egyedi user lekérése |
| POST | `/` | `create()` | Új user létrehozása |
| PUT | `/:id` | `update()` | User teljes módosítása |
| DELETE | `/:id` | `delete()` | User törlése |

**Cél**: Express alkalmazás konfigurálása, middleware-ek és route-ok beállítása, szerver indítása.

**Részletes leírás:**

Ez az alkalmazás belépési pontja (entry point) - ez a fájl indul el, amikor `npm start`-ot futtatunk.

**Fájl szerkezete:**

**1. Importok:**
```javascript
import express from 'express';
import userRouter from './routers/userRouter.js';
```
- Express framework
- User router (korábban definiált route-ok)

**2. Express app instance:**
```javascript
const app = express();
```
- Létrehozza az Express alkalmazás objektumot
- Ezen fogunk middleware-eket és route-okat regisztrálni

**3. Konfigurációs változók:**
```javascript
const PORT = 3000;
```
- Port szám, ahol a szerver hallgatózni fog
- Később környezeti változóból is jöhet: `process.env.PORT || 3000`

**4. Middleware-ek:**
```javascript
app.use(express.json());
```
- **Kritikus fontosságú!** Ez parse-olja a JSON body-t a POST/PUT kérésekben
- Nélküle a `req.body` undefined lenne
- Az Express beépített middleware-je (korábban külön `body-parser` package volt)
- Automatikusan parse-olja ha `Content-Type: application/json`

**5. Route regisztráció:**
```javascript
app.use('/users', userRouter);
```
- Base path: `/users`
- A userRouter-ben definiált relatív útvonalak eléje kerül ez a prefix
- Így a `router.get('/')` valójában `GET /users` lesz
- Moduláris: könnyen hozzáadhatunk más router-eket (pl. `/posts`, `/comments`)

**Teljes útvonalak:**
- `GET /users` → összes user
- `GET /users/:id` → egy user
- `POST /users` → új user
- `PUT /users/:id` → user módosítás
- `DELETE /users/:id` → user törlés

**6. Szerver indítása:**
```javascript
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});
```
- `app.listen()` - Express metódus a szerver indításához
- Callback function - lefut, amikor a szerver sikeresen elindult
- Console log - visszajelzés, hogy minden OK

**Fontos koncepciók:**

**Middleware sorrend:**
- A middleware-ek **sorrendben** futnak!
- `express.json()` a route-ok ELŐTT kell legyen
- Így a route handler-ek már parse-olt body-t kapnak

**További bővítési lehetőségek:**
```javascript
app.use(cors());  // CORS engedélyezése
app.use(morgan('dev'));  // HTTP logging
app.use(helmet());  // Biztonsági headers
app.use('/posts', postRouter);  // További entitások
```

<details>
<summary>📄 <strong>Teljes kód megtekintése</strong></summary>

```javascript
import express from 'express';
import userRouter from './routers/userRouter.js';

const app = express();
const PORT = 3000;

// Middleware
app.use(express.json());

// Routes
app.use('/users', userRouter);

// Server
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});
```

</details>trong></summary>

```javascript
import express from 'express';
import { UserController } from '../controllers/userController.js';

const router = express.Router();
const userController = new UserController();

router.get('/', (req, res) => userController.getAll(req, res));
router.get('/:id', (req, res) => userController.getById(req, res));
router.post('/', (req, res) => userController.create(req, res));
router.put('/:id', (req, res) => userController.update(req, res));
router.delete('/:id', (req, res) => userController.delete(req, res));

export default router;
```

</details>

#### 6.3 server.js - Szerver konfiguráció

**Fájl**: `src/API/server.js`

```javascript
import express from 'express';
import userRouter from './routers/userRouter.js';

const app = express();
const PORT = 3000;

// Middleware
app.use(express.json());

// Routes
app.use('/users', userRouter);

// Server
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});
```

**Magyarázat:**

- **express.json()**: JSON body parser middleware (kötelező a POST/PUT kérésekhez)
- **Base path**: `/users` prefix minden user route-hoz
- **Port**: 3000 (módosítható szükség szerint)

---

## Futtatás és Tesztelés

### Szerver Indítása

```bash
npm start
```

Ez elindítja a nodemon-t, ami automatikusan újraindítja a szervert fájl módosítások esetén.

### API Endpoint-ok Tesztelése

#### 1. Összes User Lekérése

**Request:**
```http
GET http://localhost:3000/users
```

**Válasz:**
```json
[]
```

#### 2. Új User Létrehozása

**Request:**
```http
POST http://localhost:3000/users
Content-Type: application/json

{
  "name": "John Doe",
  "email": "john@example.com",
  "age": 30
}
```

**Válasz:**
```json
{
  "id": "1707825600000",
  "name": "John Doe",
  "email": "john@example.com",
  "age": 30,
  "createdAt": "2026-02-13T10:00:00.000Z"
}
```

#### 3. User Lekérése ID alapján

**Request:**
```http
GET http://localhost:3000/users/1707825600000
```

**Válasz:**
```json
{
  "id": "1707825600000",
  "name": "John Doe",
  "email": "john@example.com",
  "age": 30,
  "createdAt": "2026-02-13T10:00:00.000Z"
}
```

#### 4. User Módosítása

**Request:**
```http
PUT http://localhost:3000/users/1707825600000
Content-Type: application/json

{
  "name": "John Updated",
  "email": "john.updated@example.com",
  "age": 31
}
```

**Válasz:**
```json
{
  "id": "1707825600000",
  "name": "John Updated",
  "email": "john.updated@example.com",
  "age": 31,
  "createdAt": "2026-02-13T10:00:00.000Z",
  "updatedAt": "2026-02-13T10:05:00.000Z"
}
```

#### 5. User Törlése

**Request:**
```http
DELETE http://localhost:3000/users/1707825600000
```

**Válasz:**
```
204 No Content
```

### Tesztelési Eszközök

- **Postman**: GUI alapú API tesztelő
- **Thunder Client**: VS Code extension
- **cURL**: Parancssori eszköz
- **REST Client**: VS Code extension (.http fájlokkal)

---

## User Adatmodell

```javascript
{
  "id": "string",           // Automatikusan generált (timestamp)
  "name": "string",         // Kötelező
  "email": "string",        // Kötelező
  "age": number,            // Opcionális
  "createdAt": "ISO8601",   // Automatikusan generált létrehozáskor
  "updatedAt": "ISO8601"    // Automatikusan generált módosításkor
}
```

---

## Hibakezelés

### Validációs Hibák

- **400 Bad Request**: Hiányzó kötelező mezők (name, email)
- Üzenet: `"Name and email are required"`

### Not Found Hibák

- **404 Not Found**: User nem található (GET, PUT, DELETE)
- Üzenet: `"User not found"`

### Szerver Hibák

- **500 Internal Server Error**: Váratlan hiba (pl. fájl írási probléma)
- Üzenet: Részletes hibaüzenet

---

## Best Practice-ek és Elvek

### 1. Separation of Concerns (SoC)
- Minden réteg csak a saját felelősségével foglalkozik
- Controller nem tartalmaz üzleti logikát
- Repository nem validál

### 2. Dependency Inversion Principle (DIP)
- A magasabb szintű modulok nem függenek az alacsonyabb szintűektől
- Mindketten az absztrakciótól (interface) függenek
- `UserController` → `IUserRepository` interface-től függ, nem a konkrét implementációtól

### 3. Single Responsibility Principle (SRP)
- Minden osztálynak egy felelőssége van
- Command/Query külön handler-ben
- Repository csak adatelérésért felelős

### 4. CQRS Benefits
- Egyszerűbb kód, könnyebb megérteni
- Optimalizálható külön az olvasás és írás
- Könnyebb tesztelni

### 5. Async/Await használata
- Minden I/O művelet (fájl olvasás/írás) aszinkron
- Try-catch az error handling-hez

---

## Gyakori Problémák és Megoldások

### 1. ES6 Module hibák

**Hiba**: `SyntaxError: Cannot use import statement outside a module`

**Megoldás**: `package.json`-ben add hozzá: `"type": "module"`

### 2. __dirname not defined

**Hiba**: `ReferenceError: __dirname is not defined in ES module scope`

**Megoldás**: 
```javascript
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
```

### 3. Port already in use

**Hiba**: `Error: listen EADDRINUSE: address already in use :::3000`

**Megoldás**: 
- Másik port használata
- Folyamat leállítása: Windows-on `netstat -ano | findstr :3000` majd `taskkill /PID <PID> /F`

### 4. JSON parse error

**Hiba**: JSON fájl sérült vagy üres

**Megoldás**: Try-catch blokk a repository-ban, üres tömb visszaadása hiba esetén

---

## Ellenőrző Lista (Checklist)

- [ ] Projekt struktúra létrehozva
- [ ] package.json konfigurálva (`"type": "module"`)
- [ ] Függőségek telepítve
- [ ] Domain layer: IUserRepository létrehozva
- [ ] Infrastructure layer: UserRepository és user.json létrehozva
- [ ] Application layer: Összes Query és Command létrehozva
- [ ] Application layer: Összes Handler létrehozva
- [ ] API layer: UserController létrehozva
- [ ] API layer: userRouter létrehozva
- [ ] API layer: server.js létrehozva és konfigurálva
- [ ] Szerver elindul hiba nélkül
- [ ] GET /users működik
- [ ] POST /users működik és ment a JSON fájlba
- [ ] GET /users/:id működik
- [ ] PUT /users/:id működik
- [ ] DELETE /users/:id működik
- [ ] Hibakezelés működik mindenhol
- [ ] Kód tiszta, kommentek megfelelőek

---

## Összegzés

Ez a User Management rendszer egy jól strukturált, modern Node.js alkalmazás, amely követi a Clean Architecture és CQRS elveket. A kód könnyen karbantartható, skálázható, és előkészített későbbi bővítésekre (pl. adatbázis integráció).

**Főbb tanulságok:**
- Rétegelt architektúra előnyei
- CQRS pattern alkalmazása
- Dependency Injection használata
- REST API best practice-ek
- Async/Await és hibakezelés
- ES6 modulok használata Node.js-ben

**Fejlesztési idő becslés**: 3-5 óra (tapasztalattól függően)

**Nehézségi szint**: Közép-haladó

---

**Készítette**: GitHub Copilot  
**Dátum**: 2026. február 13.  
**Verzió**: 1.0
