# 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 به سادگی قابل رفع هستند. --- *آخرین بروزرسانی: ۱۴۰۵/۰۳/۱۸ — بعد از اصلاح کامل*