# User Management Backend - Gyakorlat

## Projekt Célja

User kezelő backend implementálása CQRS pattern és layered architecture használatával.

## Technológiák

- Node.js + Express
- Prisma ORM + SQLite
- JavaScript (nem TypeScript)
- Layered Architecture
- CQRS Pattern

## Projekt Struktúra

```
├── prisma/
│   └── schema.prisma                     # Adatbázis séma (TODO)
├── src/
│   ├── api/                              # API réteg
│   │   ├── controllers/
│   │   │   └── UserController.js         # Kész
│   │   └── routes/
│   │       └── user.routes.js            # Kész
│   ├── application/                      # Alkalmazási logika
│   │   └── user/
│   │       ├── command/
│   │       │   ├── CreateUserCommand.js  # Kész
│   │       │   ├── CreateUserHandler.js  # TODO
│   │       │   ├── UpdateUserCommand.js  # Kész
│   │       │   ├── UpdateUserHandler.js  # TODO
│   │       │   ├── DeleteUserCommand.js  # Kész
│   │       │   └── DeleteUserHandler.js  # TODO
│   │       └── query/
│   │           ├── GetUserQuery.js       # Kész
│   │           ├── GetUserHandler.js     # TODO
│   │           ├── GetAllUsersQuery.js   # Kész
│   │           └── GetAllUsersHandler.js # TODO
│   ├── domain/                           # Domain réteg
│   │   ├── interfaces/
│   │   │   └── IUserRepository.js        # Kész (interfész definíció)
│   │   └── models/
│   │       └── User.js                   # TODO
│   └── infrastructure/                   # Infrastruktúra réteg
│       ├── database/
│       │   └── prisma.js                 # Kész
│       └── repositories/
│           └── UserRepository.js          # TODO (extends IUserRepository)
├── .env                                   # Kész
├── package.json                           # Kész
└── server.js                              # Kész
```

## Indítás

```bash
npm install
npx prisma generate
npx prisma migrate dev --name init
npm run dev
```

Szerver: http://localhost:3000

## Feladatok

### 1. Prisma Schema (10 perc)

Fájl: `prisma/schema.prisma`

Definiáld a User modelt a TODO komment helyén:

```prisma
model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String
  password  String
  role      String   @default("user")
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
```

Migráció után ellenőrizd: `npx prisma studio`

### 2. UserRepository Implementáció (20 perc)

Fájl: `src/infrastructure/repositories/UserRepository.js`

A UserRepository már extends IUserRepository-t. Implementáld a metódusokat:

**create(userData)**
```javascript
return await prisma.user.create({ data: userData });
```

**findById(id)**
```javascript
return await prisma.user.findUnique({ 
  where: { id: parseInt(id) } 
});
```

**findByEmail(email)**
```javascript
return await prisma.user.findUnique({ 
  where: { email } 
});
```

**findAll()**
```javascript
return await prisma.user.findMany({ 
  orderBy: { createdAt: 'desc' } 
});
```

**update(id, userData)**
```javascript
return await prisma.user.update({ 
  where: { id: parseInt(id) }, 
  data: userData 
});
```

**delete(id)**
```javascript
return await prisma.user.delete({ 
  where: { id: parseInt(id) } 
});
```

### 3. Domain Model (10 perc)

Fájl: `src/domain/models/User.js`

```javascript
class User {
  constructor(id, email, name, password, role, createdAt, updatedAt) {
    this.id = id;
    this.email = email;
    this.name = name;
    this.password = password;
    this.role = role;
    this.createdAt = createdAt;
    this.updatedAt = updatedAt;
  }

  isAdmin() {
    return this.role === 'admin';
  }

  canEdit(targetUser) {
    return this.isAdmin() || this.id === targetUser.id;
  }

  validate() {
    if (!this.email || !this.name || !this.password) {
      throw new Error('Email, name és password kötelező');
    }
    
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(this.email)) {
      throw new Error('Hibás email formátum');
    }
    
    if (this.password.length < 6) {
      throw new Error('Jelszó min. 6 karakter');
    }
  }
}
```

### 4. Command Handlers (25 perc)

**CreateUserHandler** (`src/application/user/command/CreateUserHandler.js`)

```javascript
const UserRepository = require('../../../infrastructure/repositories/UserRepository');

class CreateUserHandler {
  constructor() {
    this.userRepository = new UserRepository();
  }

  async handle(command) {
    // 1. Validáció
    if (!command.email || !command.name || !command.password) {
      throw new Error('Email, name és password kötelező');
    }

    // 2. Email formátum
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(command.email)) {
      throw new Error('Hibás email formátum');
    }

    // 3. Duplikáció ellenőrzés
    const existing = await this.userRepository.findByEmail(command.email);
    if (existing) {
      throw new Error('Email már használatban');
    }

    // 4. Létrehozás
    return await this.userRepository.create({
      email: command.email,
      name: command.name,
      password: command.password,
      role: command.role || 'user'
    });
  }
}

module.exports = CreateUserHandler;
```

**UpdateUserHandler** (`src/application/user/command/UpdateUserHandler.js`)

```javascript
const UserRepository = require('../../../infrastructure/repositories/UserRepository');

class UpdateUserHandler {
  constructor() {
    this.userRepository = new UserRepository();
  }

  async handle(command) {
    // 1. Létezés ellenőrzés
    const user = await this.userRepository.findById(command.id);
    if (!user) {
      throw new Error('User nem található');
    }

    // 2. Email egyediség (ha változott)
    if (command.email && command.email !== user.email) {
      const existing = await this.userRepository.findByEmail(command.email);
      if (existing) {
        throw new Error('Email már használatban');
      }
    }

    // 3. Frissítendő adatok
    const updateData = {};
    if (command.email) updateData.email = command.email;
    if (command.name) updateData.name = command.name;
    if (command.role) updateData.role = command.role;

    // 4. Frissítés
    return await this.userRepository.update(command.id, updateData);
  }
}

module.exports = UpdateUserHandler;
```

**DeleteUserHandler** (`src/application/user/command/DeleteUserHandler.js`)

```javascript
const UserRepository = require('../../../infrastructure/repositories/UserRepository');

class DeleteUserHandler {
  constructor() {
    this.userRepository = new UserRepository();
  }

  async handle(command) {
    const user = await this.userRepository.findById(command.id);
    if (!user) {
      throw new Error('User nem található');
    }

    await this.userRepository.delete(command.id);
    return { message: 'User törölve', id: command.id };
  }
}

module.exports = DeleteUserHandler;
```

### 5. Query Handlers (10 perc)

**GetUserHandler** (`src/application/user/query/GetUserHandler.js`)

```javascript
const UserRepository = require('../../../infrastructure/repositories/UserRepository');

class GetUserHandler {
  constructor() {
    this.userRepository = new UserRepository();
  }

  async handle(query) {
    const user = await this.userRepository.findById(query.id);
    if (!user) {
      throw new Error('User nem található');
    }
    return user;
  }
}

module.exports = GetUserHandler;
```

**GetAllUsersHandler** (`src/application/user/query/GetAllUsersHandler.js`)

```javascript
const UserRepository = require('../../../infrastructure/repositories/UserRepository');

class GetAllUsersHandler {
  constructor() {
    this.userRepository = new UserRepository();
  }

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

module.exports = GetAllUsersHandler;
```

## Tesztelés

### Endpoints

**Create User**
```bash
POST http://localhost:3000/api/users
Content-Type: application/json

{
  "email": "test@test.com",
  "name": "Test User",
  "password": "password123"
}
```

**Get All Users**
```bash
GET http://localhost:3000/api/users
```

**Get User by ID**
```bash
GET http://localhost:3000/api/users/1
```

**Update User**
```bash
PUT http://localhost:3000/api/users/1
Content-Type: application/json

{
  "name": "Updated Name"
}
```

**Delete User**
```bash
DELETE http://localhost:3000/api/users/1
```

### Hibakezelés Tesztelése

- Hiányzó email/name/password
- Duplikált email
- Nem létező user ID
- Hibás email formátum

## Architektúra

### Layered Architecture

```
API Layer (routes, controllers)
    ↓
Application Layer (commands, queries, handlers)
    ↓
Domain Layer (models, interfaces)
    ↓
Infrastructure Layer (database, repositories)
```

### CQRS Pattern

**Command** - Írás (Create, Update, Delete)
**Query** - Olvasás (Get, GetAll)

Előnyök:
- Szeparált felelősségek
- Könnyebb skálázhatóság
- Tiszta intent

## Parancsok

```bash
npm run dev              # fejlesztői szerver
npm start                # produkciós szerver
npx prisma generate      # Prisma client generálás
npx prisma migrate dev   # migráció
npx prisma studio        # adatbázis GUI
```

## Gyakori Hibák

**Cannot find module '@prisma/client'**
→ `npx prisma generate`

**Port already in use**
→ `.env`-ben módosítsd a PORT értékét

**Not implemented**
→ Implementáld a TODO-kat

## Bónusz

1. bcrypt jelszó hash-elés
2. JWT authentikáció
3. Request logging middleware
4. Joi validáció
5. Error handling middleware
