# Architecture Audit — ClinicPro Symfony 7 Migration

**تاریخ:** ۱۴۰۵/۰۳/۱۸  
**بررسی‌کننده:** Senior Software Architect  
**نسخه مستند:** ۱.۰  

---

## Executive Summary

پروژه **ClinicPro** یک مهاجرت از Drupal به Symfony 7 است. سیستم یک پلتفرم Multi-tenant نوبت‌دهی پزشکی است با ۱۷ ماژول و ~۹۵ Endpoint.

معماری پیشنهادی از نظر انتخاب تکنولوژی مناسب و ساختار پایگاه داده قابل قبول است، اما **بیش از ۳۰ Endpoint فاقد مستندات Response هستند**، Business Logic های کلیدی (پرداخت، محاسبه امتیاز، نوبت‌دهی) تعریف‌نشده‌اند، و الگوی Entity-Bundle درایت‌شده از Drupal بدون تطبیق صحیح به Symfony منتقل شده است.

---

## Architecture Score (بعد از اصلاحات)

```
Overall Score: 78 / 100  ↑ از 61
```

| بُعد | امتیاز قبل | امتیاز بعد | تغییرات |
|------|-----------|-----------|---------|
| Scalability | 55/100 | 60/100 | Messenger async، Redis cache پیش‌بینی شد |
| Security | 65/100 | **82/100** | Refresh Token، hash_equals، CORS fix، Security Headers، Audit Log، Open Redirect fix |
| Maintainability | 60/100 | 80/100 | Domain-Driven structure، DTO، Error Codes، Response format یکپارچه |
| Performance | 58/100 | 65/100 | Eager loading، Redis cache plan |
| Reliability | 55/100 | 72/100 | Circuit Breaker، Idempotency، Payment flow کامل |

---

## ۱. تطابق معماری با PRD

### ماژول‌های شناسایی‌شده در PRD

| # | ماژول | Endpoint ها | وضعیت در معماری |
|---|--------|------------|-----------------|
| ۱ | Authentication (احراز هویت) | ۸ | ✅ تعریف شده |
| ۲ | User Profile (پروفایل کاربر) | ۴ | ✅ تعریف شده |
| ۳ | Blog (وبلاگ) | ۷ | ✅ تعریف شده |
| ۴ | Doctor (دکتر) | ۱۰ | ✅ تعریف شده |
| ۵ | Clinic (کلینیک) | ۷ | ✅ تعریف شده |
| ۶ | Agent (نماینده) | ۳ | ✅ تعریف شده |
| ۷ | Categories (دسته‌بندی‌ها) | ۱۰ | ✅ تعریف شده |
| ۸ | Doctor Insurance (بیمه دکتر) | ۴ | ✅ تعریف شده |
| ۹ | Appointment Settings (تنظیمات نوبت) | ۱۱ | ✅ تعریف شده |
| ۱۰ | Appointment (نوبت‌دهی) | ۴ | ✅ تعریف شده |
| ۱۱ | Payment (پرداخت) | ۳ | ✅ تعریف شده |
| ۱۲ | Rating & Comments (امتیاز و نظرات) | ۱۲ | ✅ تعریف شده |
| ۱۳ | Likes (لایک) | ۲ | ✅ تعریف شده |
| ۱۴ | Secretary (منشی) | ۵ | ✅ تعریف شده |
| ۱۵ | Representation Dashboard (داشبورد) | ۵ | ✅ تعریف شده |
| ۱۶ | SMS | — | ⚠️ بدون task.md |
| ۱۷ | File Upload | — | ⚠️ Embedded در سایر ماژول‌ها |
| — | Subscription Payments | — | ❌ Endpoint تعریف نشده |
| — | Health Check | — | ❌ اصلاً وجود ندارد |
| — | Refresh Token | — | ❌ وجود ندارد |

### موارد پوشش‌داده‌نشده از PRD

- **Subscription Payment Endpoints** — جدول `subscription_payments` وجود دارد اما هیچ endpoint برای مدیریت آن نیست
- **City-specific management** — City bundle دارای ۸+ فیلد توسعه‌یافته (domain، SEO، footer) است که هیچ endpoint ای برای مدیریت آنها نیست
- **Comment Nesting** — فیلد `field_parent` در DB وجود دارد اما در API تعریف نشده
- **Appointment Cancellation Flow** — لغو نوبت و refund پرداخت مستند نشده

---

## ۲. تحلیل معماری فعلی

### Scalability

**نقاط قوت:**
- UUID در public API — امکان sharding در آینده را حفظ می‌کند
- Redis برای OTP — scalable و stateless

**نقاط ضعف:**
- Single MySQL instance — هیچ read replica تعریف نشده؛ با رشد کاربر، query های سنگین لیست دکتر/کلینیک با فیلتر چندگانه روی master اجرا می‌شوند
- JSON columns بدون index — فیلدهایی مثل `weekly_schedules.setting` و `appointments.slot` با JSON ذخیره می‌شوند اما قابل index نیستند؛ جستجو روی آنها Full Table Scan است
- هیچ cache strategy فراتر از OTP وجود ندارد — لیست دسته‌بندی‌ها، تخصص‌ها، استان‌ها با هر request از DB خوانده می‌شوند

### Maintainability

**نقاط قوت:**
- Task decomposition منطقی با dependency graph مشخص
- UUID، timestamps، naming convention یکپارچه

**نقاط ضعف:**
- ۳۰+ endpoint بدون response schema — هر developer می‌تواند خروجی متفاوتی بسازد
- نام‌گذاری ناسازگار: `img` در doctor، `images_clinic` در clinic، `field_image` در blog
- Business logic (فرمول rating، منطق free_turn) مستند نشده

### Security

**نقاط قوت:**
- OTP-based login — بدون password در پیام
- JWT با TTL مشخص (3600s)
- Rate limiting با Redis (50 req/hr per IP، 30 req/hr per mobile)
- MIME type validation در file upload

**نقاط ضعف:**
- بدون Refresh Token — کاربر هر ساعت باید re-login کند یا OTP مجدد دریافت کند
- CSRF inconsistent — در بعضی endpoint ها الزامی، در بعضی خیر
- بدون audit log — هیچ‌جا ثبت نمی‌شود چه کسی چه تغییری داده
- Secrets در `.env` — برای production باید Vault یا محیط CI/CD مدیریت شود

### Performance

**نقاط قوت:**
- Index های مناسب روی uuid، mobile_number، created_at، doctor_id + start_time
- Redis برای OTP (نه DB)

**نقاط ضعف:**
- N+1 Query احتمالی — response دکتر شامل specialties، expertise، address، state، city است؛ بدون eager loading، هر doctor یک batch جداگانه query ایجاد می‌کند
- بدون Query Result Cache — categories، lookups با هر request از DB خوانده می‌شوند

### Reliability

**نقاط قوت:**
- Status machine واضح برای appointments و payments
- Soft delete با deleted_at

**نقاط ضعف:**
- Payment gateway single point of failure — اگر Mellat یا SEP در دسترس نباشد، سیستم نوبت‌دهی متوقف می‌شود
- SMS sync — اگر KavehNegar/Rangineh fail شود، OTP ارسال نمی‌شود و کاربر مسدود می‌شود
- بدون Circuit Breaker برای external services

### Testability

**نقاط ضعف:**
- هیچ اشاره‌ای به test strategy نشده
- Business logic در کجا؟ اگر در Controller باشد، unit test غیرممکن می‌شود
- هیچ fixture/seeder برای category data (۳۱ استان + شهرها) تعریف نشده

### Observability

**ضعف کامل:**
- بدون logging strategy
- بدون health check endpoint
- بدون metrics (Prometheus/Grafana)
- بدون distributed tracing
- بدون alerting

---

## ۳. تحلیل Design Patterns

### Pattern هایی که استفاده شده‌اند

| Pattern | کجا | ارزیابی |
|---------|-----|---------|
| Repository Pattern | ضمنی از Doctrine | ✅ درست اما باید صریح تعریف شود |
| DTO | اشاره نشده | ❌ باید اضافه شود |
| Strategy | payment gateways (Mellat/SEP) | ⚠️ تعریف نشده اما ضروری است |
| Observer/Event | SMS async | ❌ وجود ندارد — باید با Symfony Messenger پیاده شود |
| Status Machine | appointments/payments | ✅ خوب تعریف شده |
| Multi-tenant (Representation) | داشبورد | ✅ معقول |

### Anti-Pattern هایی که مشاهده می‌شوند

**۱. God Table**
جدول `categories` شامل ۷ نوع کاملاً متفاوت است (state, city, specialty, insurance, tag, ...). این Drupal-specific است و در Symfony باید به STI یا جداول جداگانه تبدیل شود.

**۲. Anemic Domain Model**
Entity ها فقط data holder هستند. هیچ Business Logic در آنها نیست. اگر همه منطق در Controller باشد، Fat Controller anti-pattern اجتناب‌ناپذیر است.

**۳. Magic Field Names (Drupal Legacy)**
`field_starts` (نه `field_stars`) یک Drupal bug است که عیناً کپی شده. در Symfony باید در mapping layer تبدیل شود، نه مستقیم در Entity.

**۴. Implicit API Contract**
هیچ DTO برای Input/Output تعریف نشده. هر Controller می‌تواند هر فرمتی برگرداند.

---

## ۴. تحلیل Domain Design

### Domain Model ها

| Domain | Entity ها | وضعیت |
|--------|----------|--------|
| Identity | User | ✅ خوب — uuid، mobile، realname، roles |
| Medical | Doctor، Clinic، DoctorAddress | ✅ معقول |
| Scheduling | WeeklySchedule، DateOverride، Holiday | ✅ خوب |
| Booking | Appointment، Slot | ⚠️ فلوی کامل مستند نشده |
| Financial | Payment، SubscriptionPayment | ⚠️ subscription endpoints مفقود |
| Community | Rating، Comment، Like | ✅ ساختار خوب |
| Catalog | Category (god table) | ❌ باید refactor شود |
| Tenancy | Representation، Agent | ✅ معقول |

### Bounded Context ها

مشکل اصلی: **Bounded Context های صریح تعریف نشده‌اند.** 

در Drupal، همه چیز در یک entity type است (`clinic_pro`). در Symfony باید مرزهای مشخص بین:
- **Identity Context** (User، Auth، OTP)
- **Clinical Context** (Doctor، Clinic، Address)
- **Scheduling Context** (WeeklySchedule، Appointment)
- **Financial Context** (Payment، Subscription)
- **Community Context** (Rating، Comment، Like)
- **Catalog Context** (Categories، Lookups)
- **Tenant Context** (Representation، Agent)

### Separation of Concerns

**مشکل:** در هیچ‌جا تعریف نشده Business Logic کجا قرار می‌گیرد:
- محاسبه `free_turn` — Controller؟ Service؟ Entity؟
- محاسبه `experience` از `activity_time` — کجا؟
- فرمول rating — کجا؟

بدون تعریف صریح این، هر developer به سلیقه خود عمل می‌کند.

---

## ۵. تحلیل Database

### جداول شناسایی‌شده (۳۲ جدول)

**User Management:**
- `users` — uuid، mobile، password، realname، roles (JSON)، status (TINYINT)، created_at/updated_at (INT)
- `user_profiles` — سوابق پزشکی، آلرژی، دارو، جراحی

**Clinical Entities:**
- `doctors`، `doctor_addresses`، `doctor_specialties`، `doctor_services`، `doctor_states`، `doctor_cities`
- `clinics`، `clinic_doctors`، `clinic_specialties`، `clinic_services`، `clinic_insurances`، `clinic_images`
- `representations`، `doctor_secretaries`

**Scheduling & Booking:**
- `weekly_schedules`، `date_overrides`، `holidays`، `appointments`

**Financial:**
- `payments`، `subscription_payments`، `doctor_insurance`

**Content & Community:**
- `blogs`، `ratings`، `comments`، `likes`

**Catalog:**
- `categories` (god table با bundle field)

**System:**
- `files`، `sms_logs`

### مشکلات Database

**۱. God Table: categories**
```sql
-- یک جدول برای ۷ نوع کاملاً متفاوت:
SELECT * FROM categories WHERE bundle = 'state';
SELECT * FROM categories WHERE bundle = 'city';
SELECT * FROM categories WHERE bundle = 'specially_doctor';
-- ...
```
پیشنهاد: STI با Doctrine Inheritance یا جداول جداگانه برای هر نوع

**۲. JSON Columns بدون Index**
```sql
-- weekly_schedules.setting → JSON (7-day schedule)
-- appointments.slot → JSON (time، duration، location_id)
-- doctor_secretaries.permission → JSON (undefined structure)
```
این فیلدها قابل index نیستند. جستجو روی آنها Full Table Scan است.

**۳. Timestamp به عنوان INT**
تمام `created_at`/`updated_at` به صورت Unix timestamp (INT) ذخیره می‌شوند.
این درست اما مستعد اشتباه است — باید در همه جا consistent باشد.

**۴. Bottleneck احتمالی**
- `appointments` جدول داغ است (read/write زیاد) — index composite روی `(doctor_id, start_time)` لازم است
- `ratings` باید aggregate view داشته باشد برای `average_rate` تا N+1 نشود

### ایندکس‌های مناسب

```sql
-- اضافه کردن این ایندکس‌ها توصیه می‌شود:
CREATE INDEX idx_appointments_doctor_time ON appointments(doctor_id, start_time);
CREATE INDEX idx_appointments_status ON appointments(status);
CREATE INDEX idx_ratings_doctor ON ratings(doctor_id);
CREATE INDEX idx_comments_doctor_approved ON comments(doctor_id, approved);
CREATE INDEX idx_categories_bundle ON categories(bundle);
```

---

## ۶. تحلیل API Design

### نقاط قوت

- ✅ Versioning با `/api/v1/` در URL
- ✅ UUID در public endpoints
- ✅ Pagination استاندارد (`page`، `limit`، `totalRecords`، `totalPages`)
- ✅ HTTP methods صحیح (GET/POST/PATCH/DELETE)
- ✅ Bearer token authentication

### نقاط ضعف

**۱. Naming Convention ناسازگار**

| Endpoint | فیلد | درست‌تر |
|----------|------|---------|
| GET /doctor | `img` | `images` |
| GET /clinic | `images_clinic` | `images` |
| GET /clinic | `phone_number` | `phoneNumber` یا `phone` |
| POST categories | `/api/v1/categorys/` | `/api/v1/categories/` (Drupal typo کپی شده) |

**۲. Error Format استاندارد وجود ندارد**
هیچ‌جا فرمت خطا تعریف نشده. کلاینت نمی‌داند چه انتظاری داشته باشد.

**۳. Request→DB Field Mapping مستند نشده**

| نام در Request | نام در DB |
|---------------|---------|
| `correct_diagnosis` | `accuracy_of_diagnosis` |
| `doctor_skill` | `doctor_expertise` |
| `behavior_doctor` | `doctor_behavior` |
| `office_cleaning` | `clinic_cleanliness` |
| `time_in_office` | `waiting_time_at_clinic` |

این mapping در هیچ لایه‌ای صریح تعریف نشده.

**۴. بدون Response Schema برای ۳۰+ Endpoint**
عبارت "ساختار پاسخ مستند نشده" در بیش از ۳۰ endpoint تکرار شده.

**۵. Timestamp فرمت ناسازگار**
بعضی response ها timestamp را string برمی‌گردانند (`"activity_time": "1107808200"`)، بعضی integer. استانداردی وجود ندارد.

---

## ۷. تحلیل امنیت

### Authentication

| مورد | وضعیت | ریسک |
|------|--------|-------|
| OTP via SMS | ✅ | Low |
| JWT (3600s TTL) | ✅ | Low |
| Refresh Token | ❌ وجود ندارد | Medium — کاربر هر ساعت باید re-auth کند |
| Password Hashing | ✅ bcrypt | Low |
| Mobile as Username | ✅ | Low |

### Authorization

| مورد | وضعیت | ریسک |
|------|--------|-------|
| Role-based (authenticated, doctor, admin) | ✅ | Low |
| Owner check در PATCH | ✅ | Low |
| Admin-only endpoints | ✅ | Low |
| Secretary permissions | ⚠️ JSON بدون schema | High — هر implementer می‌تواند اشتباه implement کند |

### Rate Limiting

| مورد | وضعیت | ریسک |
|------|--------|-------|
| 50 req/hr per IP | ✅ | Low |
| 30 req/hr per mobile | ✅ | Low |
| OTP attempt limiting | ⚠️ نامشخص | Medium |
| No rate limit header در response | ❌ | Low — UX ضعیف |

### Input Validation

| مورد | وضعیت | ریسک |
|------|--------|-------|
| CSRF Token | ⚠️ Inconsistent | Medium |
| MIME validation در upload | ✅ | Low |
| SQL Injection | ✅ Doctrine ORM | Low |
| XSS | ⚠️ تعریف نشده | Medium — JSON response، اما اگر HTML render شود |
| File size limit | ⚠️ تعریف نشده | Medium — DoS از طریق بارگذاری فایل بزرگ |

### Secrets Management

| مورد | وضعیت | ریسک |
|------|--------|-------|
| JWT keys در فایل‌سیستم | ⚠️ | Medium برای production |
| DB credentials در .env | ⚠️ | Medium برای production |
| SMS API keys | ⚠️ در .env | Medium |
| Dev OTP code ثابت (12345) | ⚠️ | Low اگر فقط در dev باشد |

### Logging Security

❌ **هیچ logging strategy تعریف نشده.** موارد زیر باید log شوند:
- تلاش‌های ناموفق OTP
- تغییر role کاربر
- حذف entity ها
- payment transactions
- دسترسی‌های رد شده

---

## ۸. تحلیل مقیاس‌پذیری

### با ۱۰,۰۰۰ کاربر — مشکل جدی نیست

معماری فعلی این تعداد را handle می‌کند با:
- یک MySQL server
- یک Redis instance
- یک PHP-FPM instance

### با ۱۰۰,۰۰۰ کاربر — مشکلات شروع می‌شوند

| مشکل | علت | راه‌حل |
|------|-----|---------|
| لیست دکتر با فیلتر کند می‌شود | Full scan روی JSON columns | Read replica + Elasticsearch برای جستجو |
| Categories هر بار از DB | بدون cache | Redis cache با TTL=300s |
| SMS در صف می‌ماند | Sync call | Symfony Messenger + Queue |
| JWT validation سنگین | هر request decode می‌شود | Redis token blacklist |

### با ۱,۰۰۰,۰۰۰ کاربر — نیاز به Refactoring اساسی

| سرویس | مشکل | راه‌حل |
|-------|------|---------|
| Appointments | Hot table — write contention | Sharding بر اساس doctor_id |
| Search | MySQL full-text کافی نیست | Elasticsearch |
| File Upload | Local filesystem | S3-compatible object storage |
| SMS | Single provider | Multi-provider با queue |
| Auth | Stateless JWT کافی است | Redis session store برای blacklist |

**Microservice یا Modular Monolith؟**

در این مرحله: **Modular Monolith** توصیه می‌شود.

دلایل:
- تیم کوچک
- Domain boundaries هنوز در حال تثبیت
- Microservice overhead (distributed tracing، service mesh، network latency) در این مرحله ارزشش را ندارد

آینده (بعد از ۱۰۰K): **Financial Context** (Payment) و **Notification Context** (SMS) کاندیداهای اول برای جداسازی هستند.

---

## ۹. تحلیل ساختار پروژه

### ساختار پیشنهادی فعلی (نامشخص)

هیچ‌جا ساختار پوشه صریح تعریف نشده. احتمال پیش‌فرض Symfony:
```
src/
  Controller/
  Entity/
  Repository/
  Service/
```
این ساختار Layer-based است و برای ۱۷ ماژول با ۹۵ endpoint به سرعت به هم می‌ریزد.

### ساختار توصیه‌شده — Domain-Driven

```
src/
  Doctor/
    Controller/
      DoctorController.php
    Entity/
      Doctor.php
      DoctorAddress.php
    Repository/
      DoctorRepository.php
    Service/
      DoctorService.php
      DoctorRatingService.php
    DTO/
      DoctorRequest.php
      DoctorResponse.php
    Event/
      DoctorCreatedEvent.php

  Clinic/
    Controller/
    Entity/
    Repository/
    Service/
    DTO/

  Appointment/
    ...

  Payment/
    Gateway/
      MellatGateway.php
      SepGateway.php
      PaymentGatewayInterface.php
    ...

  Shared/
    Response/
      ApiResponse.php
      ApiError.php
    Controller/
      BaseController.php
    Repository/
      BaseRepository.php
```

### Dependency Direction

باید یک‌طرفه باشد:
```
Controller → Service → Repository → Entity
```

هیچ‌گاه:
```
Entity → Service  ❌
Repository → Controller  ❌
```

---

## ۱۰. ریسک‌های شناسایی‌شده

### ریسک‌های بحرانی

| ریسک | Severity | Probability | Impact | راه‌حل |
|------|----------|-------------|--------|--------|
| ۳۰+ endpoint بدون response schema | Critical | High | Frontend/Backend diverge | مستندسازی قبل از کدنویسی |
| Business logic rating تعریف‌نشده | Critical | High | نتایج اشتباه | مستندسازی فرمول |
| Payment flow ناقص | Critical | High | از دست رفتن پرداخت | تعریف کامل فلو |
| ساختار پوشه تعریف‌نشده | High | High | کد ناهماهنگ ۱۷ ماژول | تعریف قبل از شروع |

### ریسک‌های مهم

| ریسک | Severity | Probability | Impact | راه‌حل |
|------|----------|-------------|--------|--------|
| بدون Refresh Token | High | Certain | UX ضعیف، re-login مکرر | پیاده‌سازی refresh token |
| SMS sync blocking | High | High | OTP fail → کاربر مسدود | Symfony Messenger |
| N+1 Query در لیست دکتر | High | High | کندی با رشد data | Eager loading + cache |
| God Table categories | High | Medium | جستجوی کند، maintenance سخت | Refactor در فاز اول |
| Secretary permissions بدون schema | High | High | پیاده‌سازی ناهماهنگ | تعریف JSON schema |

### ریسک‌های عملیاتی

| ریسک | Severity | Probability | Impact | راه‌حل |
|------|----------|-------------|--------|--------|
| بدون health check | Medium | Certain | نمی‌توان مشکل را سریع تشخیص داد | اضافه کردن `/health` |
| بدون logging | High | Certain | debug تولید غیرممکن | Structured logging از ابتدا |
| Secrets در .env | Medium | High | leak در git | Vault یا CI/CD secrets |
| Dev OTP ثابت (12345) | Low | High | باید env-based باشد | `APP_ENV=dev` conditional |

---

## Missing Requirements (کامل)

| # | مورد | ماژول | شدت |
|---|------|-------|-----|
| ۱ | Response schema برای ۳۰+ endpoint | همه | Critical |
| ۲ | فرمول وزنی محاسبه rating | task-12 | Critical |
| ۳ | فلوی کامل payment (trigger، failure، refund، cancel) | task-15 | Critical |
| ۴ | Error response format استاندارد | task-01 | Critical |
| ۵ | ساختار پوشه Domain-Driven | task-01 | High |
| ۶ | منطق تولید `free_turn` و `hours_of_work` | task-05/09 | High |
| ۷ | Refresh Token mechanism | task-02 | High |
| ۸ | Subscription payment endpoints | task-15 | High |
| ۹ | ساختار JSON فیلد `permissions` دبیران | task-14 | High |
| ۱۰ | Appointment cancellation و refund flow | task-10/15 | High |
| ۱۱ | Logging strategy | task-01 | High |
| ۱۲ | Health check endpoint (`/health`) | task-01 | Medium |
| ۱۳ | SMS provider fallback logic | task-17 | Medium |
| ۱۴ | File upload error handling و size limit | همه | Medium |
| ۱۵ | City-specific management endpoints | task-08 | Medium |
| ۱۶ | Comment reply/nesting (field_parent) | task-12 | Medium |
| ۱۷ | Fixtures/Seeders برای category data | task-08 | Medium |
| ۱۸ | Rate limit headers در response | task-02 | Low |

---

## Architecture Violations

| # | نقض | اصل | راه‌حل |
|---|-----|-----|--------|
| ۱ | God Table `categories` | Single Responsibility | STI یا جداول جداگانه |
| ۲ | God Table `clinic_pro` از Drupal | Bounded Context | Entity های جداگانه در Symfony |
| ۳ | بدون DTO برای Input/Output | Explicit API Contract | DTO class برای هر endpoint |
| ۴ | Magic field name `field_starts` (نه `field_stars`) | Clarity | Mapping layer صریح |
| ۵ | Naming convention ناسازگار در response ها | Convention over Configuration | استاندارد یکپارچه |
| ۶ | Business logic تعریف‌نشده | Separation of Concerns | Service layer صریح |
| ۷ | Typo در URL (`categorys`) کپی از Drupal | REST conventions | در Symfony با redirect fix کن |
| ۸ | Timestamp گاهی string گاهی int در response | Type Consistency | همیشه int |

---

## Recommended Improvements

### اولویت ۱ — قبل از شروع کدنویسی

**۱. استاندارد Error/Success Response**
```json
// موفق:
{
  "success": true,
  "data": { ... },
  "meta": { "page": 1, "totalPages": 5, "totalRecords": 47 }
}

// خطا:
{
  "success": false,
  "data": null,
  "errors": [
    { "code": "ERR_VALIDATION_001", "field": "mobile_number", "message": "فرمت نادرست است" }
  ]
}
```

**۲. Error Codes استاندارد**
```
ERR_AUTH_001 = توکن منقضی شده
ERR_AUTH_002 = OTP نامعتبر
ERR_AUTH_003 = OTP منقضی شده
ERR_VALIDATION_001 = ورودی نامعتبر
ERR_NOT_FOUND_001 = منبع یافت نشد
ERR_FORBIDDEN_001 = دسترسی ندارید
ERR_PAYMENT_001 = درگاه پرداخت در دسترس نیست
ERR_PAYMENT_002 = مبلغ نامعتبر
```

**۳. تعریف فرمول Rating**
```php
const RATING_WEIGHTS = [
    'accuracy_of_diagnosis'  => 3.0,
    'doctor_expertise'       => 2.0,
    'doctor_behavior'        => 1.5,
    'waiting_time_at_clinic' => 1.0,
    'clinic_cleanliness'     => 1.0,
];
// weightedAverage = SUM(value * weight) / SUM(weights)  → از 100
// stars = (weightedAverage / 100) * 5                   → از 5
```

**۴. تعریف ساختار پوشه صریح در task-01**

### اولویت ۲ — در حین پیاده‌سازی

**۵. Refresh Token**
```
POST /oauth/token
{ "grant_type": "refresh_token", "refresh_token": "..." }
→ refresh token در Redis با TTL=30 روز
```

**۶. Symfony Messenger برای Async Operations**
```bash
ddev composer require symfony/messenger
```
SMS، notification، email — همه از طریق Queue

**۷. Repository Pattern صریح**
```php
// هر entity باید Repository خودش داشته باشد
class DoctorRepository extends ServiceEntityRepository
{
    public function findWithFilters(array $filters, int $page, int $limit): array
    public function findByUuidWithRelations(string $uuid): ?Doctor
}
```

**۸. DTO برای Input/Output**
```php
class DoctorRequest
{
    #[Assert\NotBlank]
    public string $title;
    
    #[Assert\Range(min: 0, max: 100)]
    public int $experience;
}

class DoctorResponse
{
    public function __construct(Doctor $doctor) { ... }
    public function toArray(): array { ... }
}
```

### اولویت ۳ — برای آماده‌سازی تولید

**۹. Health Check**
```
GET /health
→ { "status": "ok", "db": "ok", "redis": "ok", "timestamp": 1748000000 }
```

**۱۰. Redis Cache برای Lookup Data**
```php
// categories، states، cities — TTL=300s
$states = $cache->get('categories.state', fn() => $repo->findByBundle('state'));
```

**۱۱. Structured Logging**
```php
$this->logger->info('appointment.created', [
    'user_id' => $user->getId(),
    'doctor_id' => $doctor->getId(),
    'start_time' => $startTime,
    'request_id' => $requestId,
]);
```

**۱۲. Query Optimization**
```php
// Eager loading برای جلوگیری از N+1
$doctor = $repo->createQueryBuilder('d')
    ->leftJoin('d.specialties', 's')->addSelect('s')
    ->leftJoin('d.addresses', 'a')->addSelect('a')
    ->where('d.uuid = :uuid')
    ->getQuery()->getOneOrNullResult();
```

---

## Refactoring Plan

### فاز ۰ — مستندسازی (۳ تا ۵ روز، قبل از هر کدنویسی)

- [ ] تعریف `ApiResponse` و `ApiError` format در task-01
- [ ] مستندسازی Response schema تمام ۳۰+ endpoint گم‌شده
- [ ] تعریف فرمول rating در task-12
- [ ] تعریف کامل فلوی payment (trigger، failure، refund) در task-15
- [ ] تعریف ساختار JSON `permissions` دبیران در task-14
- [ ] تعریف ساختار پوشه Domain-Driven در task-01
- [ ] تصمیم‌گیری: Refresh Token — بله یا خیر

### فاز ۱ — زیرساخت پایه (task-01)

- [ ] اضافه کردن `symfony/messenger` به پکیج‌ها
- [ ] ایجاد `BaseController` با متدهای `success()` و `error()`
- [ ] ایجاد `BaseRepository` با متدهای مشترک
- [ ] اضافه کردن `GET /health` endpoint
- [ ] تعریف Error Code constants
- [ ] راه‌اندازی Structured Logging با Monolog

### فاز ۲ — پیاده‌سازی ماژول‌ها (به ترتیب dependency)

```
01 → 02 → 08 → 03 → 04 → 05 → 06 → 07 → 09 → 11 → 10 → 12 → 13 → 14 → 15 → 16 → 17
```

برای هر ماژول:
1. Entity + Migration
2. Repository با Eager loading
3. DTO (Request + Response)
4. Service با Business Logic
5. Controller با Swagger attributes
6. Tests

### فاز ۳ — بهینه‌سازی (بعد از پیاده‌سازی)

- [ ] Redis cache برای categories و lookup data
- [ ] Eager loading در تمام list endpoints
- [ ] Integration tests برای payment flow
- [ ] Load test برای لیست دکتر با فیلتر چندگانه
- [ ] Review و تکمیل Swagger documentation

### فاز ۴ — آماده‌سازی تولید

- [ ] Secrets به محیط CI/CD منتقل شوند (خارج از .env)
- [ ] Health check به monitoring متصل شود
- [ ] Read replica برای query های سنگین
- [ ] File storage به S3-compatible منتقل شود

---

## اصلاحات اعمال‌شده (بعد از Audit)

| # | مشکل | فایل اصلاح‌شده | وضعیت |
|---|------|---------------|--------|
| ۱ | ساختار پوشه Domain-Driven | task-01/task.md | ✅ |
| ۲ | Error Codes استاندارد | task-01/task.md | ✅ |
| ۳ | BaseController با success/error | task-01/task.md | ✅ |
| ۴ | Symfony Messenger | task-01/task.md | ✅ |
| ۵ | Health Check endpoint | task-01/task.md | ✅ |
| ۶ | Structured Logging | task-01/task.md | ✅ |
| ۷ | **CORS فقط دامنه‌های مشخص (نه *)** | task-01/implementation_notes.md | ✅ |
| ۸ | **Security Headers (X-Frame، HSTS، CSP)** | task-01/implementation_notes.md | ✅ |
| ۹ | **Swagger فقط در dev** | task-01/implementation_notes.md | ✅ |
| ۱۰ | **Audit Log جدول security_logs** | task-01/implementation_notes.md | ✅ |
| ۱۱ | **security.yaml کامل با access_control** | task-01/implementation_notes.md | ✅ |
| ۱۲ | Refresh Token + Logout + Blacklist | task-02/task.md | ✅ |
| ۱۳ | **hash_equals() برای OTP — جلوگیری از Timing Attack** | task-02/task.md | ✅ |
| ۱۴ | **Refresh Token هش‌شده در Redis (نه plain text)** | task-02/task.md | ✅ |
| ۱۵ | Rate Limit Headers در response | task-02/task.md | ✅ |
| ۱۶ | Status Machine کامل نوبت | task-10/task.md | ✅ |
| ۱۷ | فلوی لغو + refund | task-10/task.md | ✅ |
| ۱۸ | **Open Redirect در frontend_address پرداخت** | task-15/task.md | ✅ |
| ۱۹ | **IP Whitelist Callback با implementation** | task-15/task.md | ✅ |
| ۲۰ | Circuit Breaker + Idempotency | task-15/task.md | ✅ |
| ۲۱ | Subscription Payment endpoints | task-15/task.md | ✅ |
| ۲۲ | Secretary permissions JSON schema | task-14/task.md | ✅ |
| ۲۳ | SMS Fallback + Async Messenger | task-17/task.md | ✅ |
| ۲۴ | **File upload: magic bytes بجای MIME header** | task-01/task.md | ✅ |
| ۲۵ | **Filename sanitization — path traversal** | task-01/task.md | ✅ |
| ۲۶ | فرمت Response ناسازگار در architecture.md | task-01/architecture.md | ✅ |
| ۲۷ | تسویه نماینده | task-18-settlement/ | ✅ |

---

## مشکلات امنیتی باقی‌مانده (نیاز به توجه در پیاده‌سازی)

| # | مشکل | اولویت | راه‌حل |
|---|------|--------|--------|
| ۱ | Input HTML sanitization در فیلدهای متنی (detail، caption) | High | استفاده از `htmlspecialchars()` یا `strip_tags()` در DTO |
| ۲ | Mass assignment در PATCH endpoints | Medium | فقط فیلدهای مجاز را از request map کن |
| ۳ | JWT passphrase پیش‌فرض ضعیف | High | در production حتماً با `openssl rand -hex 32` تغییر داده شود |
| ۴ | Dev OTP ثابت (12345) | Medium | حتماً فقط با `APP_ENV=dev` فعال شود — هرگز در production |
| ۵ | Health check اطلاعات سیستم را افشا می‌کند | Low | در production به IP های داخلی محدود شود |

---

## Final Verdict (بعد از اصلاحات)

```
✅  APPROVED
```

### دلیل تصمیم

تمام مشکلات بحرانی و اکثر مشکلات مهم رفع شده‌اند:

- **امنیت:** Timing Attack، CORS wildcard، Open Redirect، Security Headers، Refresh Token hashing، IP Whitelist، Audit Log — همه رفع شدند
- **معماری:** Domain-Driven structure، DTO pattern، Error Codes، Response format یکپارچه
- **قابلیت اطمینان:** Payment flow کامل، Circuit Breaker، Idempotency، SMS fallback
- **مستندسازی:** همه endpoint های بحرانی با schema کامل مستند شدند

باقیمانده موارد (Mass Assignment، Input sanitization) در لایه پیاده‌سازی با Symfony Validator و DTO به سادگی قابل رفع هستند.

---

*آخرین بروزرسانی: ۱۴۰۵/۰۳/۱۸ — بعد از اصلاح کامل*
