یک API فروشگاهی کامل با ASP.NET Core 9 و معماری Clean Architecture که مسیر یک خرید واقعی را پوشش میدهد: ثبتنام و احراز هویت، مرور محصولات، سبد خرید، تخفیف و کد تخفیف، Checkout با Idempotency Key، مدیریت موجودی انبار، سفارش و پرداخت با درگاههای Sandbox (سامان و زرینپال).
همراه با زیرساخت قابل مشاهدهپذیری (Observability) کامل شامل OpenTelemetry، Prometheus، Grafana، Loki و Jaeger که همه با یک دستور docker compose up بالا میآیند.
این پروژه برای یادگیری و تمرین معماری چندلایه و الگوهای واقعی تولیدی (Production-grade) مناسب است.
- سیستم Seed ماژولار با ۱۱ زیرسیدر مستقل:
Category،Brand،Product،Discount،Coupon،User،Review،Cart،WishlistوOrder - دادهها از فایلهای JSON در
Infrastructure/Persistence/Seed/Data/*.jsonخوانده میشوند - کاملاً Idempotent: هر زیرسیدر قبل از Insert، وجود رکورد را با کلید طبیعی (Title / Email / SKU / Code و...) بررسی میکند و در صورت وجود، از آن صرفنظر میکند؛ اجرای مکرر کاملاً بیخطر است
- اجرای کل Seed درون یک Transaction انجام میشود؛ در صورت بروز خطا Rollback کامل انجام میگیرد
- کنترل از طریق تنظیمات
Seed:EnabledوSeed:Force(فقط در محیط Development) و اجرای خودکار Migration هنگام استارت برنامه
- Optimistic Concurrency با
RowVersion(SQL Server rowversion) روی entityهای حساس:Product،InventoryItemوRefreshToken - مدیریت
DbUpdateConcurrencyExceptionدر سرویسهای حساس (Product، Checkout، Inventory و Auth) تا از گم شدن بهروزرسانیها در درخواستهای همزمان جلوگیری شود
محدودسازی درخواست با Rate Limiter داخلی .NET، بهصورت سراسری + چند پالیسی اختصاصی بر اساس پنجره ثابت (Fixed Window):
| پالیسی | محدودیت | پارتیشنبندی |
|---|---|---|
| Global | ۱۰۰ درخواست در دقیقه | بر اساس IP |
Auth |
۵ درخواست در دقیقه | بر اساس IP |
RefreshToken |
۱۰ درخواست در دقیقه | بر اساس IP |
Sensitive |
۱۰ درخواست در دقیقه | بر اساس UserId / IP |
Search |
۶۰ درخواست در دقیقه | بر اساس IP |
Write |
۳۰ درخواست در دقیقه | بر اساس UserId / IP |
در صورت عبور از حد مجاز، پاسخ 429 Too Many Requests با پیام فارسی و هدر Retry-After برگردانده میشود.
- اعتبارسنجی خودکار روی ورودیهای همه کنترلرها (
AutomaticValidationEnabled) - Validatorهای مجزا برای هر Feature و هر عملیات (Create/Edit و...)
- پاسخ خطای سفارشی و یکپارچه برای خطاهای Model State
- مدیریت متمرکز خطاها با
IExceptionHandlerوProblemDetails - نگاشت خودکار Exceptionهای دامنه به Status Code مناسب:
NotFoundException→404ConflictExceptionوInsufficientStockException→409ForbiddenAccessException→403BusinessException،CartEmptyException،InvalidQuantityException→400
- لاگگیری کامل همراه با
TraceId،UserId،MethodوPathبرای کورلیشن راحتتر در Grafana/Loki
- کش Redis: لایه کش اختصاصی با
RedisCacheServiceوCacheKeyBuilderبرای لیست، جستجو و جزئیات محصولات + باطلسازی کش با Pattern بعد از هر تغییر - Idempotency Key: ذخیره کلید idempotency برای عملیات Checkout تا پرداخت تکراری در اثر Retry کلاینت اتفاق نیفتد
- احراز هویت JWT: Access Token + Refresh Token (با انقضا و RowVersion)، هش رمز عبور، فراموشی/بازیابی رمز با ارسال ایمیل SMTP و قالب HTML
- درگاههای پرداخت: سامان و زرینپال (Sandbox) با الگوی Strategy و
PaymentGatewayResolver - Health Checks: سه endpoint ی
/health،/health/liveو/health/ready(شامل چک اتصال دیتابیس) - Soft Delete برای محصولات و ثبت تاریخچه
InventoryTransactionبرای موجودی انبار - Swagger / OpenAPI با پشتیبانی کامل از احراز هویت Bearer (دکمه Authorize)
| دسته | تکنولوژی | نسخه |
|---|---|---|
| زبان و پلتفرم | C# 13 / .NET 9 | 9.0 |
| فریمورک | ASP.NET Core Web API | 9.0.17 |
| ORM | Entity Framework Core + SQL Server | 9.0.17 |
| کش | Redis (StackExchange.Redis) | 3.1.31 |
| اعتبارسنجی | FluentValidation | 11.3.1 |
| لاگگیری | Serilog (Console / File / Grafana Loki) | 4.4.0 |
| مستندات | Swagger / Swashbuckle | 9.0.6 |
| Observability | OpenTelemetry | 1.18.0 |
| Metrics | Prometheus | latest |
| Distributed Tracing | Jaeger (OTLP) | latest |
| داشبورد و لاگ | Grafana + Loki | latest |
| کانتینر | Docker Compose | — |
ShopAPI/
├── API/ # نقطه ورود، کنترلرها، Middlewareها، Rate Limiter، Health Checks و تنظیمات
├── Application/ # سرویسها، DTOها، Validatorها، قراردادها و لایه Caching
├── Domain/ # Entityها، Enumها و Exceptionهای دامنه
├── Infrastructure/ # EF Core، Migrations، Repositoryها، Seeder، JWT، Hashing، Email و Payment Providerها
├── Shared/ # کلاسهای مشترک (Exceptionها و...)
└── docker-compose.yml
جریان وابستگیها به سمت Domain است: API → Application → Domain و Infrastructure → Application/Domain؛ قراردادها (Interfaceها) در Application تعریف و در Infrastructure پیادهسازی میشوند.
- .NET SDK 9
- SQL Server (یا SQL Server Express / LocalDB)
- Docker Desktop (برای Redis و پشته Observability)
git clone https://github.com/NimaHaji/ShopAPI.git
cd ShopAPIdocker compose up -dاین دستور این سرویسها را بالا میآورد:
| سرویس | آدرس |
|---|---|
| Redis | localhost:63799 |
| Jaeger UI | http://localhost:16686 |
| Prometheus | http://localhost:9090 |
| Loki | http://localhost:31000 |
| Grafana | http://localhost:30000 (admin/admin) |
cp API/appsettings.example.json API/appsettings.Development.jsonسپس این مقادیر را در API/appsettings.Development.json مطابق سیستم خودتان تغییر دهید:
ConnectionStrings:local— رشته اتصال SQL ServerConnectionStrings:Redis— آدرس Redis (پیشفرضlocalhost:6379، با docker-compose بالاlocalhost:63799)JwtSettings:SecretKey— یک کلید طولانی و تصادفی- تنظیمات Sandbox پرداخت در بخش
Payment Seed:Enabled— فعال/غیرفعال کردن دادههای اولیهOtlp:Endpoint— آدرس Jaeger (پیشفرضhttp://localhost:4317)
فایلهای حاوی secret نباید commit شوند؛
appsettings.example.jsonفقط بهعنوان نمونه در گیت نگه داشته میشود.
dotnet run --project API- Swagger:
http://localhost:4075/swagger - Metrics:
http://localhost:4075/metrics - Health:
http://localhost:4075/health
هنگام استارت برنامه، Migrationها بهصورت خودکار اجرا و در صورت فعال بودن
Seed:Enabled، دادههای نمونه (محصولات، برندها، دستهبندیها، کاربران و...) بهصورت Idempotent درج میشوند.
- پروژه و سرویسهای Docker را اجرا کنید.
- وارد Swagger شوید و با
POST /api/Users/RegisterوPOST /api/Users/loginتوکن بگیرید. - در Swagger روی دکمه Authorize کلیک کنید و مقدار زیر را وارد کنید:
Bearer YOUR_ACCESS_TOKEN
مسیرهای اصلی برای تست سناریوی خرید:
GET /api/Products # لیست محصولات (کششده در Redis)
GET /api/Products/Search # جستجو
POST /api/Cart/items # افزودن به سبد خرید
POST /api/Checkouts/checkout # ثبت سفارش (با Idempotency Key)
POST /api/Payments/GetPaymentUrl# دریافت لینک پرداخت (سامان / زرینپال)
POST /api/Coupons/... # کد تخفیف
پس از اجرای پروژه و docker compose up:
- متریکها: endpoint
/metricsتوسط Prometheus هر ۵ ثانیه scrape میشود (تنظیمات درprometheus.yml) - تریسها: Distributed Tracing با OpenTelemetry (ASP.NET Core، EF Core، HttpClient) از طریق OTLP به Jaeger ارسال و در
http://localhost:16686قابل مشاهده است - لاگها: Serilog بهصورت همزمان در Console، فایلهای رولینگ (
API/Logs/) و Grafana Loki مینویسد - داشبورد: Grafana در
http://localhost:30000برای ساخت داشبورد متریک و لاگ (با کورلیشن TraceId)
این پروژه آموزشی و در حال توسعه است. تستهای خودکار هنوز اضافه نشدهاند و ممکن است edge caseهایی پوشش داده نشده باشند. اگر قصد استفاده جدی دارید، تنظیمات امنیتی و HTTPS را کاملتر کنید.
اگر این پروژه برایتان مفید بود، میتوانید آن را Fork کنید و روی بخشهایی مثل تستنویسی، الگوهای CQRS، تولید کد یا بهبود Observability تمرین کنید. از PR ها با حفظ معماری استقبال می شود .