masodik_gyak
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
node_modules/
|
||||||
|
.env
|
||||||
|
dev.db
|
||||||
|
dev.db-journal
|
||||||
|
prisma/migrations/
|
||||||
@@ -0,0 +1,423 @@
|
|||||||
|
# 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
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"watch": ["src", "server.js", ".env"],
|
||||||
|
"ext": "js,json",
|
||||||
|
"ignore": ["node_modules", "prisma/migrations"],
|
||||||
|
"exec": "node server.js"
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "user-management-backend",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "User management backend with CQRS and layered architecture",
|
||||||
|
"main": "./src/api/server.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node ./src/api/server.js",
|
||||||
|
"dev": "nodemon ./src/api/server.js",
|
||||||
|
"prisma:generate": "prisma generate",
|
||||||
|
"prisma:migrate": "prisma migrate dev",
|
||||||
|
"prisma:studio": "prisma studio"
|
||||||
|
},
|
||||||
|
"keywords": ["express", "prisma", "sqlite", "cqrs"],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"@prisma/client": "^5.8.0",
|
||||||
|
"dotenv": "^16.3.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"nodemon": "^3.0.2",
|
||||||
|
"prisma": "^5.8.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// This is your Prisma schema file,
|
||||||
|
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||||
|
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "sqlite"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Definiáld itt a User modelt
|
||||||
|
// Tartalmazza:
|
||||||
|
// - id (Int, @id, @default(autoincrement()))
|
||||||
|
// - email (String, @unique)
|
||||||
|
// - name (String)
|
||||||
|
// - password (String)
|
||||||
|
// - role (String, @default("user"))
|
||||||
|
// - createdAt (DateTime, @default(now()))
|
||||||
|
// - updatedAt (DateTime, @updatedAt)
|
||||||
|
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
const CreateUserCommand = require('../../application/user/command/CreateUserCommand');
|
||||||
|
const UpdateUserCommand = require('../../application/user/command/UpdateUserCommand');
|
||||||
|
const DeleteUserCommand = require('../../application/user/command/DeleteUserCommand');
|
||||||
|
const GetUserQuery = require('../../application/user/query/GetUserQuery');
|
||||||
|
const GetAllUsersQuery = require('../../application/user/query/GetAllUsersQuery');
|
||||||
|
|
||||||
|
const CreateUserHandler = require('../../application/user/command/CreateUserHandler');
|
||||||
|
const UpdateUserHandler = require('../../application/user/command/UpdateUserHandler');
|
||||||
|
const DeleteUserHandler = require('../../application/user/command/DeleteUserHandler');
|
||||||
|
const GetUserHandler = require('../../application/user/query/GetUserHandler');
|
||||||
|
const GetAllUsersHandler = require('../../application/user/query/GetAllUsersHandler');
|
||||||
|
|
||||||
|
class UserController {
|
||||||
|
constructor() {
|
||||||
|
this.createUserHandler = new CreateUserHandler();
|
||||||
|
this.updateUserHandler = new UpdateUserHandler();
|
||||||
|
this.deleteUserHandler = new DeleteUserHandler();
|
||||||
|
this.getUserHandler = new GetUserHandler();
|
||||||
|
this.getAllUsersHandler = new GetAllUsersHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/users - Összes user lekérése
|
||||||
|
async getAllUsers(req, res) {
|
||||||
|
try {
|
||||||
|
const query = new GetAllUsersQuery();
|
||||||
|
const users = await this.getAllUsersHandler.handle(query);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
data: users
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/users/:id - Egy user lekérése ID alapján
|
||||||
|
async getUserById(req, res) {
|
||||||
|
try {
|
||||||
|
const query = new GetUserQuery(parseInt(req.params.id));
|
||||||
|
const user = await this.getUserHandler.handle(query);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
data: user
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
message: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /api/users - Új user létrehozása
|
||||||
|
async createUser(req, res) {
|
||||||
|
try {
|
||||||
|
const { email, name, password, role } = req.body;
|
||||||
|
const command = new CreateUserCommand(email, name, password, role);
|
||||||
|
const user = await this.createUserHandler.handle(command);
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
success: true,
|
||||||
|
message: 'User sikeresen létrehozva!',
|
||||||
|
data: user
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PUT /api/users/:id - User frissítése
|
||||||
|
async updateUser(req, res) {
|
||||||
|
try {
|
||||||
|
const { email, name, role } = req.body;
|
||||||
|
const command = new UpdateUserCommand(
|
||||||
|
parseInt(req.params.id),
|
||||||
|
email,
|
||||||
|
name,
|
||||||
|
role
|
||||||
|
);
|
||||||
|
const user = await this.updateUserHandler.handle(command);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: 'User sikeresen frissítve!',
|
||||||
|
data: user
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE /api/users/:id - User törlése
|
||||||
|
async deleteUser(req, res) {
|
||||||
|
try {
|
||||||
|
const command = new DeleteUserCommand(parseInt(req.params.id));
|
||||||
|
const result = await this.deleteUserHandler.handle(command);
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: result.message
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
message: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = UserController;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const UserController = require('../controllers/UserController');
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
const userController = new UserController();
|
||||||
|
|
||||||
|
// GET /api/users - Összes user lekérése
|
||||||
|
router.get('/', (req, res) => userController.getAllUsers(req, res));
|
||||||
|
|
||||||
|
// GET /api/users/:id - Egy user lekérése ID alapján
|
||||||
|
router.get('/:id', (req, res) => userController.getUserById(req, res));
|
||||||
|
|
||||||
|
// POST /api/users - Új user létrehozása
|
||||||
|
router.post('/', (req, res) => userController.createUser(req, res));
|
||||||
|
|
||||||
|
// PUT /api/users/:id - User frissítése
|
||||||
|
router.put('/:id', (req, res) => userController.updateUser(req, res));
|
||||||
|
|
||||||
|
// DELETE /api/users/:id - User törlése
|
||||||
|
router.delete('/:id', (req, res) => userController.deleteUser(req, res));
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
require('dotenv').config();
|
||||||
|
const express = require('express');
|
||||||
|
const userRoutes = require('./routes/user.routes');
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const PORT = process.env.PORT || 3000;
|
||||||
|
|
||||||
|
// Middleware
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: true }));
|
||||||
|
|
||||||
|
// Routes
|
||||||
|
app.use('/api/users', userRoutes);
|
||||||
|
|
||||||
|
// Alapértelmezett route
|
||||||
|
app.get('/', (req, res) => {
|
||||||
|
res.json({
|
||||||
|
message: 'User Management API - CQRS & Layered Architecture',
|
||||||
|
endpoints: {
|
||||||
|
users: {
|
||||||
|
getAll: 'GET /api/users',
|
||||||
|
getById: 'GET /api/users/:id',
|
||||||
|
create: 'POST /api/users',
|
||||||
|
update: 'PUT /api/users/:id',
|
||||||
|
delete: 'DELETE /api/users/:id'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 404 handler
|
||||||
|
app.use((req, res) => {
|
||||||
|
res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Endpoint nem található!'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Error handler
|
||||||
|
app.use((err, req, res, next) => {
|
||||||
|
console.error(err.stack);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Szerver hiba történt!'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`Szerver fut a http://localhost:${PORT} címen`);
|
||||||
|
console.log(`Prisma Studio: npx prisma studio`);
|
||||||
|
});
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
class CreateUserCommand {
|
||||||
|
constructor(email, name, password, role = 'user') {
|
||||||
|
this.email = email;
|
||||||
|
this.name = name;
|
||||||
|
this.password = password;
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CreateUserCommand;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
class CreateUserHandler {
|
||||||
|
constructor() {
|
||||||
|
// TODO: Repository inicializálása
|
||||||
|
}
|
||||||
|
|
||||||
|
async handle(command) {
|
||||||
|
// TODO: Implementáld a user létrehozási logikát
|
||||||
|
// - Validációk
|
||||||
|
// - Email formátum ellenőrzése
|
||||||
|
// - Létező user ellenőrzése
|
||||||
|
// - User létrehozása repository-val
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CreateUserHandler;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
class DeleteUserCommand {
|
||||||
|
constructor(id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = DeleteUserCommand;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
class DeleteUserHandler {
|
||||||
|
constructor() {
|
||||||
|
// TODO: Repository inicializálása
|
||||||
|
}
|
||||||
|
|
||||||
|
async handle(command) {
|
||||||
|
// TODO: Implementáld a user törlési logikát
|
||||||
|
// - User létezésének ellenőrzése
|
||||||
|
// - Delete végrehajtása repository-val
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = DeleteUserHandler;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
class UpdateUserCommand {
|
||||||
|
constructor(id, email, name, role) {
|
||||||
|
this.id = id;
|
||||||
|
this.email = email;
|
||||||
|
this.name = name;
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = UpdateUserCommand;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
class UpdateUserHandler {
|
||||||
|
constructor() {
|
||||||
|
// TODO: Repository inicializálása
|
||||||
|
}
|
||||||
|
|
||||||
|
async handle(command) {
|
||||||
|
// TODO: Implementáld a user frissítési logikát
|
||||||
|
// - User létezésének ellenőrzése
|
||||||
|
// - Email egyediség ellenőrzése
|
||||||
|
// - Frissítendő adatok összeállítása
|
||||||
|
// - Update végrehajtása repository-val
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = UpdateUserHandler;
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
class GetAllUsersHandler {
|
||||||
|
constructor() {
|
||||||
|
// TODO: Repository inicializálása
|
||||||
|
}
|
||||||
|
|
||||||
|
async handle(query) {
|
||||||
|
// TODO: Implementáld az összes user lekérési logikát
|
||||||
|
// - Összes user lekérése repository-val
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = GetAllUsersHandler;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
class GetAllUsersQuery {
|
||||||
|
constructor() {
|
||||||
|
// Lehet bővíteni filterekkel, paginációval stb.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = GetAllUsersQuery;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
class GetUserHandler {
|
||||||
|
constructor() {
|
||||||
|
// TODO: Repository inicializálása
|
||||||
|
}
|
||||||
|
|
||||||
|
async handle(query) {
|
||||||
|
// TODO: Implementáld az egy user lekérési logikát
|
||||||
|
// - User lekérése ID alapján repository-val
|
||||||
|
// - Hibakezelés ha nem található
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = GetUserHandler;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
class GetUserQuery {
|
||||||
|
constructor(id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = GetUserQuery;
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// Interface a UserRepository számára
|
||||||
|
// JavaScript-ben nincs natív interface, ezért dokumentációként szolgál
|
||||||
|
|
||||||
|
class IUserRepository {
|
||||||
|
/**
|
||||||
|
* Új user létrehozása
|
||||||
|
* @param {Object} userData - User adatai (email, name, password, role)
|
||||||
|
* @returns {Promise<Object>} Létrehozott user objektum
|
||||||
|
*/
|
||||||
|
async create(userData) {
|
||||||
|
throw new Error('create() metódust implementálni kell');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User keresése ID alapján
|
||||||
|
* @param {number} id - User ID
|
||||||
|
* @returns {Promise<Object|null>} User objektum vagy null
|
||||||
|
*/
|
||||||
|
async findById(id) {
|
||||||
|
throw new Error('findById() metódust implementálni kell');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User keresése email alapján
|
||||||
|
* @param {string} email - User email címe
|
||||||
|
* @returns {Promise<Object|null>} User objektum vagy null
|
||||||
|
*/
|
||||||
|
async findByEmail(email) {
|
||||||
|
throw new Error('findByEmail() metódust implementálni kell');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Összes user lekérése
|
||||||
|
* @returns {Promise<Array>} User objektumok tömbje
|
||||||
|
*/
|
||||||
|
async findAll() {
|
||||||
|
throw new Error('findAll() metódust implementálni kell');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User frissítése
|
||||||
|
* @param {number} id - User ID
|
||||||
|
* @param {Object} userData - Frissítendő adatok
|
||||||
|
* @returns {Promise<Object>} Frissített user objektum
|
||||||
|
*/
|
||||||
|
async update(id, userData) {
|
||||||
|
throw new Error('update() metódust implementálni kell');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User törlése
|
||||||
|
* @param {number} id - User ID
|
||||||
|
* @returns {Promise<Object>} Törölt user objektum
|
||||||
|
*/
|
||||||
|
async delete(id) {
|
||||||
|
throw new Error('delete() metódust implementálni kell');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = IUserRepository;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// TODO: Definiáld a User domain modelt
|
||||||
|
// Ez az üzleti logikai entitás
|
||||||
|
// Tartalmazza:
|
||||||
|
// - Tulajdonságok: id, email, name, password, role, createdAt, updatedAt
|
||||||
|
// - Metódusok: isAdmin(), canEdit(), validate()
|
||||||
|
|
||||||
|
class User {
|
||||||
|
// Implementáld a User domain modelt
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = User;
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
const { PrismaClient } = require('@prisma/client');
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
module.exports = prisma;
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
const prisma = require('../database/prisma');
|
||||||
|
const IUserRepository = require('../../domain/interfaces/IUserRepository');
|
||||||
|
|
||||||
|
// UserRepository implementálja az IUserRepository interfészt
|
||||||
|
class UserRepository extends IUserRepository {
|
||||||
|
async create(userData) {
|
||||||
|
// TODO: Implementáld a user létrehozást Prisma-val
|
||||||
|
// Példa: return await prisma.user.create({ data: userData });
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
async findById(id) {
|
||||||
|
// TODO: Implementáld a user keresést ID alapján
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
async findByEmail(email) {
|
||||||
|
// TODO: Implementáld a user keresést email alapján
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAll() {
|
||||||
|
// TODO: Implementáld az összes user lekérését
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id, userData) {
|
||||||
|
// TODO: Implementáld a user frissítést
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(id) {
|
||||||
|
// TODO: Implementáld a user törlést
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = UserRepository;
|
||||||
Reference in New Issue
Block a user