در توسعهی نرمافزارهای سازمانی، یکی از چالشهای تکراری نحوهی مدیریت واحد ذخیرهسازی دادههاست. روش سنتی معمولاً در Controller یا Service، عملیات مستقیم بر روی DbContext انجام میدهد، که با رشد سیستم، مشکلاتی در انسجام تراکنشی، تکرار کد و وابستگی به EF ایجاد میکند.
معماری مبتنی بر Repository Pattern راهکاری ارائه میدهد که ضمن جداسازی منطق داده از منطق کسبوکار، امکان کنترل بهتر تراکنشها و رفتارهای چندزبانه را فراهم میسازد.
بخش اول: نگاهی به معماری سنتی ذخیرهسازی
در مدل سنتی EF، کنترلر مستقیماً موجودیتها را به کانتکست اضافه و ذخیره میکند:
using var transaction = await _dbContext.Database.BeginTransactionAsync();
try
{
_dbContext.Categories.Add(category);
await _dbContext.SaveChangesAsync();
categoryCulture.CategoryId = category.Id;
_dbContext.CategoryCultures.Add(categoryCulture);
await _dbContext.SaveChangesAsync();
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
در نگاه سطحی، این کد «امن» است، اما هزینهی نگهداری و ریسک عملیاتی آن بالا است.
هر کنترلر خودش وارد بازی تراکنش میشود، منطق خطا در بیرون از لایه داده پخش میشود، و در نهایت دو عامل بزرگ پروژه را زمین میزنند
مشکلات اصلی این روش:
- فراخوانی مستقیم DbContext در Controller موجب افزایش وابستگی بین لایهها میشود.
- هر Controller باید تراکنش را بهصورت مستقل مدیریت کند، در نتیجه کدهای تکراری در پروژه ایجاد میشوند.
- هرگونه تغییر در ساختار داده نیازمند بازنویسی بخشهایی از منطق ذخیرهسازی است.
- در سیستمهای چندزبانه (مانند ساختارهای Category و CategoryCulture)، منطق کد بهشدت به مقادیر ثابت وابسته میشود.
- مدیریت همزمان خطا + rollback دستی که در پروژههای سنگین، ریسک خطای انسانی را بالا میبرد.
- کدهای پرنویز و نهچندان مقیاسپذیر؛ هر اضافه کردن موجودیت جدید، یک بلاک
try-catchجدید به همراه دارد.
بخش دوم: گذار به معماری مبتنی بر Repository
در این الگو، منطق ذخیره در یک لایهی مجزا قرار میگیرد که کل عملیات SaveChangesAsync()، کنترل حذف منطقی (IsDeleted) و پاکسازی رشتهها توسط DbContext مرکزی انجام میشود.
نمونهای از کد ذخیره در این ساختار:
await _categoryRepository.AddAsync(category, cancellationToken);
await _categoryCultureRepository.AddAsync(categoryCulture, cancellationToken);
مزایای فنی:
- تراکنشها بهصورت اتمیک در سطح Repository مدیریت میشوند. در واقع EF Core در سطح Context تراکنش ضمنی دارد؛ هر ریپازیتوری خودش عملیات را تا مرحله Commit مدیریت میکند.
- Controller تنها وظیفهٔ هماهنگی منطق کسبوکار را دارد، نه کنترل مستقیم تراکنشها.
- Unit Test بدون دیتابیس واقعی ممکن است — شاخص اصلی معماری تمیز در سطح Enterprise.
- ساخت همزمان
CategoryCultureبرای زبانها بدون نگرانی از تراکنش یا شناسههای وابسته. - افزودن یا حذف زبان (Culture) بدون نیاز به تغییر در منطق ذخیرهسازی انجام میگیرد. ( نکته : در معماری سنتی نیز امکان خواندن زبانها از دیتابیس وجود داشت، اما این عملیات بهطور مستقیم در لایهٔ کنترلر انجام میشد و سیستم به ساختار پایگاه داده وابسته بود. در معماری مبتنی بر Repository همان عملیات در سطح انتزاعیتر انجام میشود؛ تفاوت در جداسازی مسئولیتها و قابلیت توسعه است، نه در ماهیت ذخیرهسازی داده.)
و همهچیز در سکوت، بینیاز از بلاکهای try-catch تکراری انجام میشود.
اگر قرار باشد فقط یک خط معیار برای معماری تمیز باقی بماند، آن خط این است:
«در معماری خوب، کنترلرها فقط تصمیم میگیرند، ریپازیتوریها اجرا میکنند.»
بخش سوم: بهینهسازی در سطح تزریق وابستگی (Dependency Injection)
در پروژههای Enterprise بهتر است ریپازیتوریها با Reflection از اسمبلی داده ثبت شوند:
var repositoryAssembly = AppDomain.CurrentDomain.GetAssemblies()
.First(a => a.FullName.Contains("Phoenix.Data"));
builder.Services.AddRepositories(repositoryAssembly);
روش فوق تمام کلاسهای ریپازیتوری را بدون نیاز به ثبت تکی، در DI Container اضافه میکند. برای پایداری بیشتر میتوان از رویکرد زیر استفاده کرد تا وابستگی به نام اسمبلی حذف شود:
builder.Services.AddRepositories(typeof(CategoryRepository).Assembly);
نتیجهگیری
معماری مبتنی بر Repository، علاوه بر کاهش پیچیدگی کنترلرها و حذف تراکنشهای دستی، ساختاری را فراهم میکند که برای پروژههای چندزبانه کاملاً مقیاسپذیر است. این مدل، الگوی صنعتی مورد تأیید مایکروسافت برای لایههای دادهی Enterprise به شمار میآید و پیادهسازی آن تضمین میکند که رشد پروژه منجر به افزایش شفافیت و قابلیت نگهداری کد گردد.