ساختار کامل لایههای بکاند در معماری پیازی
معماری پیازی یا Onion Architecture یکی از الگوهای محبوب طراحی نرمافزار برای ساخت سامانههای بزرگ، قابل نگهداری و قابل توسعه است. هدف اصلی این معماری آن است که منطق اصلی کسبوکار از دیتابیس، رابط کاربری، سرویسهای خارجی و فناوریهای زیرساختی مستقل باقی بماند.
در این معماری، لایه Domain در مرکز سیستم قرار میگیرد و سایر لایهها مانند حلقههای یک پیاز در اطراف آن شکل میگیرند. جهت وابستگیها همیشه باید از لایههای بیرونی به سمت لایههای داخلی باشد.
معماری پیازی چیست؟
در بسیاری از پروژههای سنتی، منطق کسبوکار مستقیماً به دیتابیس، فریمورک، API یا کتابخانههای جانبی وابسته میشود. این وابستگی شدید باعث میشود تغییر دیتابیس، تعویض سرویس پیامک، تغییر سیستم احراز هویت یا حتی ارتقای فریمورک به کاری دشوار و پرریسک تبدیل شود.
معماری پیازی این مشکل را با قرار دادن قوانین اصلی سیستم در مرکز معماری حل میکند. لایههای بیرونی میتوانند از لایههای داخلی استفاده کنند، اما لایههای داخلی نباید هیچ شناخت مستقیمی از جزئیات لایههای بیرونی داشته باشند.
قانون اساسی معماری پیازی: همه وابستگیها باید به سمت داخل باشند. لایه Domain نباید به API، دیتابیس، Entity Framework، Redis، RabbitMQ یا سرویسهای خارجی وابسته شود.
لایههای اصلی معماری پیازی
یک پروژه بکاند استاندارد در معماری پیازی حداقل از چهار لایه اصلی تشکیل میشود:
- لایه دامنه یا Domain
- لایه کاربرد یا Application
- لایه زیرساخت یا Infrastructure
- لایه ارائه یا Presentation / API
۱. لایه Domain؛ هسته اصلی سیستم
وظیفه لایه Domain چیست؟
لایه Domain داخلیترین بخش سیستم است. موجودیتها، قوانین اصلی کسبوکار، رفتارهای دامنه، رویدادهای دامنه و اشیای مقداری در این لایه قرار میگیرند.
این لایه باید تا حد ممکن مستقل باشد و معمولاً نباید به پکیجهای مربوط به دیتابیس، API، کش یا سرویسهای خارجی وابستگی داشته باشد.
اجزای مهم لایه Domain
- Entities: موجودیتهای اصلی مانند کاربر، محصول، سفارش و پرداخت
- Aggregate Roots: ریشههایی که کنترل یک مجموعه از موجودیتهای مرتبط را بر عهده دارند
- Value Objects: اشیایی بدون هویت مستقل مانند آدرس، شماره تلفن و مبلغ
- Domain Events: رویدادهایی که وقوع یک اتفاق مهم در دامنه را اعلام میکنند
- Business Rules: قوانین اساسی و محدودیتهای کسبوکار
- Domain Services: منطق دامنهای که به یک Entity خاص تعلق ندارد
- Domain Exceptions: خطاهای مربوط به نقض قوانین دامنه
- Specifications: قوانین قابل ترکیب برای فیلتر و اعتبارسنجی
- Enums: مقادیر ثابت دامنه مانند وضعیت سفارش
- Constants: ثابتهای مرتبط با قوانین دامنه
ساختار پیشنهادی پروژه Domain
MyProject.Domain
├── Common
│ ├── BaseEntity.cs
│ ├── BaseAuditableEntity.cs
│ ├── BaseAggregateRoot.cs
│ └── ValueObject.cs
├── Entities
├── AggregateRoots
├── ValueObjects
├── Enums
├── Constants
├── Events
├── Exceptions
├── Rules
├── Services
├── Specifications
└── Interfaces
۲. لایه Application؛ مدیریت سناریوهای سیستم
نقش لایه Application
لایه Application مشخص میکند سیستم چه قابلیتها و عملیاتهایی دارد. برای مثال ایجاد محصول، ثبت سفارش، لغو سفارش، دریافت فهرست کاربران یا ورود به سامانه از جمله سناریوهای این لایه هستند.
این لایه عملیات را هماهنگ میکند، اما نباید جزئیات فنی مانند نحوه اتصال به SQL Server، ارسال پیامک یا ذخیره فایل را پیادهسازی کند. برای این کارها فقط Interface تعریف میشود و پیادهسازی آنها در لایه Infrastructure قرار میگیرد.
اجزای مهم لایه Application
- Use Caseها و سناریوهای کاربردی
- Commandها برای عملیات تغییردهنده اطلاعات
- Queryها برای دریافت اطلاعات
- Handlerهای مربوط به Command و Query
- DTOها و مدلهای انتقال اطلاعات
- Validatorها برای اعتبارسنجی درخواستها
- Mappingها برای تبدیل Entity و DTO
- Interface سرویسها و Repositoryها
- Pipeline Behaviorها
- مدیریت Transactionها در سطح کاربرد
- Exceptionهای کاربردی
- مدلهای صفحهبندی و نتیجه عملیات
ساختار Feature-Based در لایه Application
MyProject.Application
├── Common
│ ├── Behaviors
│ ├── Exceptions
│ ├── Interfaces
│ ├── Mappings
│ ├── Models
│ └── Results
├── Features
│ ├── Products
│ │ ├── Commands
│ │ │ ├── CreateProduct
│ │ │ ├── UpdateProduct
│ │ │ └── DeleteProduct
│ │ ├── Queries
│ │ │ ├── GetProduct
│ │ │ └── GetProducts
│ │ ├── DTOs
│ │ └── Validators
│ ├── Orders
│ ├── Users
│ └── Authentication
└── DependencyInjection.cs
۳. لایه Infrastructure؛ پیادهسازی جزئیات فنی
وظیفه Infrastructure
لایه Infrastructure مسئول پیادهسازی جزئیات فنی و ارتباط با فناوریهای بیرونی است. هرجا Application یک Interface تعریف کرده باشد، Infrastructure میتواند پیادهسازی واقعی آن را ارائه کند.
برای مثال Application فقط اعلام میکند که به سرویسی برای ارسال پیامک نیاز دارد، اما اینکه پیامک از طریق کدام شرکت، API یا پروتکل ارسال شود، در Infrastructure مشخص میشود.

موارد قرارگرفته در Infrastructure
- ارسال ایمیل و پیامک
- ارتباط با درگاههای بانکی
- ذخیره فایل و فضای ابری
- پیادهسازی Redis و Memory Cache
- ارتباط با RabbitMQ، Kafka یا MassTransit
- سرویسهای رمزنگاری و Hashing
- ثبت Log و Monitoring
- پیادهسازی HttpClientها
- ارتباط با APIهای خارجی
- Background Jobها
- پیادهسازی سرویس تاریخ و زمان
- تولید Token و سرویسهای امنیتی
ساختار پیشنهادی Infrastructure
MyProject.Infrastructure
├── Authentication
├── Authorization
├── BackgroundJobs
├── Cache
│ ├── Memory
│ └── Redis
├── Email
├── Encryption
├── ExternalServices
├── FileStorage
├── HttpClients
├── Logging
├── Messaging
├── Notifications
├── PaymentGateways
├── Security
├── SMS
└── DependencyInjection.cs
۴. لایه Presentation یا API
نقش لایه ارائه
لایه Presentation نقطه ورود کاربران و برنامههای دیگر به سیستم است. در یک پروژه ASP.NET Core Web API، این لایه همان پروژه API خواهد بود.
این لایه درخواست HTTP را دریافت میکند، آن را به یک Command یا Query تبدیل میکند و نتیجه را در قالب پاسخ استاندارد به کلاینت برمیگرداند. منطق اصلی کسبوکار نباید داخل Controllerها نوشته شود.
اجزای لایه API
- Controllerها یا Minimal API Endpointها
- Middlewareها
- Exception Handler سراسری
- فیلترها و Attributeها
- Swagger و OpenAPI
- API Versioning
- Authentication و Authorization
- CORS
- Rate Limiting
- Health Checkها
- SignalR Hubها
- پیکربندی Dependency Injection
- فایلهای تنظیمات برنامه
ساختار پیشنهادی API
MyProject.Api
├── Controllers
├── Endpoints
├── Middleware
├── Filters
├── Attributes
├── Authentication
├── Authorization
├── ExceptionHandling
├── HealthChecks
├── OpenApi
├── Swagger
├── Versioning
├── Extensions
├── Configurations
├── appsettings.json
├── appsettings.Development.json
└── Program.cs
لایههای تکمیلی در پروژههای حرفهای
چهار لایه اصلی برای اجرای معماری پیازی کافی هستند، اما در پروژههای بزرگ میتوان مسئولیتها را به پروژههای مستقل بیشتری تقسیم کرد.
۵. لایه Persistence؛ دسترسی به دیتابیس
لایه Persistence مسئول ذخیره و بازیابی اطلاعات است. این لایه در پروژههای کوچک میتواند بخشی از Infrastructure باشد، اما در پروژههای Enterprise بهتر است جدا تعریف شود.
اجزای Persistence
- DbContext
- Entity Configurationها
- Repositoryها
- Unit of Work
- Migrationها
- Seed Data
- Database Interceptorها
- Transactionها
- Query Extensionها
- Value Converterها
MyProject.Persistence
├── Contexts
├── Configurations
├── Repositories
├── UnitOfWork
├── Migrations
├── Seeds
├── Interceptors
├── Converters
├── Transactions
├── QueryExtensions
└── DependencyInjection.cs
۶. لایه Identity؛ مدیریت هویت کاربران
در پروژههای بزرگ، احراز هویت و مدیریت کاربران میتواند در پروژهای مستقل به نام Identity قرار گیرد.
- ApplicationUser و ApplicationRole
- مدیریت JWT
- Refresh Token
- Claimها
- Roleها و Permissionها
- Policyهای دسترسی
- OTP
- احراز هویت دومرحلهای
- ورود با Google یا Microsoft
- بازیابی و تغییر رمز عبور
۷. لایه Contracts؛ قراردادهای ورودی و خروجی
پروژه Contracts مدلهای مربوط به ارتباط API با مصرفکنندگان را نگهداری میکند. با این روش، Entityهای Domain مستقیماً در پاسخ API نمایش داده نمیشوند.
- Request Modelها
- Response Modelها
- DTOها
- مدلهای صفحهبندی
- مدلهای فیلتر و جستجو
- قراردادهای احراز هویت
- قراردادهای مربوط به کاربران، محصولات و سفارشها
۸. لایه Integration؛ ارتباط با سامانههای خارجی
تمام ارتباطات مستقیم با سامانههای سازمانی یا سرویسهای شخص ثالث میتوانند در پروژه Integration پیادهسازی شوند.
- درگاههای پرداخت
- سرویسهای بانکی
- سامانههای دولتی
- نرمافزارهای CRM و ERP
- سرویس نقشه و موقعیت مکانی
- سرویسهای ارسال پیام
- APIهای شرکای تجاری
۹. لایه Messaging؛ پیامرسانی غیرهمزمان
در سامانههای بزرگ و توزیعشده، بخش Messaging برای مدیریت Eventها، Queueها و ارتباط غیرهمزمان بین سرویسها استفاده میشود.
- Integration Eventها
- Producerها و Publisherها
- Consumerها و Subscriberها
- RabbitMQ
- Kafka
- MassTransit
- Outbox Pattern
- Inbox Pattern
- Dead Letter Queue
۱۰. Shared Kernel و Common
کدهای پایه و عمومی که توسط چند لایه استفاده میشوند، میتوانند در پروژههای SharedKernel یا Common قرار گیرند.
موارد مناسب برای SharedKernel
- کلاس Result و Result<T>
- کلاس Error
- مدل PagedResult
- Base Exceptionها
- Guard Clauseها
- ثابتها و Enumهای واقعاً مشترک
- ابزارهای عمومی اعتبارسنجی
SharedKernel نباید به انباری برای همه کلاسهای نامشخص پروژه تبدیل شود. فقط مواردی را در این پروژه قرار دهید که واقعاً بین چند بخش سیستم مشترک هستند.
۱۱. Background Jobs و Worker
عملیات زمانبر، زمانبندیشده یا غیرهمزمان بهتر است در پروژه Worker اجرا شوند.
- ارسال گروهی پیامها
- پردازش صفها
- تولید گزارشهای سنگین
- پاکسازی اطلاعات منقضیشده
- اجرای Jobهای زمانبندیشده
- پردازش فایلها
- همگامسازی با سامانههای خارجی
۱۲. پروژههای Test
برای اینکه هر لایه بهصورت مستقل آزمایش شود، بهتر است پروژههای تست نیز مطابق ساختار اصلی Solution تقسیمبندی شوند.
- Domain.Tests: آزمایش قوانین دامنه
- Application.Tests: آزمایش Commandها، Queryها و Handlerها
- Infrastructure.Tests: آزمایش سرویسهای زیرساختی
- Persistence.Tests: آزمایش Repository و DbContext
- Api.Tests: آزمایش Endpointها و Middlewareها
- IntegrationTests: آزمایش یکپارچگی اجزای سیستم
- ArchitectureTests: کنترل قوانین وابستگی میان لایهها
ساختار کامل یک Solution حرفهای
MyProject
├── src
│ ├── Core
│ │ ├── MyProject.Domain
│ │ ├── MyProject.Application
│ │ ├── MyProject.Contracts
│ │ └── MyProject.SharedKernel
│ ├── Infrastructure
│ │ ├── MyProject.Infrastructure
│ │ ├── MyProject.Persistence
│ │ ├── MyProject.Identity
│ │ ├── MyProject.Integration
│ │ └── MyProject.Messaging
│ ├── Presentation
│ │ └── MyProject.Api
│ └── Workers
│ └── MyProject.Worker
├── tests
│ ├── MyProject.Domain.Tests
│ ├── MyProject.Application.Tests
│ ├── MyProject.Infrastructure.Tests
│ ├── MyProject.Persistence.Tests
│ ├── MyProject.Api.Tests
│ ├── MyProject.IntegrationTests
│ └── MyProject.ArchitectureTests
├── docs
├── scripts
├── docker
├── deploy
├── .github
├── docker-compose.yml
├── Directory.Build.props
├── Directory.Packages.props
└── MyProject.sln
جدول وظایف و وابستگی لایهها
| لایه |
وظیفه اصلی |
وابستگی مجاز |
| Domain |
مدلسازی کسبوکار و قوانین اصلی |
بدون وابستگی به لایههای بیرونی |
| Application |
اجرای سناریوها و Use Caseها |
Domain |
| Infrastructure |
پیادهسازی سرویسهای فنی |
Application و Domain |
| Persistence |
دسترسی به دیتابیس |
Application و Domain |
| Identity |
مدیریت کاربران و احراز هویت |
Application |
| Presentation / API |
دریافت درخواست و ارائه پاسخ |
Application و Contracts |
| Worker |
اجرای پردازشهای پسزمینه |
Application و Infrastructure |
| Tests |
کنترل صحت رفتار و معماری سیستم |
لایه مورد آزمایش |
قانون جهت وابستگیها
جهت اصلی وابستگی در معماری پیازی باید به شکل زیر باشد:
Presentation / API
↓
Application
↓
Domain
لایههای فنی نیز Interfaceهای Application را پیادهسازی میکنند:
Infrastructure ───────→ Application / Domain
Persistence ─────────→ Application / Domain
Identity ────────────→ Application
Integration ─────────→ Application
Messaging ───────────→ Application
مزایای استفاده از معماری پیازی
- جداسازی مسئولیتها: هر بخش وظیفه مشخص و محدودی دارد.
- کاهش وابستگی: منطق اصلی به ابزارها و فناوریهای بیرونی وابسته نیست.
- تستپذیری بالا: قوانین سیستم بدون دیتابیس و API قابل آزمایش هستند.
- قابلیت نگهداری: تغییر یک بخش، تأثیر کمتری بر سایر بخشها دارد.
- قابلیت توسعه: افزودن قابلیتهای جدید با ساختار مشخص انجام میشود.
- امکان تعویض فناوری: میتوان دیتابیس یا سرویس خارجی را با هزینه کمتری تغییر داد.
- مناسب برای تیمهای بزرگ: مرز مسئولیت تیمها و ماژولها روشنتر است.
- پشتیبانی از DDD و CQRS: این معماری با Domain-Driven Design و CQRS هماهنگی خوبی دارد.
اشتباهات رایج در پیادهسازی
- نوشتن منطق کسبوکار داخل Controller
- وابسته کردن Domain به Entity Framework Core
- استفاده مستقیم Application از DbContext
- قرار دادن همه کلاسها در پوشه Services
- ایجاد Repository عمومی بدون نیاز واقعی
- استفاده بیش از حد از DTOهای تکراری
- تبدیل SharedKernel به پوشهای نامنظم
- ایجاد تعداد زیادی پروژه برای یک سیستم کوچک
- وابسته کردن Application به Infrastructure
- برگرداندن مستقیم Entityهای Domain از API
برای پروژههای کوچک چند لایه کافی است؟
برای یک پروژه کوچک یا متوسط معمولاً چهار پروژه اصلی کافی است:
- MyProject.Domain
- MyProject.Application
- MyProject.Infrastructure
- MyProject.Api
در این حالت Persistence، Identity، Messaging و سایر بخشها میتوانند بهعنوان پوشههایی در Infrastructure قرار گیرند. با بزرگتر شدن پروژه، میتوان آنها را به پروژههای مستقل منتقل کرد.
ساختار پیشنهادی برای پروژه Enterprise
برای یک پروژه سازمانی بزرگ با ASP.NET Core، Entity Framework Core، CQRS، Redis، RabbitMQ و Background Worker، ساختار زیر گزینه مناسبی است:
- Domain
- Application
- Contracts
- SharedKernel
- Infrastructure
- Persistence
- Identity
- Integration
- Messaging
- API
- Worker
- Tests
جمعبندی نهایی
معماری پیازی فقط به معنای ساختن چند پروژه با نامهای مختلف نیست. هدف اصلی آن، حفاظت از منطق کسبوکار در برابر تغییرات فناوری و جلوگیری از وابستگیهای نامناسب است.
لایه Domain باید در مرکز سیستم باقی بماند، Application سناریوهای سیستم را مدیریت کند، Infrastructure و Persistence جزئیات فنی را پیادهسازی کنند و API تنها مسئول دریافت و ارسال اطلاعات باشد.
با رعایت صحیح قانون وابستگی، پروژهای به دست میآید که توسعه، آزمایش، نگهداری و تغییر آن در طول زمان بسیار سادهتر خواهد بود.