C# Geeks (.NET) – Telegram
🛡 دستور using در #C: بهترین دوست IDisposable و راهی برای جلوگیری از نشت منابع
وقتی با فایل‌ها، دیتابیس، ارتباطات شبکه یا هر منبع خارجی دیگه‌ای کار می‌کنید، یه خطر بزرگ وجود داره: یادتون بره منابعی که باز کردید رو ببندید! این کار باعث "نشت منابع" (Resource Leaks) و مشکلات جدی در پرفورمنس و پایداری برنامه میشه.

سی‌شارپ یه راه حل خیلی شیک، امن و حرفه‌ای برای این مشکل داره: دستور using.

1️⃣ مشکل کجاست؟ IDisposable و مدیریت منابع
بعضی از آبجکت‌ها در دات‌نت، منابعی رو مدیریت می‌کنن که Garbage Collector به صورت خودکار نمی‌تونه اون‌ها رو آزاد کنه (مثل دستگیره فایل در سیستم‌عامل). این کلاس‌ها، اینترفیس IDisposable رو پیاده‌سازی می‌کنن که فقط یک متد به اسم Dispose() داره.

وظیفه ما به عنوان برنامه‌نویس اینه که همیشه و تحت هر شرایطی، بعد از تموم شدن کارمون با این آبجکت‌ها، متد Dispose() اون‌ها رو صدا بزنیم.

2️⃣ راه حل قدیمی (و زشت): try...finally 👎
راه سنتی برای اینکه مطمئن بشیم Dispose() همیشه صدا زده میشه (حتی اگه وسط کار یه Exception رخ بده)، استفاده از بلوک try...finally هست:
StreamReader reader = null;
try
{
reader = new StreamReader("myFile.txt");
// ... کار کردن با فایل ...
}
finally
{
if (reader != null)
{
reader.Dispose(); // تضمین می‌کنه که فایل همیشه بسته میشه
}
}

این کد کار می‌کنه، ولی طولانی و زشته.

3️⃣ راه حل مدرن و شیک: دستور using
دستور using یه "شکر سینتکسی" (Syntactic Sugar) برای همون بلوک try...finally هست.

به محض اینکه اجرای کد از بلوک using خارج بشه (چه به صورت عادی و چه به خاطر یه Exception)، کامپایلر به صورت خودکار متد Dispose() رو روی اون آبجکت صدا می‌زنه. اینجوری کد شما خیلی تمیزتر، کوتاه‌تر و امن‌تر میشه.
// این کد دقیقاً معادل کد try...finally بالاست
using (StreamReader reader = new StreamReader("myFile.txt"))
{
// ... کار کردن با فایل ...
}
// reader.Dispose() اینجا به صورت خودکار در هر حالتی صدا زده میشه


4️⃣ فرم جدیدتر (از C# 8): using declarations 🚀
از C# 8 به بعد، کار حتی از این هم ساده‌تر شده. اگه متغیر using رو به این شکل تعریف کنید، دیگه نیازی به آکولاد {} ندارید و اون متغیر در انتهای اسکوپ فعلی (معمولاً در انتهای متد) به صورت خودکار Dispose میشه.
void ReadFile()
{
using var reader = new StreamReader("myFile.txt");
using var writer = new StreamWriter("log.txt");

// ... کار کردن با فایل‌ها ...

} // reader.Dispose() و writer.Dispose() اینجا به صورت خودکار صدا زده میشن


🤔 حرف حساب و قانون طلایی

قانون طلایی: هر کلاسی که IDisposable رو پیاده‌سازی کرده، باید داخل یه بلوک using (یا با سینتکس جدیدش) ازش استفاده بشه.

شما بیشتر از کدوم فرمت using استفاده می‌کنید؟ کلاسیک با براکت یا فرمت جدید بدون براکت؟

🔖 هشتگ‌ها:
#CSharp #Programming #Developer #DotNet #BestPractices #CleanCode #IDisposable
😵‍💫 برنامه‌نویس NET.؟

می‌خوای از باگ‌های دردناک، امنیت داغون و کد اسپاگتی در امان بمونی؟
این 10 قاتل خاموش رو دست‌کم نگیر!
(حتی تو پروژه‌های به‌ظاهر «Enterprise» هم می‌بینمشون…)



1️⃣ بدون اعتبارسنجی ورودی؟ خداحافظ با ثبات 😬

داده‌ی خراب بیاد تو، همه‌چی میریزه به هم.
پاک کردن داده‌ی خراب ۱۰ برابر سخت‌تر از جلوگیری از ورودشه!

🧱 همیشه ورودی رو چک کن →
Validate before trust



2️⃣ مقادیر هاردکد شده 🎯

عاشق سرتاسری Replace زدن تو پروژه‌ای؟
از «عدد جادویی» استفاده کن 😎
نه‌؟ پس برو سراغ:
🔸 فایل پیکربندی
🔸 ثابت‌ها
🔸 دیتابیس



3️⃣ وابستگی سفت و سخت بین کلاس‌ها 🔗

وقتی یه کلاس به کلاس دیگه دوخته شده باشه، انعطاف می‌ره هوا.
🙅‍♂️ با abstraction (مثل Interface) کدت رو قابل تغییر نگه دار.



4️⃣ تست واحد؟ وقت ندارم؟ 😐

اگه الان وقت نداری تست بنویسی،
فردا کلی وقت داری باگ‌هات رو دیباگ کنی! 🔥



5️⃣ مدیریت نکردن Exception ها 💣

اگه استک‌تریس کامل رو فرستادی تو پاسخ HTTP، یه کادو به هکر دادی!
📛 ارورها رو لاگ کن.
پیام امن بده به کاربر.



6️⃣ کد غیرقابل خوندن 🤯

واسه انسان بنویس نه ماشین!
اگه باید یه کد رو ۳ بار بخونی تا بفهمی چی می‌گه، خیلی پیچیده‌ست.

🧼 تمیز بنویس تا خودت ۳ ماه دیگه هم بفهمیش!



7️⃣ طراحی ضعیف دیتابیس 🐢

طرح دیتای اشتباه یعنی:
🔹 کندی
🔹 غیرقابل توسعه بودن
🔹 زجر در آینده

بلندمدت فکر کن موقع مدل‌سازی!



8️⃣ امنیت؟ بذار بعداً!

👾 این‌جوری هک می‌شی رفیق!
ورودی‌ها رو بررسی کن
کمترین سطح دسترسی
هرچی می‌اد رو پاکسازی کن (Sanitize)

امنیت چیز اختیاری نیست… هیچ‌وقت!



9️⃣ بدون لاگ و مانیتورینگ 🚫📊

برنامه رو منتشر کردی؟ دم‌ت گرم!
ولی حالا از کجا می‌فهمی داره درست کار می‌کنه؟
🕵️‍♂️ بدون لاگ = پرواز کور



🔟 اختراع دوباره چرخ؟ 😅

مگه قراره ORM اختراع کنی؟
تا وقتی یه ابزار تست‌شده و امن هست،
استفاده‌ش کن
🧠 وقتتو صرف چیزای ارزشمندتر کن!



🎯 لازم نیست کدت بی‌نقص باشه…

ولی اگه اینا رو نادیده بگیری،
کد Legacy درست کردی، نه پروژه حرفه‌ای!



🔖 هشتگ‌ها:
#DotNet #CSharp #CodingTips #BestPractices #Programming #SoftwareEngineering #DevLife
💾 حافظه کلاس‌ها در #C: راهنمای کامل Fields, readonly و const

تا حالا شده از خودتون بپرسید فرق بین یه فیلد readonly و یه const چیه؟ یا اینکه متغیرهایی که داخل یه کلاس تعریف می‌کنیم (فیلدها) چطور و کِی مقداردهی میشن؟

امروز می‌خوایم بریم تو دل کلاس‌ها و با اعضای داده‌ای اون‌ها و قوانین حاکم بر اون‌ها آشنا بشیم.

1️⃣ فیلدها (Fields):

حافظه داخلی یک آبجکت فیلدها، متغیرهایی هستن که داخل یه کلاس یا struct تعریف میشن و "وضعیت" یا "داده"‌های اون آبجکت رو در طول عمرش نگه می‌دارن. هر نمونه (instance) از کلاس، یک کپی مستقل از فیلدهای خودش رو داره.

💡نکته حرفه‌ای (قرارداد نام‌گذاری):
یه قرارداد رایج و خوب، استفاده از آندرلاین (_) برای فیلدهای private هست تا به راحتی از متغیرهای محلی و پارامترها تشخیص داده بشن (مثلاً name_).
مقداردهی اولیه فیلدها:
مقداردهی اولیه به فیلدها اختیاریه. اگه مقداری بهشون ندید، مقدار پیش‌فرض نوعشون رو می‌گیرن. نکته مهم اینه که این مقداردهی قبل از اجرای اولین خط کد در سازنده (constructor) انجام میشه.

2️⃣ کلید readonly: فیلدهای فقط-خواندنی 🔒

اگه می‌خواید یه فیلد فقط یک بار مقداردهی بشه و بعد از ساخته شدن آبجکت دیگه هرگز تغییر نکنه، از readonly استفاده کنید. این برای مقادیری که در زمان ساخت آبجکت مشخص میشن ولی بعدش باید ثابت بمونن، عالیه.

قانون مهم: فیلدهای readonly فقط در دو جا می‌تونن مقدار بگیرن: یا موقع تعریف اولیه، یا داخل سازنده (constructor) اون کلاس.
public class UserProfile
{
// مقداردهی موقع تعریف
public readonly Guid UserId = Guid.NewGuid();

// تعریف بدون مقدار اولیه
public readonly DateTime RegistrationDate;

public UserProfile()
{
// مقداردهی در سازنده
RegistrationDate = DateTime.UtcNow;
}

public void UpdateProfile()
{
// UserId = Guid.NewGuid(); // خطای زمان کامپایل!
}
}


3️⃣ دوئل بزرگ: const در برابر static readonly ⚔️

این یکی از کلاسیک‌ترین سوالات مصاحبه و یکی از مهم‌ترین نکات در طراحی حرفه‌ایه. هر دو برای مقادیر ثابت به کار میرن، ولی تفاوت‌های حیاتی دارن.

🔵 const (ثابت زمان کامپایل):

• مقدارش باید در زمان کامپایل مشخص و ثابت باشه
(const double Pi = 3.14;).
• کامپایلر مقدارش رو مستقیماً در کد جایگزین می‌کنه (مثل ماکرو).
• فقط برای انواع داده ساده (عددی، رشته، بولین و...) قابل استفاده‌ست.

🔴 static readonly (ثابت زمان اجرا):

• مقدارش در زمان اجرا (معمولاً موقع استارت برنامه) مشخص میشه.
• یه فیلد واقعیه و در حافظه نگهداری میشه، مقدارش جایگزین نمیشه.
• برای هر نوع داده‌ای، حتی آبجکت‌های پیچیده، قابل استفاده‌ست.
public static readonly DateTime StartupTime = DateTime.Now;

😈تله خطرناک public const:

اگه شما یه public const رو در یک کتابخانه (Assembly A) تعریف کنید و در یک پروژه دیگه (Assembly B) ازش استفاده کنید، مقدار اون ثابت موقع کامپایل در Assembly B "پخته" یا "bake in" میشه. حالا اگه شما مقدار const رو در Assembly A عوض کنید، تا وقتی که Assembly B رو دوباره کامپایل نکنید، همچنان از همون مقدار قدیمی استفاده خواهد کرد! این می‌تونه منجر به باگ‌های فاجعه‌بار بشه. static readonly این مشکل رو نداره و همیشه آخرین مقدار رو می‌خونه، بنابراین انتخاب امن‌تریه.

🤔 حرف حساب و تجربه شما

انتخاب درست بین const و static readonly یکی از نشانه‌های یه توسعه‌دهنده باتجربه‌ست که به نگهداری بلندمدت کد فکر می‌کنه.
شما بیشتر از const استفاده می‌کنید یا static readonly؟ آیا تا حالا به مشکل اسمبلی با const برخورد کرده بودید؟

🔖 هشتگ‌ها:
#CSharp #Programming #Developer #DotNet #OOP #CleanCode #BestPractices
🔬 کالبدشکافی متدها در #C: امضا (Signature) و Overloading

هر متدی در #C یک "اثر انگشت" یا امضای (Signature) منحصر به فرد داره. این امضا هویت متد رو مشخص می‌کنه و به کامپایلر اجازه میده بین چندین متد با اسم یکسان، تفاوت قائل بشه.

امروز می‌خوایم این مفهوم حیاتی و قوانین Overloading رو کالبدشکافی کنیم.

1️⃣ امضای متد (Method Signature) چیست؟ 🆔
امضای یک متد از دو بخش اصلی تشکیل شده:
• اسم متد
• نوع و ترتیب پارامترها

نکات کلیدی و تله‌ها: ⚠️
موارد زیر جزو امضای متد حساب نمی‌شن:

نوع خروجی (Return Type)

کلمه کلیدی params

اسم پارامترها (فقط نوع و ترتیبشون مهمه)

اما کلمات کلیدی ref و out جزو امضا حساب میشن!

2️⃣ اورلودینگ (Overloading): یک اسم، چند کاربرد 🎭

اورلودینگ یعنی شما می‌تونید چند متد با اسم یکسان در یک کلاس داشته باشید، به شرطی که امضاشون متفاوت باشه (یعنی تعداد، نوع یا ترتیب پارامترهاشون فرق کنه).

مثال Overloadهای معتبر:
// این‌ها همگی Overloadهای معتبری از Foo هستن
void Foo(int x) { }
void Foo(double x) { } // نوع پارامتر فرق داره
void Foo(int x, float y) { } // تعداد پارامترها فرق داره
void Foo(float x, int y) { } // ترتیب انواع پارامتر فرق داره
void Foo(ref int x) { } // وجود ref امضا رو تغییر میده


مثال Overloadهای نامعتبر:
//  خطای زمان کامپایل!
// چون فقط نوع خروجی فرق می‌کنه که جزو امضا نیست.
void Foo(int x) { }
float Foo(int x) { }

// خطای زمان کامپایل!
// چون params جزو امضا نیست.
void Goo(int[] x) { }
void Goo(params int[] x) { }

// خطای زمان کامپایل!
// ref و out نمی‌تونن تنها تفاوت امضا باشن.
void Another(ref int x) { }
void Another(out int x) { }


🤔 حرف حساب و تجربه شما

درک دقیق قوانین امضا و اورلودینگ، به شما کمک می‌کنه APIهای تمیز، خوانا و قابل فهمی طراحی کنید.

آیا تا حالا با خطای کامپایل به خاطر امضای تکراری متدها برخورد کردید؟ یا از Overloading برای ساختن متدهای انعطاف‌پذیرتر در کدهاتون استفاده می‌کنید؟

🔖 هشتگ‌ها:
#CSharp #Programming #Developer #DotNet #OOP #Methods #BestPractices
🐳 داکر (Docker) چیست و چرا باید به عنوان یک توسعه‌دهنده #C بهش اهمیت بدیم؟
تا حالا شده بگید "ولی رو سیستم من کار می‌کرد!"؟ 😑 یا برای راه‌اندازی پروژه روی یه سیستم جدید، ساعت‌ها درگیر نصب دات‌نت، دیتابیس و هزارتا چیز دیگه بشید؟
داکر (Docker) اومده که این کابوس‌ها رو برای همیشه تموم کنه.
داکر به زبان ساده: کانتینرهای باربری! 🚢
فکر کنید برنامه‌ی شما یه سری کالای باارزشه. داکر این کالاها رو به همراه تمام وسایل مورد نیازشون (کد، دات‌نت ران‌تایم، کتابخانه‌ها، تنظیمات و...) داخل یه کانتینر استاندارد بسته‌بندی می‌کنه.
حالا این کانتینر روی هر کشتی‌ای (هر سیستمی که داکر روش نصبه، از ویندوز و مک گرفته تا لینوکس) بدون هیچ مشکلی بارگیری و اجرا میشه! این یعنی پایان تمام مشکلات وابستگی و تفاوت محیط‌ها.
🤔 خب، این به چه درد ما توسعه‌دهنده‌های #C می‌خوره؟

1️⃣ پایان مشکل "رو سیستم من کار می‌کرد":
اگه تو کانتینر کار کنه، همه جا کار می‌کنه. تمام!

2️⃣ محیط‌های یکسان (Development / Production Parity):
محیطی که شما روی لپ‌تاپتون کد می‌زنید، با محیط تست و سرور پروداکشن شما دقیقاً یکی میشه.

3️⃣ دیپلوی (Deploy) ساده و سریع:
دیگه نیازی به نصب چیزی روی سرور نیست. فقط کانتینر رو روی سرور می‌برید و با یک دستور اجراش می‌کنید.

4️⃣ معماری میکروسرویس:
داکر ستون فقرات معماری‌های مدرن میکروسرویسه و به شما اجازه میده هر سرویس رو در یک کانتینر ایزوله اجرا کنید.

5️⃣ توسعه چندسکویی:
به راحتی اپلیکیشن ASP.NET Core خودتون رو روی یک کانتینر لینوکس دولوپ و اجرا کنید،حتی اگه سیستم‌عامل خودتون ویندوزه.

یادگیری داکر دیگه یه آپشن نیست، یه مهارت ضروری برای هر توسعه‌دهنده مدرنه، به خصوص برای ماهایی که تو دنیای NET. و وب کار می‌کنیم.
شما از داکر تو پروژه‌هاتون استفاده می‌کنید؟ بزرگترین مزیتی که ازش دیدید چی بوده؟

🔖 هشتگ‌ها:
#Docker #DevOps #CSharp #DotNet #Backend #SoftwareArchitecture #Containerization
می‌تونید مشکل این کد رو پیدا کنید؟ 🧐

پیدا کردنش یه چیزه. چطور حلش می‌کنید؟

😈اینجا نقطه‌ایه که همه چیز خراب میشه:
شما یک کاربر رو در دیتابیس ذخیره می‌کنید.
بعد یه ایمیل خوش‌آمدگویی می‌فرستید.
و بعد یه پیام رو برای یه سرویس دیگه منتشر می‌کنید.
این‌ها سه تا عملیات مجزا هستن و هر کدومشون ممکنه شکست بخوره.
حالا سیستم شما در یک وضعیت عجیب و ناسازگار (inconsistent) قرار می‌گیره:
• کاربر ذخیره شده، ولی ایمیل هرگز ارسال نشده.
• یا پیام در صف قرار نگرفته.
• یا عملیات رو دوباره تکرار کردید و همه چیز دو بار انجام شده!
وضعیت به سرعت بهم‌ریخته و پیچیده میشه.

راه حل: الگوی Outbox 📬

الگوی Outbox به شما کمک می‌کنه از این مشکل جلوگیری کنید.
این الگو اینجوری کار می‌کنه:
1️⃣ یک تراکنش (transaction) دیتابیس رو شروع کنید.
2️⃣ کاربر رو ذخیره کنید.
3️⃣ قصدِ ارسال ایمیل و انتشار پیام رو در یک جدول به نام Outbox (صندوق خروجی) درج کنید.
4️⃣ تراکنش رو Commit کنید.
بعداً، یک سرویس پس‌زمینه (background service) این رکوردهای Outbox رو برمی‌داره و اون‌ها رو به صورت قابل اطمینان و با مکانیزم تلاش مجدد (retry) پردازش می‌کنه.

✨️ نتیجه نهایی:
حالا، تمام این مراحل با هم موفق میشن، یا هیچکدومشون انجام نمیشن.
عملیات نوشتن در دیتابیس شما اتمیک (atomic) هست. دلیلی نداره که جریان‌های کاری شما هم اتمیک نباشن.
🛠 متدهای مدرن در #C
Expression-bodied و Local Methods

سی‌شارپ مدرن، پر از قابلیت‌های شیک و کاربردیه که به ما کمک می‌کنه کدهای کوتاه‌تر، خواناتر و منظم‌تری بنویسیم. امروز با دو تا از این تکنیک‌های خفن برای کار با متدها آشنا میشیم.

1️⃣ متدهای Expression-bodied (=>): خداحافظی با return و {}

اگه متد شما فقط از یک عبارت (Expression) تشکیل شده، دیگه نیازی به نوشتن بلوک {} و کلمه کلیدی return ندارید! می‌تونید با استفاده از "فت ارو" (=>)، اون رو در یک خط بنویسید.

این کار، کد شما رو فوق‌العاده تمیز و مینیمال می‌کنه.
// روش قدیمی
int Double(int x)
{
return x * 2;
}

// روش مدرن و شیک با Expression-bodied
int DoubleModern(int x) => x * 2;

// این قابلیت برای متدهای void هم کار می‌کنه
void SayHello() => Console.WriteLine("Hello!");


2️⃣ متدهای محلی (Local Methods): متد در دل متد!

گاهی وقتا یه منطق کمکی دارید که فقط و فقط داخل یک متد دیگه استفاده میشه. به جای اینکه اون رو به عنوان یه متد private تو کل کلاس تعریف کنید و کلاس رو شلوغ کنید، می‌تونید اون رو به عنوان یه متد محلی، دقیقاً داخل همون متدی که بهش نیاز دارید، تعریف کنید.

مزایای این کار:

• کپسوله‌سازی عالی: اون متد کمکی، فقط همونجا قابل دسترسه و هیچ جای دیگه‌ای از کلاس رو آلوده نمی‌کنه.

• دسترسی به متغیرهای محلی: متد محلی می‌تونه به متغیرهای محلی و پارامترهای متد بیرونی دسترسی داشته باشه.
void WriteCubes()
{
Console.WriteLine(Cube(3));
Console.WriteLine(Cube(4));

// این متد، فقط داخل WriteCubes قابل دسترسه
int Cube(int value) => value * value * value;
}


💡نکته حرفه‌ای (static local methods):
از C# 8 به بعد، اگه متد محلی شما نیازی به دسترسی به متغیرهای متد بیرونی نداره، می‌تونید اون رو با کلمه کلیدی static تعریف کنید. این کار به کامپایلر کمک می‌کنه بهینه‌سازی‌های بهتری انجام بده و جلوی دسترسی‌های ناخواسته رو هم می‌گیره.

🤔 حرف حساب و تجربه شما

این تکنیک‌های مدرن، ابزارهای شما برای نوشتن کدهایی هستن که هم کار می‌کنن و هم خوندنشون لذت‌بخشه.

شما از کدوم یکی از این قابلیت‌ها بیشتر استفاده می‌کنید؟ آیا متدهای Expression-bodied جزو استایل کدنویسی شما هستن؟

🔖 هشتگ‌ها:
#CSharp #Programming #Developer #DotNet #CleanCode #ModernCSharp #BestPractices
🏛️ مونولیت ماژولار چیست؟ معماری هوشمندانه‌ای که قبل از میکروسرویس باید بشناسید
همیشه بحث بوده: سادگی و یکپارچگی مونولیت یا انعطاف‌پذیری و قدرت میکروسرویس؟ اما اگه یه راه سومی وجود داشته باشه که بهترین مزایای هر دو رو داشته باشه چی؟
با معماری مونولیت ماژولار (Modular Monolith) آشنا بشید؛ رویکردی که سادگی مونولیت رو با نظم و مرزبندی میکروسرویس ترکیب می‌کنه.

1️⃣ مونولیت ماژولار به زبان ساده چیه؟ 🧱
تصور کنید به جای ساختن یه ساختمون غول‌پیکر یک‌تکه (مونولیت سنتی)، یا چندین ساختمون کاملاً جدا از هم (میکروسرویس)، شما یک مجتمع آپارتمانی بزرگ می‌سازید.
کل مجتمع یک واحد یکپارچه است، ولی داخلش به واحدهای مستقل و ایزوله با دیوارهای محکم تقسیم شده. این واحدها همون ماژول‌ها هستن (مثلاً ماژول پرداخت، ماژول کاربران). هر ماژول کارکردهای مرتبط به خودش رو گروه‌بندی می‌کنه و مرزهای مشخصی داره.
💡نکته کلیدی: این واحدها (ماژول‌ها) اتصال سستی (loosely coupled) با هم دارن و فقط از طریق یک API عمومی و مشخص با هم صحبت می‌کنن، نه اینکه در جزئیات پیاده‌سازی هم دخالت کنن.


انعطاف‌پذیری در عمل: فرض کنید در فصل تعطیلات، ماژول رزرو شما نیاز به منابع بیشتری داره. در این معماری، شما می‌تونید موقتاً همون ماژول رو به صورت مستقل دیپلوی کنید و بعداً دوباره به مونولیت اصلی برش گردونید!
2️⃣ چرا "Monolith First"؟ (حتی به گفته بزرگان!) 💡
با اینکه میکروسرویس‌ها خیلی محبوبن، پیچیدگی‌های سیستم‌های توزیع‌شده (Distributed Systems) رو به همراه دارن. برای همین، خیلی از متخصصان، از جمله مارتین فاولر، میگن:

"شما نباید یک پروژه جدید را با میکروسرویس‌ها شروع کنید، حتی اگر مطمئن باشید که اپلیکیشن شما به اندازه‌ای بزرگ خواهد شد که این کار ارزشش را داشته باشد."
حتی گوگل هم در مقالات تحقیقاتی جدیدش، به ترند مونولیت ماژولار پیوسته. دلیل این امر، چالش‌های ذاتی میکروسرویس‌هاست:
1️⃣ پرفورمنس: سربار شبکه و سریال‌سازی داده‌ها، عملکرد رو کاهش میده.
2️⃣ صحت: تضمین صحت یک سیستم توزیع‌شده بسیار دشواره.
3️⃣ مدیریت: شما باید چندین اپلیکیشن مختلف با چرخه‌های انتشار متفاوت رو مدیریت کنید.
4️⃣ بعدی APIهای منجمد: تغییر APIها بدون شکستن سرویس‌های دیگه، سخت میشه.
5️⃣ سرعت توسعه: یک تغییر کوچک در یک سرویس، ممکنه نیازمند تغییر و هماهنگی در چندین سرویس دیگه باشه.

حالا که فهمیدیم مونولیت ماژولار چیه و چرا یک انتخاب هوشمندانه‌ست، در قسمت بعدی این سری به صورت عمیق به مزایای مشخص اون می‌پردازیم و اون رو مستقیماً با میکروسرویس‌ها مقایسه می‌کنیم.

🔖 هشتگ‌ها:
#SoftwareArchitecture #CSharp #DotNet #Monolith #Microservices #Developer #CleanCode
⚖️ دوئل معماری: مزایای مونولیت ماژولار در برابر میکروسرویس‌ها

حالا بیاید ببینیم این معماری در عمل چه مزایای ملموسی داره و تفاوت اصلیش با میکروسرویس‌ها چیه.

1️⃣ مهم‌ترین مزایای مونولیت ماژولار

🚀 استقرار ساده‌شده:
برخلاف میکروسرویس‌ها که نیازمند زیرساخت و استراتژی‌های پیچیده هستن، یک مونولیت ماژولار به سادگی و به عنوان یک واحد دیپلوی میشه.

⚡️ پرفورمنس بالا:
ارتباط بین ماژول‌ها به صورت درون-فرآیندی (in-process) و مستقیمه. این یعنی هیچ تأخیر شبکه یا سربار سریال‌سازی داده‌ای که در میکروسرویس‌ها وجود داره، اینجا نیست.

💻 سرعت توسعه بالا:
شما با یک پایگاه کد واحد سر و کار دارید. این کار دیباگ کردن، تست و تجربه کلی توسعه رو به شدت ساده و سریع می‌کنه.

🔗 مدیریت تراکنش آسان:
مدیریت تراکنش‌ها در سیستم‌های توزیع‌شده یه کابوس واقعیه. اما در مونولیت ماژولار، چون ماژول‌ها می‌تونن از یک دیتابیس مشترک استفاده کنن، این کار خیلی ساده‌تره.

🛠️ پیچیدگی عملیاتی کمتر:
شما فقط یک اپلیکیشن رو مدیریت، مانیتور و دیپلوی می‌کنید، نه ده‌ها سرویس مختلف رو.

🌱 مسیر هموار به سمت میکروسرویس:
اگه در آینده نیاز شد، به راحتی می‌تونید یه ماژول رو از ساختار اصلی جدا کنید و به یه سرویس مستقل تبدیلش کنید! این بزرگترین مزیت بلندمدت این معماریه.

2️⃣ مقایسه نهایی: مونولیت ماژولار در برابر میکروسرویس‌ها

بزرگترین تفاوت در نحوه استقرار (Deployment) اون‌هاست. میکروسرویس‌ها مرزهای منطقی داخل یه مونولیت ماژولار رو به مرزهای فیزیکی ارتقا میدن.
• مونولیت ماژولار به شما میده:
انسجام بالا، اتصال سست، کپسوله‌سازی داده و تمرکز بر کارکردهای بیزینسی.

•میکروسرویس‌ها به شما میده:
تمام موارد بالا + دیپلوی مستقل، مقیاس‌پذیری مستقل و توانایی استفاده از پشته‌های فناوری (tech stacks) مختلف برای هر سرویس.

3️⃣حرف آخر: نقل قول طلایی 🎯

همونطور که سایمون براون میگه:
"میکروسرویس‌ها را به خاطر مزایایشان انتخاب کنید، نه به این دلیل که پایگاه کد مونولیت شما یک فاجعه است."


مونولیت ماژولار به شما اجازه میده ابتدا خانه‌ی خود را مرتب کنید و بعداً تصمیم بگیرید که آیا واقعاً به چندین خانه جداگانه نیاز دارید یا نه.
🤔 نظر شما چیه؟
شما تو پروژه‌هاتون تجربه کار با مونولیت ماژولار رو داشتید؟ به نظرتون بزرگترین مزیت یا چالش این معماری چیه؟[منبع]

🔖 هشتگ‌ها:
#SoftwareArchitecture #CSharp #DotNet #Monolith #Microservices #Developer #CleanCode
🏗 سازنده‌ها (Constructors) در #C: معمار آبجکت‌های شما

هر آبجکتی در #C یه "لحظه تولد" داره. اون لحظه، کدی اجرا میشه که آبجکت رو برای زندگی آماده می‌کنه. به این کد جادویی میگن سازنده (Constructor).

سازنده، اولین متدیه که موقع ساختن یک نمونه جدید از کلاس با کلمه کلیدی new اجرا میشه. امروز می‌خوایم با تمام زیر و بم این معماران آبجکت آشنا بشیم.

1️⃣ سازنده چیست و چطور کار می‌کند؟

سازنده شبیه یک متده، با این تفاوت که اسمش دقیقاً هم‌اسم خود کلاس هست و هیچ نوع خروجی‌ای (حتی void) نداره. کار اصلیش، مقداردهی اولیه فیلدها و آماده‌سازی آبجکته.
public class Panda
{
string _name; // فیلد

// این سازنده است
public Panda(string name)
{
_name = name; // کد مقداردهی اولیه
}
}

// ... نحوه استفاده
Panda p = new Panda("Petey"); // سازنده اینجا صدا زده میشه


💡نکته حرفه‌ای: برای سازنده‌های تک‌خطی، می‌تونید از سینتکس Expression-bodied استفاده کنید:
public Panda(string name) => _name = name;
ترفند this: اگه اسم پارامتر با اسم فیلد یکی بود، می‌تونید با کلمه کلیدی this به فیلد کلاس اشاره کنید:
public Panda(string name) => this.name = name;

2️⃣ اورلودینگ و زنجیره‌ای کردن سازنده‌ها (this):

شما می‌تونید چندین سازنده با ورودی‌های مختلف (Overloading) داشته باشید. برای جلوگیری از تکرار کد، یک سازنده می‌تونه اون یکی رو با کلمه کلیدی : this(...) صدا بزنه.
public class Wine
{
public decimal Price;
public int Year;

public Wine(decimal price)
{
Price = price;
}

// این سازنده، قبل از اجرای بدنه خودش، سازنده بالایی رو صدا میزنه
public Wine(decimal price, int year) : this(price)
{
Year = year;
}
}


3️⃣ تله‌ی سازنده پیش‌فرض! ⚠️

قانون مهم: کامپایلر #C فقط و فقط زمانی که شما هیچ سازنده‌ای در کلاس تعریف نکرده باشید، یه سازنده عمومی بدون پارامتر ( ()public MyClass) براتون میسازه.

به محض اینکه شما حتی یک سازنده (با یا بدون پارامتر) بنویسید، اون سازنده اتوماتیک حذف میشه و اگه بهش نیاز دارید، باید خودتون دستی بنویسیدش. این یکی از خطاهای رایج برای تازه‌کارهاست!

4️⃣ ترتیب اجرا: اول فیلدها، بعد سازنده

اگه فیلدهاتون مقدار اولیه دارن، این مقداردهی همیشه قبل از اجرای اولین خط کد در سازنده و به ترتیب تعریفشون در کلاس انجام میشه.
class Player
{
int shields = 50; // این اول اجرا میشه
int health = 100; // این دوم اجرا میشه

public Player()
{
// این سوم اجرا میشه
Console.WriteLine("Constructor executed!");
}
}


5️⃣ کاربرد حرفه‌ای: سازنده‌های private

چرا باید یه سازنده رو private کنیم؟ برای اینکه کنترل کامل ساخت آبجکت رو به دست بگیریم! این الگو (که بهش Factory Method میگن) به ما اجازه میده که منطق ساخت رو داخل یه متد استاتیک قرار بدیم. مثلا برای پیاده‌سازی الگوی Singleton یا برگردوندن آبجکت از یک Pool.
public class MyApiClien
{
// سازنده خصوصیه، پس کسی از بیرون نمی‌تونه new کنه
private MyApiClient() { }

// فقط از طریق این متد استاتیک میشه نمونه ساخت
public static MyApiClient Create()
{
// ... منطق پیچیده برای ساخت و کانفیگ ...
return new MyApiClient();
}
}

🔖 هشتگ‌ها:
#CSharp #Programming #Developer #DotNet #OOP #Constructor #BestPractices
🚀 کشینگ در ASP.NET Core (قسمت ۱):

مبانی و چرا باید اهمیت بدیم؟
یکی از ساده‌ترین و در عین حال قدرتمندترین تکنیک‌ها برای افزایش چشمگیر پرفورمنس اپلیکیشن شما، کشینگ (Caching) هست.

کشینگ یعنی ذخیره موقت داده‌ها در یک محل سریع‌تر (معمولاً حافظه RAM). شما معمولاً نتایج عملیات‌های سنگین یا داده‌هایی که به صورت مکرر بهشون نیاز دارید رو کش می‌کنید تا در درخواست‌های بعدی، به جای مراجعه به منبع اصلی (مثل دیتابیس)، مستقیماً از کش استفاده کنید.

امروز قسمت اول از مینی-سریال جدیدمون رو شروع می‌کنیم و به مبانی این موضوع حیاتی می‌پردازیم.

1️⃣ کشینگ چطور پرفورمنس رو بهبود میده؟ ⚡️

🚀 دسترسی سریع‌تر به داده‌ها:

داده‌های کش شده از حافظه RAM خوانده میشن که هزاران برابر سریع‌تر از دیتابیس یا یک API خارجیه.

📉 کاهش بار دیتابیس:

با کش کردن داده‌هایی که زیاد خونده میشن، تعداد کوئری‌ها به دیتابیس به شدت کم میشه و فشار از روی دیتابیس برداشته میشه.

🧠 استفاده کمتر از CPU:

جلوی پردازش‌های تکراری و سنگین (مثل ساختن یک صفحه وب پیچیده) رو می‌گیره.

💪 مقیاس‌پذیری بیشتر:

برنامه شما می‌تونه ترافیک بیشتری رو با منابع کمتر مدیریت کنه و به کاربران بیشتری سرویس بده.

2️⃣ ابزارهای کشینگ در ASP.NET Core 🛠

حالا ASP.NET Core دو تا ابزار (اینترفیس) اصلی برای کار با کش در اختیار ما میذاره:

• IMemoryCache:

برای کشینگ درون-حافظه‌ای (In-Memory). داده‌ها رو در حافظه RAM همون سروری که اپلیکیشن روش اجرا شده، ذخیره می‌کنه. استفاده ازش خیلی ساده‌ست ولی برای سناریوهایی که چند سرور دارید، مناسب نیست.

• IDistributedCache:

برای کشینگ توزیع‌شده (Distributed). به شما اجازه میده داده‌های کش رو در یک سرور خارجی مثل Redis ذخیره کنید تا بین چندین نمونه (instance) از اپلیکیشن شما به اشتراک گذاشته بشه.

🔖 هشتگ‌ها:
#ASPNETCore #Caching
🚀 کشینگ در ASP.NET Core (قسمت ۲):

شیرجه عمیق در IMemoryCache
در قسمت اول با مبانی کشینگ آشنا شدیم. حالا وقتشه آستین‌ها رو بالا بزنیم و به صورت عملی، اولین و ساده‌ترین نوع کشینگ در ASP.NET Core یعنی کشینگ درون-حافظه‌ای (In-Memory) رو با IMemoryCache پیاده‌سازی کنیم.

1️⃣ قدم اول: فعال‌سازی سرویس

مثل خیلی از قابلیت‌های دیگه در ASP.NET Core، اول باید سرویس IMemoryCache رو در فایل Program.cs به اپلیکیشن خودمون اضافه کنیم تا بتونیم اون رو از طریق Dependency Injection (تزریق وابستگی) همه جا استفاده کنیم.
var builder = WebApplication.CreateBuilder(args);

// اضافه کردن سرویس کشینگ درون-حافظه‌ای
builder.Services.AddMemoryCache();

// ... بقیه تنظیمات


2️⃣ قدم دوم: الگوی پیاده‌سازی (Try-Get-Set)

الگوی استاندارد برای کار با کش خیلی ساده‌ست:

• امتحان کن (Try): سعی کن داده رو از کش بگیری.

• بگیر (Get): اگه تو کش بود، همونو برگردون.

• تنظیم کن (Set): اگه تو کش نبود، از منبع اصلی (مثل دیتابیس) بگیر و برای دفعه‌های بعدی، تو کش ذخیره کن.

این الگو رو در یک Minimal API ببینیم:
app.MapGet("products/{id}", 
(int id, IMemoryCache cache, AppDbContext context) =>
{
// ۱. سعی می‌کنیم محصول رو از کش با کلید id بخونیم
if (!cache.TryGetValue(id, out Product product))
{
// ۲. اگه تو کش نبود (Cache Miss)، از دیتابیس می‌خونیم
product = context.Products.Find(id);

// ۳. داده رو در کش ذخیره می‌کنیم تا دفعه بعد استفاده بشه
// (گزینه‌های انقضا رو در بخش بعدی توضیح میدیم)
var cacheOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(5));

cache.Set(id, product, cacheOptions);
}

// ۴. داده رو (چه از کش، چه از دیتابیس) برمی‌گردونیم
return Results.Ok(product);
});


3️⃣ قدم سوم: مدیریت انقضای کش (Cache Expiration) ⏱️

داده‌ها نباید برای همیشه تو کش بمونن، چون ممکنه در دیتابیس تغییر کنن و کهنه (stale) بشن. ما باید به کش بگیم که چه زمانی این داده‌ها رو دور بریزه. دو تا سیاست اصلی برای این کار وجود داره:

✨️انقضای مطلق (Absolute Expiration):
یه تاریخ انقضای مشخص تعیین می‌کنه. مثلاً "۱۰ دقیقه دیگه، چه کسی از این داده استفاده کرد یا نکرد، حذفش کن."
.SetAbsoluteExpiration(TimeSpan.FromMinutes(10))

✨️انقضای لغزنده (Sliding Expiration):
یه بازه زمانی عدم فعالیت تعیین می‌کنه. مثلاً "اگه تا ۲ دقیقه کسی به این داده دست نزد، حذفش کن. ولی اگه کسی ازش استفاده کرد، عمرش رو ۲ دقیقه دیگه تمدید کن."
.SetSlidingExpiration(TimeSpan.FromMinutes(2))

🔖 هشتگ‌ها:
#ASPNETCore #Caching
🚀 کشینگ در ASP.NET Core (قسمت ۳):

الگوی حرفه‌ای Cache-Aside
در قسمت قبل، با IMemoryCache آشنا شدیم. عالی بود، ولی یه مشکل بزرگ داشت: اگه چند تا سرور داشته باشیم، کش بینشون به اشتراک گذاشته نمیشه.

امروز می‌خوایم با الگوی Cache-Aside و اینترفیس IDistributedCache آشنا بشیم تا این مشکل رو حل کنیم و کدهامون رو برای کشینگ، خیلی تمیزتر و قابل استفاده مجدد کنیم.

1️⃣ الگوی Cache-Aside چیست؟ 🤓

این الگو، رایج‌ترین و استانداردترین استراتژی برای کار با کشه. منطقش خیلی ساده‌ست:

• اول کش رو چک کن: برنامه شما اول به کش نگاه می‌کنه.

• اگه تو کش بود، برش گردون: اگه داده اونجا بود (Cache Hit)، کار تمومه.

• اگه نبود، برو سراغ منبع اصلی: اگه داده تو کش نبود (Cache Miss)، برو از منبع اصلی (مثل دیتابیس) بخونش.

• کش رو آپدیت کن و برگردون: داده‌ای که از منبع اصلی گرفتی رو تو کش ذخیره کن تا برای دفعه بعد آماده باشه و بعد به کاربر برگردون.

2️⃣ ساخت یک ابزار حرفه‌ای: متد توسعه GetOrCreateAsync 🛠

به جای اینکه این منطق ۴ مرحله‌ای رو هر بار تکرار کنیم، می‌تونیم یه متد توسعه (Extension Method) خفن برای IDistributedCache بنویسیم که این کار رو برامون انجام بده.
public static class DistributedCacheExtensions
{
// یه زمان انقضای پیش‌فرض تعریف می‌کنیم
public static DistributedCacheEntryOptions DefaultExpiration => new()
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(2)
};

public static async Task<T> GetOrCreateAsync<T>(
this IDistributedCache cache,
string key,
Func<Task<T>> factory, // تابعی که قراره داده رو از منبع اصلی بگیره
DistributedCacheEntryOptions? options = null)
{
var cachedData = await cache.GetStringAsync(key);
if (cachedData is not null)
{
// اگه داده تو کش بود، از JSON برش می‌گردونیم
return JsonSerializer.Deserialize<T>(cachedData);
}

// اگه نبود، تابع factory رو اجرا می‌کنیم تا از منبع اصلی بگیره
var data = await factory();

// داده جدید رو به صورت JSON در کش ذخیره می‌کنیم
await cache.SetStringAsync(
key,
JsonSerializer.Serialize(data),
options ?? DefaultExpiration);

return data;
}
}


3️⃣ نحوه استفاده از ابزار جدید

حالا ببینید اون کد شلوغ قبلی، با این متد توسعه چقدر تمیز و خوانا میشه:
app.MapGet("products/{id}", 
async (int id, IDistributedCache cache, AppDbContext context) =>
{
// فقط کافیه متد خودمون رو صدا بزنیم!
var product = await cache.GetOrCreateAsync($"products-{id}", async () =>
{
// این تابع فقط زمانی اجرا میشه که داده تو کش نباشه
return await context.Products.FindAsync(id);
});

return Results.Ok(product);
});


💡نکته: برای اینکه این کد کار کنه، باید اول سرویس IDistributedCache رو در Program.cs ثبت کنیم:
builder.Services.AddDistributedMemoryCache();

🔖 هشتگ‌ها:
#ASPNETCore #Caching