تفاوت IEnumerable و IEnumerator
یادمه در یکی از مصاحبه هایی که داشتم مصاحبه کننده وارد مبحث Collection ها شد و ازم خواست تفاوت IEnumerable و IEnumerator رو توضیح بدم.
منم چون فقط اسمشونو شنیده بودم گفتم هردو اینترفیس های Collection هستن
و وقتی مصاحبه کننده گفت: خب؟! همین...؟؟!
تازه فهمیدم چقدر زیاد راجب این دو و تفاوت هاشون نمیدونم...
تفاوت IEnumerable و IEnumerator
برای اینکه موضوع برام جا بیفته، یک مثال واقعی کمکم کرد.
ببینیم IEnumerable چیه؟
فرض کن وارد یک کتابخانه میشیم.
اولین چیزی که می بینیم لیست موضوعات کتابهاست: رمان، فلسفه، تاریخی، علمی و…
اما هنوز به هیچ کتابی نزدیک نشدیم، چیزی ورق نزدیم و هیچکدومو انتخاب نکردیم.
درواقع IEnumerable دقیقاً همین کار رو میکنه:
فقط میگه این مجموعه قابل پیمایشه و میشه روش iterat کرد.
نه آیتم میاره، نه جلو و عقب میره، نه حرکت میکنه.
و IEnumerator چیه؟
حالا نوبت کتابداره! IEnumerator همون کتابداریه که:
حرکت میکنه
آیتم فعلی رو میاره
و حتی دوباره میتونه به اول لیست برگرده
و سه قابلیت اصلی و کلیدی داره:
حرکت به آیتم بعدی : ()MoveNext
آیتم فعلی : Current
برگشت به ابتدا : ()void Reset
یعنی تنها «عامل حرکت» در تمامی collection ها ، IEnumerator هست.
و حالا یه اتفاق جالب رخ میده وقتی ما روی یک لیست حلقه میزنیم.
وقتی مینویسیم:
foreach (var item in list)
کامپایلر اینو تبدیل میکنه به:
IEnumerator enumerator=list.GetEnumerator();
while (enumerator.MoveNext())
{
var item = enumerator.Current;
}
یعنی foreach دقیقا این کار رو انجام میده:
از IEnumerable → یک Enumerator جدید میگیره و باهاش پیمایش انجام میده.
خلاصه تفاوت های اصلی:
🔸️IEnumerable :
مشخص میکنه یک مجموعه قابل پیمایشه (مثل فهرست کتابخانه)
🔹️IEnumerator:
یک الگوریتم پیمایشه (مثل کتابداری که حرکت میکنه و کتاب میاره)
این موضوع شاید وقتیکه متوجه ش شدم ساده بهنظر رسید،
اما فهمیدن همین مفاهیم پایه و به ظاهر ساده ست که کیفیت کد و صاحب کد رو مشخص میکنه.
🔗Link
🚫 ءDbContext Thread-Safe نیست: اجرای موازی Queryهای EF Core به روش درست
ما همه یک بار آن endpoint را ساختهایم.
همان Endpoint معروفی که برای Executive Dashboard یا User Summary استفاده میشود. جایی که باید چندین مجموعه دادهٔ کاملاً نامربوط را بگیری تا یک تصویر کامل به کاربر نشان بدهی.
مثلاً:
• ۵۰ سفارش آخر
• لاگهای سلامت سیستم
• تنظیمات پروفایل کاربر
• تعداد اعلانها
و معمولاً کد را اینگونه مینویسیم:
var orders = await GetRecentOrdersAsync(userId);
var logs = await GetSystemLogsAsync();
var stats = await GetUserStatsAsync(userId);
return new DashboardDto(orders, logs, stats);
این کد کار میکند. تمیز است. خواناست. اما یک مشکل جدی وجود دارد. ⚠️
اگر:
🔹️GetRecentOrdersAsync = 300ms
🔹️GetSystemLogsAsync = 400ms
🔹️GetUserStatsAsync = 300ms
کاربر باید ۱ ثانیه کامل منتظر بماند (۳۰۰ + ۴۰۰ + ۳۰۰).
در سیستمهای توزیعشده، Latency تجربهٔ کاربری را نابود میکند.
و چون این دادهها از هم مستقل هستند، باید بتوانیم آنها را به صورت موازی اجرا کنیم.
اگر این کار را بکنیم، طول کل درخواست = کندترین Query (۴۰۰ms)
یعنی ۶۰٪ بهبود عملکرد فقط با تغییر نحوهٔ اجرای Queryها. 🔥
اما اگر با Entity Framework Core به شکل ساده بخواهی آنها را موازی کنی…
اپلیکیشن Crash میکند.
💥 وعدهٔ دروغین Task.WhenAll
رایجترین اشتباه این است که کد را برداری، آن را در چند Task بیندازی و منتظر بمانی:
// ❌ DO NOT DO THIS
public async Task<DashboardData> GetDashboardData(int userId)
{
var ordersTask = _repository.GetOrdersAsync(userId);
var logsTask = _repository.GetLogsAsync();
var statsTask = _repository.GetStatsAsync(userId);
await Task.WhenAll(ordersTask, logsTask, statsTask); // BOOM 💥
return new DashboardData(ordersTask.Result, logsTask.Result, statsTask.Result);
}
اگر این را اجرا کنی، بلافاصله با این Exception روبرو میشوی:
A second operation started on this context before a previous operation completed.
This is usually caused by different threads using the same instance of DbContext...
چرا این اتفاق میافتد؟ 🤔
❗️چرا DbContext Thread-Safe نیست؟
زیرا DbContext در EF Core stateful است و برای "یک Unit of Work" طراحی شده.
دلایل اصلی:
1️⃣ Change Tracker
ءDbContext وضعیت Entityهایی که بارگذاری شدهاند را نگه میدارد.
دو Thread همزمان نمیتوانند این وضعیت را تغییر دهند.
2️⃣ Single Database Connection
هر DbContext معمولاً یک Connection دارد.
پروتکل پایگاهداده (TCP Stream برای PostgreSQL یا SQL Server) اجازهٔ اجرای همزمان چند Query روی یک Connection را نمیدهد.
3️⃣ ءRace Condition و خراب شدن State داخلی
اگر دو Query همزمان اجرا شوند، DbContext نمیداند کدام نتیجه مربوط به کدام Query است.
بنابراین EF Core برای جلوگیری از فساد داده، Exception میاندازد.
🧩 پس یک تناقض داریم:
میخواهیم Queryها را موازی اجرا کنیم
اما EF Core ما را مجبور میکند پیاپی اجرا کنیم.
در ادامهٔ مقاله راهحلهای درست معرفی میشود…
✅ راهحل
از NET 5. به بعد، EF Core دقیقاً برای همین سناریو یک راهحل درجهیک ارائه داده:
IDbContextFactory<T> 🎉
بهجای تزریق یک DbContext با طول عمر Scoped (که برای کل Request زنده میماند)، یک Factory تزریق میکنیم که هر زمان لازم شد یک DbContext سبک، مستقل و بدون تداخل بسازد.
این یعنی:
هر Task → یک DbContext مستقل → یک Connection مستقل → بدون هیچ Conflict👌
💡نکته:
اگر به DbContextOptions دسترسی داری، حتی میتوانی دستی هم Context بسازی:
using var context = new AppDbContext(options);
🛠 ثبت Factory در Program.cs
ء<IDbContextFactory<AppDbContext به صورت Singleton رجیستر میشود و خود AppDbContext همچنان به صورت Scoped قابل استفاده است
builder.Services.AddDbContextFactory<AppDbContext>(options =>
{
options.UseNpgsql(builder.Configuration.GetConnectionString("db"));
});
⚡️ ءRefactor کردن Dashboard برای اجرای موازی
بهجای اینکه AppDbContext را تزریق کنیم، <IDbContextFactory<AppDbContext را تزریق میکنیم.
در هر Task:
1️⃣ یک DbContext جدید میسازیم
2️⃣ ءQuery را اجرا میکنیم
3️⃣ آن را Dispose میکنیم
بدون هیچ تداخلی 🔥
using Microsoft.EntityFrameworkCore;
public class DashboardService(IDbContextFactory<AppDbContext> contextFactory)
{
public async Task<DashboardDto> GetDashboardAsync(int userId)
{
// 1. اجرای موازی تمام Query ها
var ordersTask = GetOrdersAsync(userId);
var logsTask = GetSystemLogsAsync();
var statsTask = GetUserStatsAsync(userId);
// 2. صبر برای اتمام همه
await Task.WhenAll(ordersTask, logsTask, statsTask);
// 3. برگرداندن نتایج
return new DashboardDto(
await ordersTask,
await logsTask,
await statsTask
);
}
private async Task<List<Order>> GetOrdersAsync(int userId)
{
await using var context = await contextFactory.CreateDbContextAsync();
return await context.Orders
.AsNoTracking()
.Where(o => o.UserId == userId)
.OrderByDescending(o => o.CreatedAt)
.ThenByDescending(o => o.Amount)
.Take(50)
.ToListAsync();
}
private async Task<List<SystemLog>> GetSystemLogsAsync()
{
await using var context = await contextFactory.CreateDbContextAsync();
return await context.SystemLogs
.AsNoTracking()
.OrderByDescending(l => l.Timestamp)
.Take(50)
.ToListAsync();
}
private async Task<UserStats?> GetUserStatsAsync(int userId)
{
await using var context = await contextFactory.CreateDbContextAsync();
return await context.Users
.Where(u => u.Id == userId)
.Select(u => new UserStats { OrderCount = u.Orders.Count })
.FirstOrDefaultAsync();
}
}
🧠 مفاهیم کلیدی
1️⃣ جداسازی کامل Contextها 🧩
هر Task → یک DbContext مستقل
یعنی:
• یک Connection جدا
• بدون رقابت روی Change Tracker
• بدون Race Condition
• بدون Exception خطرناک EF Core
2️⃣ ءDispose شدن سریع Context 🧹
با await using، Context فوراً Dispose میشود و Connection به Connection Pool برمیگردد.
این نکته حیاتی است، مخصوصاً زیر بار بالا.
📊 Benchmark
برای اثبات اینکه این روش واقعاً کار میکند، یک برنامه کوچک NET 10. با Aspire و PostgreSQL ساختم.
چون همهچیز روی سیستم محلی اجرا میشد، زمانها خیلی پاییناند؛
اما اگر دیتابیس ریموت بود، اعداد بزرگتر میشدند — نسبتِ سرعت (Speedup Ratio) تقریباً همین میمانَد.
🐢 اجرای ترتیبی (Sequential Execution): حدود ۳۶ms
در حالت ترتیبی، دقیقاً یک Waterfall داریم:
هر Query منتظر تمامشدن Query قبلی میمانَد.
یعنی:
Query 1 → تمام شود → Query 2 → تمام شود → Query 3
این ساختار از لحاظ تجربهٔ کاربری، فوقالعاده کند است. ⏳
⚡️ اجرای موازی (Parallel Execution): حدود ۱۳ms
با رویکرد موازیسازی:
تمام Query ها همزمان شروع میشوند و تقریباً همزمان به پایان میرسند.
نتیجه:
۳ برابر سریعتر
(در این مثال: ۳۶ms → ۱۳ms)
این همان جایی است که تاخیرهای شبکه، I/O و latency دیتابیس را بهصورت مؤثری کاهش میدهیم. 🚀
⚖️ ملاحظات، Trade-off ها و نتیجهگیری
استفاده از IDbContextFactory پلی است بین:
معماری EF Core که بر واحد کار (Unit of Work) و Context واحد طراحی شده و نیازهای مدرن که اجرای موازی حقیقی را میطلبد.
این الگو به شما اجازه میدهد از جعبهٔ
"یک درخواست → یک Thread → یک DbContext"
خارج شوید — بدون اینکه Thread-Safety یا Data Integrity قربانی شود. ✔️
اما باید با احتیاط استفاده شود:
⚠️ 1️⃣ احتمال اتمام Connection Pool
وقتی درخواست قبلاً فقط ۱ Connection مصرف میکرد،
در حال حاضر همان درخواست ۳ Connection میگیرد.
اگر ترافیک بالایی داشته باشید و connection pool کوچک باشد:
ممکن است سریعاً Connection Exhaustion رخ دهد.
این یعنی درخواستها در صف میمانند یا Timeout میدهند. ❗️
⚠️ 2️⃣ سربار Context
اگر Query های شما بسیار سریع هستند (lookup ساده بر اساس ID)،
ساختن چندین Task و DbContext جدید
ممکن است حتی کندتر از حالت ترتیبی شود!
این روش مناسب Query های سنگینتر و پنجرههای بزرگ داده است—not simple queries. ⚙️
🧠 نکتهٔ پایانی
وقتی با یک Dashboard کند مواجه شدید،
قبل از اینکه سراغ SQL دستی یا Stored Procedure بروید…
نگاهی به await های خود بیندازید.
اگر پشتسرهم صف شدهاند،
وقت آن است که کمی فکر موازی انجام دهید. ⚡️
🔖هشتگها:
#EFCore #ParallelProgramming
#AsyncAwait #SoftwareEngineering #IDbContextFactory
Forwarded from Sonora.Dev
🚀 بهینهسازی پروژههای .NET با Directory.Build.props
اگر روی پروژههای چندپروژهای یا بزرگ در .NET کار میکنید، احتمالاً با مشکلاتی مثل:
ناسازگاری نسخهها
تعریف تنظیمات تکراری برای هر پروژه
مدیریت مسیرهای خروجی و تنظیمات کامپایلر
روبرو شدهاید. اینجاست که Directory.Build.props میتواند ناجی شما باشد!
چرا Directory.Build.props مهم است؟
پیکربندی مرکزی پروژهها: میتوانید تنظیمات عمومی مانند نسخه C# (LangVersion)، مسیر خروجی (OutputPath)، Company و Version را یکجا تعریف کنید.
صرفهجویی در زمان: دیگر نیازی به تغییر دستی تنظیمات در هر پروژه نیست.
ثبات و هماهنگی: تمام پروژههای زیرشاخه از این تنظیمات به ارث میبرند، بنابراین رفتار یکسانی خواهند داشت.
کنترل Warningها و Build Options: میتوانید به راحتی فعال یا غیرفعال کردن Warningها و گزینههای کامپایلر را مدیریت کنید.
نکته عملی:
کافیست یک فایل Directory.Build.props در ریشه Solution ایجاد کنید و Propertyهای مشترک را در آن تعریف کنید. این فایل به صورت خودکار روی تمام پروژههای زیرشاخه اعمال میشود و از تکرار کد و ناسازگاری جلوگیری میکند.
💡 استفاده از Directory.Build.props مخصوصاً در تیمهای بزرگ، باعث سادگی، امنیت و ثبات پروژهها میشود و توسعهدهندگان میتوانند روی نوشتن کد تمرکز کنند، نه تنظیمات پروژه.
#DotNet #CSharp #MSBuild #DirectoryBuildProps #SoftwareDevelopment #BestPractices
اگر روی پروژههای چندپروژهای یا بزرگ در .NET کار میکنید، احتمالاً با مشکلاتی مثل:
ناسازگاری نسخهها
تعریف تنظیمات تکراری برای هر پروژه
مدیریت مسیرهای خروجی و تنظیمات کامپایلر
روبرو شدهاید. اینجاست که Directory.Build.props میتواند ناجی شما باشد!
چرا Directory.Build.props مهم است؟
پیکربندی مرکزی پروژهها: میتوانید تنظیمات عمومی مانند نسخه C# (LangVersion)، مسیر خروجی (OutputPath)، Company و Version را یکجا تعریف کنید.
صرفهجویی در زمان: دیگر نیازی به تغییر دستی تنظیمات در هر پروژه نیست.
ثبات و هماهنگی: تمام پروژههای زیرشاخه از این تنظیمات به ارث میبرند، بنابراین رفتار یکسانی خواهند داشت.
کنترل Warningها و Build Options: میتوانید به راحتی فعال یا غیرفعال کردن Warningها و گزینههای کامپایلر را مدیریت کنید.
نکته عملی:
کافیست یک فایل Directory.Build.props در ریشه Solution ایجاد کنید و Propertyهای مشترک را در آن تعریف کنید. این فایل به صورت خودکار روی تمام پروژههای زیرشاخه اعمال میشود و از تکرار کد و ناسازگاری جلوگیری میکند.
💡 استفاده از Directory.Build.props مخصوصاً در تیمهای بزرگ، باعث سادگی، امنیت و ثبات پروژهها میشود و توسعهدهندگان میتوانند روی نوشتن کد تمرکز کنند، نه تنظیمات پروژه.
#DotNet #CSharp #MSBuild #DirectoryBuildProps #SoftwareDevelopment #BestPractices
50 سؤال برای آمادگی مصاحبه #C 🚀
سؤالات واقعی — نه تولیدشده توسط AI 👇
1️⃣ تفاوت readonly و const چیست؟
2️⃣ کلمه کلیدی sealed برای چه استفاده میشود؟
3️⃣ تمام access modifier های مربوط به type ها را نام ببرید.
4️⃣ تفاوت interface و abstract class چیست؟
5️⃣ چه زمانی static constructor فراخوانی میشود؟
6️⃣ چگونه یک extension method ایجاد میکنیم؟
7️⃣ آیا #C از multiple class inheritance پشتیبانی میکند؟
8️⃣ ءboxing و unboxing را توضیح دهید.
9️⃣ ءheap و stack چیستند؟
🔟 تفاوت string و StringBuilder چیست؟
1️⃣1️⃣ چگونه یک تاریخ با timezone مشخص ایجاد میکنیم؟
2️⃣1️⃣ چگونه current culture را تغییر میدهیم؟
3️⃣1️⃣ تفاوت HashSet و Dictionary چیست؟
4️⃣1️⃣ هدف متد ToLookup چیست؟
5️⃣1️⃣ آیا متد <Cast<T در LINQ یک object جدید ایجاد میکند؟
6️⃣1️⃣ ءdeferred execution در LINQ را توضیح دهید.
7️⃣1️⃣ ءImmutableList چگونه کار میکند؟
8️⃣1️⃣ مزایای استفاده از Frozen Collection ها چیست؟
9️⃣1️⃣ مجموعههای thread-safe را نام ببرید.
0️⃣2️⃣ چگونه برای کد asynchronous یک lock اعمال میکنیم؟
1️⃣2️⃣ تمام روشهای ساخت یک Thread جدید را نام ببرید.
2️⃣2️⃣ چگونه چند async task را همزمان اجرا میکنیم؟
3️⃣2️⃣ ءInheritance در برابر Composition را توضیح دهید.
4️⃣2️⃣ تفاوت class و record و struct چیست؟
5️⃣2️⃣ ءref struct برای چه استفاده میشود؟
6️⃣2️⃣ دو نوع record را نام ببرید.
7️⃣2️⃣ کلمه کلیدی with برای چه استفاده میشود؟
8️⃣2️⃣ هدف Primary Constructor ها چیست؟
9️⃣2️⃣ ءNullable Reference Types چگونه کار میکنند؟
0️⃣3️⃣ آیا switch expression محدودیت نوع بازگشتی دارد؟
1️⃣3️⃣ ءyield return برای چه استفاده میشود؟
2️⃣3️⃣ ءGarbage Collector چند generation دارد؟
3️⃣3️⃣ کلاس Interlocked برای چه استفاده میشود؟
4️⃣3️⃣ کامپایلر برای auto-property ها چه کدی تولید میکند؟
5️⃣3️⃣ ءPolymorphism چگونه در #C پیادهسازی میشود؟
6️⃣3️⃣ ءEncapsulation چگونه در #C پیادهسازی میشود؟
7️⃣3️⃣ تفاوت ref و out چیست؟
8️⃣3️⃣ دستور using چگونه کار میکند؟
9️⃣3️⃣ ءdelegate چیست و چگونه استفاده میشود؟
0️⃣4️⃣ تفاوت overloading و overriding را توضیح دهید.
1️⃣4️⃣ تفاوت IEnumerable و IQueryable چیست؟
2️⃣4️⃣ ءexpression tree ها در LINQ چیستند؟
3️⃣4️⃣ ءException Handling در #C چگونه کار میکند؟
4️⃣4️⃣ تمام روشهای rethrow کردن exception را نام ببرید.
5️⃣4️⃣ ءgenerics را توضیح دهید.
6️⃣4️⃣ تفاوت Auto reset event و Manual reset event چیست؟
7️⃣4️⃣ چگونه کد async را lock میکنیم؟
8️⃣4️⃣ تفاوت volatile و Interlocked چیست؟
9️⃣4️⃣ تمام روشهای ایجاد Thread را نام ببرید.
0️⃣5️⃣ تفاوت Task.Run و TaskFactory.StartNew چیست؟
🔖هشتگها:
#DotNetInterview #ProgrammingInterview #CSharpInterview
12الگوی ضروری طراحی در سیستمهای توزیعشده که هر Architect باید بداند 🌐🚀
ساخت Distributed System کار سادهای نیست.
هرچه سیستمها بزرگتر میشوند و بین چندین سرویس توزیع میشوند، تیمها با چالشهایی مثل Service Communication، Data Consistency، Fault Tolerance و Deployment Strategyها روبهرو میشوند. ⚙️🔥
خبر خوب این است که بسیاری از این چالشها از قبل راهحلهای استاندارد و اثباتشده دارند.
این راهحلها همان Design Patternهای سیستمهای توزیعشدهاند؛ الگوهایی که در میدان عمل بارها تست شدهاند و معماران نرمافزار برای حل مشکلات مشترک معماری از آنها استفاده میکنند. 💡
درک این الگوها کمک میکند:
• تصمیمهای معماری دقیقتری بگیری
• از اشتباهات رایج دوری کنی
• سیستمهایی بسازی که مقاوم، مقیاسپذیر و قابلنگهداری باشند
در این پست، ۱۲ الگوی مهم و کلیدی در سیستمهای توزیعشده را بررسی میکنیم:
1️⃣ API Gateway 🛂
یک نقطه ورودی واحد برای تمام درخواستها که امنیت، Routing، Aggregation و Cross-Cutting Concernها را مدیریت میکند.
2️⃣ Point-To-Point Async Integration 🔄
یک سرویس بهطور مستقیم پیام را برای یک سرویس دیگر ارسال میکند؛ مناسب سناریوهای ساده با coupling بیشتر.
3️⃣ Publish/Subscribe Pattern 📢📨
ءPublisher پیامها را منتشر میکند و Subscriberها بدون وابستگی به ارسالکننده آنها را دریافت میکنند. ایدهآل برای decoupling و Event-Driven Architecture.
4️⃣ Outbox Pattern 📦📝
برای حفظ Consistency بین Database و Message Broker.
اول پیام در Outbox ذخیره میشود؛ بعداً توسط یک پروسس ارسال میگردد → هیچ Eventی گم نمیشود.
5️⃣ CQRS (Command Query Responsibility Segregation) ⚔️
جداسازی مسیر Query (فقط خواندنی) از Command (تغییر وضعیت).
در سیستمهای پیچیده کارایی و خوانایی را افزایش میدهد.
6️⃣ Saga Pattern 🎭🧩
برای مدیریت Distributed Transactionها بدون استفاده از دو-phase commit.
در صورت Failure، عملیات جبرانی اجرا میشود.
7️⃣ Sidecar Pattern 🧳
قرار دادن یک Container کوچک کنار سرویس اصلی برای کارهایی مثل Proxy، Observability، Config و امنیت.
در Service Meshها حیاتی است.
8️⃣ Strangler Fig Pattern 🌱➡️🌳
یک سیستم قدیمی بهتدریج با بخشهای جدید جایگزین میشود.
مهاجرت بدون downtime.
9️⃣ Anti-Corruption Layer Pattern 🛡
لایهای که سیستم جدید را از مدل قدیمی جدا میکند تا domain جدید آلوده نشود.
1️⃣0️⃣ Service Discovery Pattern 🔍
سرویسها بدون نیاز به hardcoded URL همدیگر را پیدا میکنند (Eureka، Consul، Envoy).
1️⃣1️⃣ Sharding Pattern 🧩📊
تقسیم دادهها بین چند دیتابیس برای افزایش مقیاسپذیری و کاهش load.
1️⃣2️⃣ Replication Pattern 📚🔁
کپیکردن دادهها روی چند Node برای دسترسپذیری بالا و fault tolerance.
1️⃣ API Gateway 🛂🚪(بخش اول)
ءAPI Gateway یک نقطهٔ ورودی واحد است که بین Clientها و سرویسهای Backend قرار میگیرد.
این کامپوننت در نقش Reverse Proxy عمل میکند و درخواستها را به Microservice مناسب Route میکند. علاوهبراین، بسیاری از Cross-Cutting Concernها مثل احراز هویت، Rate Limiting، و Transform کردن درخواستها را مدیریت میکند.
نحوهٔ کارکرد API Gateway ⚙️📨
تمام درخواستهای Client به یک Endpoint واحد (API Gateway) ارسال میشود، نه به تکتک سرویسها
ءGateway پس از دریافت درخواست، عملیات Authentication و Authorization را انجام میدهد
قوانین Rate Limiting و Throttling اعمال میشود تا سرویسها زیر فشار از کار نیفتند
ءGateway با توجه به مسیر URL یا Headerها، Request را به سرویس مناسب Route میکند
میتواند پاسخ چند سرویس را جمعآوری و به یک Response تجمیعشده برای Client تبدیل کند
مزایا ✨
🔹️سادهسازی سمت Client با ارائهٔ یک Endpoint واحد
🔹️ایجاد لایهٔ انتزاعی برای تغییر سرویسهای Backend بدون اثرگذاری روی Client
🔹️بهبود امنیت با مخفی کردن ساختار داخلی سرویسها و Endpointهای واقعی آنها
معایب ⚠️
🔸️تبدیل شدن به Single Point of Failure در صورت طراحی غلط
🔸️احتمال تبدیل شدن به Bottleneck در ترافیکهای بالا
🔸️افزودن Latency به هر درخواست بهخاطر یک Roundtrip اضافی شبکه
موارد استفاده 🎯
• معماریهای Microservices که نیاز به یک رابط یکپارچه برای چند سرویس مختلف دارند
• سیستمهایی که Authentication و Authorization یکپارچه نیاز دارند
• مدرنسازی سیستمهای Legacy با پنهان کردن سرویسهای قدیمی پشت یک API مدرن
تفاوت Task و ValueTask چیه؟ 🤔⚡️
خیلیها فکر میکنن ValueTask فقط یک نسخه سبکتر از Task هست…
ولی داستان خیلی جالبتره!
بیاین با یک مثال واقعی درکش کنیم 👇
📌 ءTask چیه؟
🔹️ءTask یعنی:
«یک عملیات async که ممکنه هنوز انجام نشده باشه.»
در NET.، حتی اگر عملیات نتیجهای داشته باشه، یه Task جدید ساخته میشه.
این ساخت Task هزینه داره:
• یک Object جدید ساخته میشه
• ءGC باید بعداً جمعش کنه
• ءMemory Allocation رخ میده
در عملیات سبک و پرتکرار؟
این Allocations میتونه پرفورمنس رو زمین بزنه.
📌 ءValueTask چیه؟
ءValueTask اومده همین مشکل رو حل کنه!
🔸️ءValueTask یعنی:
«یک عملیات async که ممکنه نتیجه رو همین الان داشته باشه.»
اگر نتیجه آماده باشه، ValueTask هیچ object اضافهای ایجاد نمیکنه
• یعنی Zero Allocation
• یعنی سرعت بیشتر
• یعنی فشار کمتر روی GC
به همین دلیل یکی از سریعترین Primitiveهای داتنته.
💥 یک مثال واقعی
تصور کن یک Cache داری:
public Task<Item> GetItemAsync(string key)
{
if (_cache.TryGetValue(key, out var item))
return Task.FromResult(item); // Allocates
return FetchFromDbAsync(key);
}
در حالت بالا حتی برای cache hit هم یک Task جدید ساخته میشه!
ولی با ValueTask:
public ValueTask<Item> GetItemAsync(string key)
{
if (_cache.TryGetValue(key, out var item))
return new ValueTask<Item>(item); // No allocation
return new ValueTask<Item>(FetchFromDbAsync(key));
}
نتیجه؟
در حالت cache hit → هیچ Task اضافهای ساخته نمیشود.
⚠️ اما ValueTask همیشه بهتر نیست!
نکات مهم:
1️⃣ فقط یک بار میتونی awaitش کنی
چون value-type هست
اگر چند بار await کنی → Undefined Behavior
2️⃣ اگر عملیات همیشه async باشه ValueTask فقط پیچیدگی اضافه میکنه. Task مناسبتره.
3️⃣ ءchaining پیچیدهتره
ءConfigureAwait، ContinueWith و … روی ValueTask رفتارهای خاص دارند.
🎯 کی Task؟ کی ValueTask؟
🔸️ از Task استفاده کن وقتی:🔸️
عملیات همیشه async هست
مثل: query دیتابیس، ارسال ایمیل، فراخوانی API
نتیجه زود آماده نمیشود
سرویس ساده و خوانایی مهمتر از پرفورمنسه
🔸️ از ValueTask استفاده کن وقتی:🔸️
عملیات گاهی sync و گاهی async است
مانند: Cache, MemoryReader, CPU-bound operations
عملیات پرتکرار است و performance حیاتی است
میخواهی مخصــوصاً Allocation را صفر کنی
📝 جمعبندی
ءTask → همیشه async → همیشه object جدید → هزینه بیشتر
ءValueTask → async یا sync → گاهی بدون object → سریعتر
اما ValueTask ابزار پیشرفتهست.
نسبت به Task مسئولیت بیشتری روی دوش برنامهنویسه میذاره.
پیشنهاد مایکروسافت:
“Only use ValueTask when performance measurements justify it.”
2️⃣ Point To Point Async Integration ⚡️📩(بخش دوم)
ءPoint To Point Async Integration یک الگوی ارتباطی هست که در آن یک سرویس، پیامها را از طریق یک Message Queue برای سرویس دیگر ارسال میکند.
برخلاف ارتباط synchronous، فرستنده منتظر پاسخ نمیماند و به کار خودش ادامه میدهد؛ درحالیکه سرویس گیرنده پیامها را با سرعت و زمانبندی خودش مصرف میکند.
این موضوع یک رابطه غیرهمزمان اما همچنان مستقیم بین دو سرویس ایجاد میکند.
🔧 How it works:
🔸️ءService A پیام را به یک صف اختصاصی که فقط Service B از آن مصرف میکند ارسال میکند
🔹️ءMessage Queue بهعنوان بافر بین فرستنده و گیرنده عمل میکند
🔸️ءService A بلافاصله بعد از ارسال پیام ادامه پروسه را انجام میدهد
🔹️ءService B پیامها را در زمان موردنظر خودش مصرف میکند
🔸️ءMessage Broker تضمین میکند که پیامها بهدرستی و قابلاعتماد ارسال شوند
🔹️اگر سرویـس B موقتاً در دسترس نباشد، پیامها در صف باقی میمانند
🔸️ءDead Letter Queue برای پیامهایی که بعد از چند تلاش دوباره fail میشوند استفاده میشود
✅ Benefits:
• ءDecouples services in time → سرویسها میتوانند با سرعتهای متفاوت کار کنند
• ءResilience بالاتر → قطع بودن Service B روی A تأثیری ندارد
• ءLoad leveling طبیعی از طریق صف
• ءAsync processing برای کارهای طولانی بدون بلاک کردن سرویس فرستنده
• ءScalability آسان با افزودن Consumerهای بیشتر
❌ Drawbacks:
• ءMessage Broker یک وابستگی اضافه است و ممکن است Single Point of Failure ایجاد کند
• منجر به Eventual Consistency میشود
• ءDebugging سختتر بهدلیل جریان غیرهمزمان پیامها
🎯 Use cases:
1️⃣ءBackground Job Processing
2️⃣سیستمهای پردازش سفارش (Order Fulfillment)
3️⃣ءEmail / Notification Services
4️⃣سیستمهایی با Load نامتوازن که Queue نقش بافر ایفا میکند
Forwarded from tech-afternoon (Amin Mesbahi)
چند سالیه که سهم عبارت «AI» لابلای جملات، تیتر اخبار، صحبتهای یومیهی عوام تا متخصصین، شهروند تا دولتمرد، مصرفکننده تا صنعتگر روز به روز بیشتر شده. ترمهایی مثل Vibe Coding یا AI-Driven Development یا AI Slop به دایرهی واژگانمون اضافه شدن. حالا این وسط یه عده سودهای کوتاهمدت میبرن، مثل پکیجفروشها، سرویسهایی که چند تا API رو صدا میکنن و یه سرویس مثلا هوشمند ارائه میکنن؛ و برخی هم بیزنسهای بر پایهی این تحول ایجاد کردن، مثل سازندههای مدلهای پایه، سرویسهای کاربردی مدیریت AI مثل Agent Manager یا Prompt Engineering Platform و… یا اینکه AI رو مثل یک ابزار دیدن و کاربری اون رو «صحیح و اصولی» یاد گرفتن و مرتبا بهروز میشن تا مثل دورانی که اکثریت با محدودیتهای نرمافزارهای دسکتاپ دستوپنجه نرم میکردن و عدهای خیلی زود و به موقع، توسعه مبتنی بر وب رو جایگزین کردن، بتونن از مواهب AI به نفع بهرهوری، خلاقیت، و توسعه پایدار بهره ببرن.
این مطلب رو در چند بخش مینویسم، با توجه به فضای جامعه توسعه نرمافزار، متن رو خیلی مطالعه-محور مینویسم تا مقاومت کمتری نسبت به تحلیل شخصی داشته باشه؛ اول به «بد»، و در ادامه به «زشت» و نهایتا به «خوب» میپردازم:
- کد چرخشی (Code Churn): سیگنال خطر: تحقیقات GitClear روی ۲۱۱ میلیون خط کد تغییریافته بین سالهای ۲۰۲۰ تا ۲۰۲۴ نشون میده که Code Churn (درصد کدی که کمتر از دو هفته پس از نوشته شدن اصلاح یا حذف میشه) در سال ۲۰۲۴ دو برابر سال ۲۰۲۱ شده. این یعنی کدی که AI تولید میکنه، اغلب ناقص یا اشتباهه و نیاز به بازنگری سریع داره.
- کپیپیست به جای معماری: در سال ۲۰۲۴، GitClear افزایش ۸ برابری در بلوکهای کد تکراری (۵ خط یا بیشتر) رو ثبت کرده (یعنی ۱۰ برابر بیشتر از دو سال پیشش). مشکل اینجاست که AI به جای refactor کردن و استفاده مجدد از کد موجود، ترجیح میده کد جدید بنویسه. نتیجه؟ نقض اصل (DRY (Don't Repeat Yourself و کدبیسی که مدیریتش کابوسه.
در سال ۲۰۲۴، ۴۶٪ تغییرات کد، خطوط جدید بودند و کد کپیپیست شده، بیش از کد جابجا شده (moved) بوده (یعنی کمتر refactoring شده و بیشتر به صورت بیرویه کد اضافه شده).
- افزایش باگ و کاهش پایداری: مطالعه شرکت Uplevel که توسعهدهندههای با دسترسی به Copilot رو بررسی کرده، نشون میده این دولوپرها به طور معناداری نرخ باگ بالاتری تولید کردن، در حالی که بهرهوری کلیشون ثابت مونده. گزارش DORA 2024 گوگل هم تأیید میکنه: ۲۵٪ افزایش در استفاده از AI منجر به بهبود در code review و مستندات میشه، اما ۷.۲٪ کاهش در پایداری تحویل (delivery stability) ایجاد میکنه. همچنین گزارش Harness 2025 نشون داد اکثر توسعهدهندگان زمان بیشتری صرف debugging کد تولیدشده توسط AI و رفع آسیبپذیریهای امنیتی میکنند.
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from tech-afternoon (Amin Mesbahi)
اگر "بد" نمایانگر اصطکاک عملیاتیه، "زشت" نمایانگر ریسک سیستمیه. دادههای سالهای ۲۰۲۴ و ۲۰۲۵ به بحرانی قریبالوقوع در قابلیت نگهداری و امنیت نرمافزار اشاره میکنن...
جنبهی «زشت» ماجرا اینه که نتیجهی نهایی استفاده از هوش مصنوعی مولد بهشدت وابسته به بلوغ فنی و انضباط تیمه. اگر تیمی فرهنگ کدنویسی سالم، معیارهای کیفی و فرایندهای بازبینی روشن نداشته باشه، برای استفاده از GenAI دستورالعمل «فکر شده» و متناسب با نیازها و استعداد تیم نداشته باشه؛ AI میتونه هرجومرج ایجاد کنه یا هرجومرج موجود رو تشدید کنه. توی برخی نظرسنجیها دیده شده که کارکنان احساس کردن بهرهوریشون با وجود هوش مصنوعی کاهش یافته!
بدهی فنی که قابل پرداخت نیست
پروفسور Armando Solar-Lezama استاد دانشگاه MIT میگه: "AI مثل یه کارت اعتباری جدیده که به ما اجازه میده بدهی فنی رو به روشهایی انباشته کنیم که هرگز قبلاً نتونسته بودیم."
مطالعه دانشگاه Carnegie Mellon روی ۸۰۷ ریپو GitHub که بین ژانویه ۲۰۲۴ تا مارچ ۲۰۲۵ که از Cursor استفاده کرده بودن، نشون میده که با وجود بهبودهای مدلهای AI (Sonnet، GPT و غیره)، الگوی کاهش کیفیت کد همچنان ادامه داره. حتی با ارتقای ابزارها، کیفیت کد مسیر خودش رو به سمت افول طی میکنه! دلایلی مثل زمان صرف زیاد برای آزمونوخطا با ابزار یا رفع خطاهای ناشی از اون رو میشه در نظر گرفت؛ و تفاوت نتایج بین شرکتهای مختلف (از افزایش کارامدی تا معضلات عمیق) نشون میده که صرف خریداری یا فعالسازی ابزار یا سرویس هوشمصنوعی تضمینی برای موفقیت نیست.
- نابودی دانش تیمی: باز هم مطالعات نشون میدن در ۱۶.۸٪ از چتهای ChatGPT، کد تولید شده به صورت دقیق (با تغییرات جزئی) توی پروژههای GitHub استفاده شدن. مشکل اینجاست: وقتی توسعهدهندهها کد AI رو بدون درک عمیق copy میکنن، expertise model توی تیم توسعه آسیب میبینه و Truck Factor (تعداد اعضای تیم که از دست دادنشون پروژه را میتونه نابود کنه، گاهی هم bus factor گفته میشه) بدتر میشه.
- معضل Context Collapse در آینده: اگه کدهایی که مدلهای آینده از روی اونها train میشن، پیچیدهتر و غیرقابل نگهداریتر بشه، خطر واقعی اینه که مدلهای جدیدتر این روندها رو به صورت نمایی تقویت و تشدید میکنن و کد بدتری تولید خواهند کرد؛ دلیلش هم اینه که از روی کدهای شلوغ و بیکیفیتی آموزش دیدهاند.
- مشارکتکننده دورهگرد: کدهای تولید شده توسط هوش مصنوعی شبیه کار یک پیمانکار کوتاهمدته: از نظر عملکردی در انزوا، صحیح، اما منفک از قراردادها و معماری سیستم کلی! این منجر به تکهتکه شدن (Fragmentation) سبک و منطق کد میشه.
- پارادوکس بهرهوری مهندسی: ترکیب "خوب" (سرعت) و "زشت" (ریزش/کیفیت) منجر به شکلگیری "پارادوکس بهرهوری مهندسی" شده. سازمانها شاهد افزایش چشمگیر خروجی (پولریکوئستها، ویژگیها) هستن، اما همزمان کاهش پایداری و افزایش هزینههای نگهداری رو تجربه میکنن. گزارش سال ۲۰۲۵ DORA از گوگل نشون داد که افزایش ۹۰ درصدی در پذیرش هوش مصنوعی با افزایش ۹ درصدی نرخ باگ و افزایش ۹۱ درصدی زمان بازبینی کد همبستگی داره (بدتر از گزارش DORA در سال ۲۰۲۴ که پیشتر در بخش افزایش باگ و کاهش پایداری قسمت اول اشاره کردم). زمان صرفهجویی شده در تایپ کردن کد، عملاً به مرحله بازبینی و دیباگ منتقل شده؛ با این تفاوت که هزینه این مرحله بالاتره، چون خوندن کد تولید شده سختتر از نوشتنشه.
- انباشت بدهی فنی: انباشت کدهای ضعیف ساختاری، که با پیچیدگی بالا (Cyclomatic Complexity) و تکرار زیاد مشخص میشن؛ بدهیای ایجاد میکنه که باید با بهره پرداخت بشه. Forrester پیشبینی میکنه که سال ۲۰۲۶، ۷۵٪ از شرکتها به دلیل تولید کد کنترلنشدهی هوش مصنوعی، با بدهی فنی "متوسط تا شدید" مواجه خواهند شد.
Please open Telegram to view this post
VIEW IN TELEGRAM