Files
clinicpro/docs/Architecture_Audit.md
hamed de1a78a235 feat: Implement SMS sending functionality with KavehNegar and Rangineh providers
- Add SendSmsMessage class for encapsulating SMS message data.
- Create KavehNegarProvider and RanginehProvider classes implementing SmsProviderInterface for sending SMS.
- Implement SmsLogRepository and SmsTemplateRepository for managing SMS logs and templates.
- Develop SendSmsHandler for handling SMS sending messages.
- Create SmsService to manage SMS dispatching and logging.
- Add UserProfileController for managing user profiles with CRUD operations.
- Implement UserProfile entity and repository for user profile data management.
- Update symfony.lock and bootstrap.php for project dependencies and environment setup.
2026-06-09 22:00:34 +03:30

33 KiB
Raw Permalink Blame History

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

-- یک جدول برای ۷ نوع کاملاً متفاوت:
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

-- 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 نشود

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

-- اضافه کردن این ایندکس‌ها توصیه می‌شود:
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

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

۱. استاندارد Error/Success Response

// موفق:
{
  "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

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

ddev composer require symfony/messenger

SMS، notification، email — همه از طریق Queue

۷. Repository Pattern صریح

// هر 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

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

// categories، states، cities — TTL=300s
$states = $cache->get('categories.state', fn() => $repo->findByBundle('state'));

۱۱. Structured Logging

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

۱۲. Query Optimization

// 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 به سادگی قابل رفع هستند.


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