Developer Advocate
Ir al canal en Telegram
1 035
Suscriptores
Sin datos24 horas
Sin datos7 días
-130 días
Archivo de publicaciones
\n\n\nنکات فنی حرفهای:\n- هش باید با الگوریتم قوی (ترجیحاً SHA-384 یا SHA-512) تولید شود.\n- مقدار crossorigin معمولاً روی anonymous قرار میگیرد، مگر اینکه به کوکی یا credentials نیاز باشد.\n- اگر فایل تغییر کند (مثلاً CDN آپدیت شود)، SRI mismatch رخ میدهد و مرورگر آن را reject میکند؛ پس SRI را همیشه با نسخه فایل sync کنید.\n- ابزار تولید SRI: از دستور زیر جهت تولید Hash برای فایل دلخواه استفاده کنید:\nopenssl dgst -sha384 -binary luxon.min.js | openssl base64 -A\n\n\nاین رویکرد در پروژههایی که CDN سومشخص استفاده میشود، امنیت جدی اضافه میکند و بهویژه در تیمهایی که به امنسازی supply chain اهمیت میدهند، ضروری است.\n\n#Security #Frontend #SRI #CDN\n\n@DeveloperAdvocate 🥑","datePublished":"2025-06-20T17:30:59Z","dateModified":"2025-06-20T17:30:59Z","author":{"@type":"Organization","name":"Developer Advocate","url":"https://telemetr.io/es/channels/1781873126-developeradvocate","image":"https://img.tlmtr.io/c/1WAyB8/5938065502930847822?ty=x"},"publisher":{"@type":"Organization","name":"Developer Advocate","url":"https://telemetr.io/es/channels/1781873126-developeradvocate","image":"https://img.tlmtr.io/c/1WAyB8/5938065502930847822?ty=x"},"commentCount":0,"interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/ViewAction","userInteractionCount":46},{"@type":"InteractionCounter","interactionType":"https://schema.org/ShareAction","userInteractionCount":1}]}},{"@type":"ListItem","position":20,"item":{"@type":"SocialMediaPosting","@id":"https://telemetr.io/es/channels/1781873126-developeradvocate/posts/2203","url":"https://telemetr.io/es/channels/1781873126-developeradvocate/posts/2203","mainEntityOfPage":"https://telemetr.io/es/channels/1781873126-developeradvocate/posts/2203","headline":"وقتی صحبت از «Virtual DOM» در React میشود، بهتر است آن را با مدل سنتی مستقیمنویسی DOM مقایسه کنیم. تغییرات…","articleBody":"وقتی صحبت از «Virtual DOM» در React میشود، بهتر است آن را با مدل سنتی مستقیمنویسی DOM مقایسه کنیم. تغییرات روی DOM مرورگر، ذاتاً عملیات سنگینی هستند؛ برای هر آپدیت، مرورگر باید ساختار درختی DOM را پیمایش، محاسبهٔ layout و رندر مجدد المانها را انجام دهد. این نقطه ضعف باعث میشود اپلیکیشنهای تعاملی کند و سنگین شوند.\n\nمعادله با Virtual DOM فرق میکند. React به جای دستزدن مستقیم به DOM، ابتدا تغییرات را در یک نسخهٔ انتزاعی و سبک از DOM (همان Virtual DOM) اعمال میکند؛ این یک درخت جاوااسکریپتی است که تقریباً شبیه DOM واقعی اما بسیار سریعتر پیمایش و پردازش میشود. حالا، هر بار وضعیت (state) یا props عوض میشود، React یک Virtual DOM جدید میسازد و با نسخه قبلی diff میکند (عملیات Diffing). نتیجه: فقط «تفاوتها» پیدا میشوند.\n\nخلاصه: React فقط تغییرات ضروری (minimal set of updates) را روی DOM واقعی به کمک الگوریتمی مثل Reconciliation اعمال میکند. این مکانیزم باعث میشود حتی در پیجهای بسیار تعاملی یا صفحات با هزاران نود، عملکرد چشمگیری تجربه کنید.\n\nجمعبندی برای آشنایی عمیقتر: اگر روزی کرت میل داشتید همین pattern را در سطح #ASP.NET Core پیادهسازی کنید—مثلاً هنگام ساخت یک موتور رندرینگ سمت کلاینت یا SSR—میتوانید الگوریتم diff را با data structureهایی مثل tree و queue تقویت کنید. بخش عمدهای از موفقیت رندرفریمورکها دقیقا در «بهینهسازی حداقلی تغییرات روی DOM» نهفته است؛ یعنی جایی که معماری front و back strong ذکر میشوند.\n\n@DeveloperAdvocate 🥑","datePublished":"2025-06-20T16:30:53Z","dateModified":"2025-06-20T16:30:53Z","author":{"@type":"Organization","name":"Developer Advocate","url":"https://telemetr.io/es/channels/1781873126-developeradvocate","image":"https://img.tlmtr.io/c/1WAyB8/5938065502930847822?ty=x"},"publisher":{"@type":"Organization","name":"Developer Advocate","url":"https://telemetr.io/es/channels/1781873126-developeradvocate","image":"https://img.tlmtr.io/c/1WAyB8/5938065502930847822?ty=x"},"commentCount":0,"interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/ViewAction","userInteractionCount":36}]}}]}


















1 035
📊 پایگاههای داده گرافی؛ کجا نور را میتابانند؟
تا امروز با SQL و حتی NoSQL برای مدلسازی داده سر و کار داشتهاید، اما وقتی به شبکهای پیچیده از ارتباطات میرسیم—مثلا تحلیل شبکههای اجتماعی، توصیهگرها، شبکه حملونقل یا کشف تقلب—دیتابیسهای سنتی ناکارآمد میشوند. Graph Databaseهایی مثل Neo4j دقیقا برای همین سناریو هستند.
در پایگاه داده گراف، نودها (Nodes) موجودیتها و یالها (Relationships) ارتباطات را مدل میکنند. مثلا شبکهای از کاربران، دوستان و تعاملات را به راحتی میتوان مدل کرد:
فرض کنید دنبال «کوتاهترین مسیر معرفی» بین دو کاربر در یک شبکه اجتماعی هستید (“دوستانِ دوستانِ دوست…”). کوئریای که در SQL یک جهنم recursive است، در Neo4j به سادگی زیر قابل بیان است:
MATCH p=shortestPath((a:User {id:'A1'})-[:FRIEND*]-(b:User {id:'B9'}))
RETURN p
این ساختار به شکلی نزدیک به مغز انسان ارتباطات را مدل میکند و قدرتی باورنکردنی به الگوریتمهای مسیریابی، پیشنهادگرها و کشف جوامع میدهد.
اگر به هر نوع فرآیند تحلیل داده با وابستگیهای قابل ارائه به گراف برخوردهاید—مثلا شناسایی حلقههای ارتباط مشکوک در تراکنش مالی (Anti-Money Laundering)—گراف دیتابیس راهکار ایدهآل شماست.
در پروژههای #.NET، میتوانید با کتابخانه Neo4jClient به راحتی با Neo4j ارتباط بگیرید:
var client = new BoltGraphClient(new Uri("bolt://localhost:7687"), "neo4j", "password");
await client.ConnectAsync();
// جستجوی دوستان یک کاربر
var result = await client.Cypher
.Match("(user:User)-[:FRIEND]->(friend:User)")
.Where((User user) => user.Id == "A1")
.Return(friend => friend.As<User>())
.ResultsAsync;
📎اگر گرهها و ارتباطات اولین دغدغهی مدل دادههای شماست و میخواهید الگوریتمهای پویای گراف را به سرعت و مقیاس بالا اجرا کنید، Graph Databaseها مسیر را برایتان روشن میکنند.
@DeveloperAdvocate 🥑1 035
👾 امنیت پکیجمنیجرها - حمله Dependency Confusion و راهکارها
یکی از تهدیدات مدرن برای پروژههای .NET (و اکثر اکوسیستمهای وابسته به پکیجمنیجر) حمله Dependency Confusion است. ایده ساده است: فرض کنید پروژه شما همزمان به ریجستری خصوصی (مثلاً یک سرور Azure Artifacts یا Nexus داخلی) و ریجستری عمومی (مثل NuGet.org) وصل است. حال اگر یکی از پکیجهایی که فقط در ریجستری داخلی دارید، روی ریجستری عمومی با همان نام ولی نسخه بالاتر Publish شود و در فایل csproj یا NuGet.config هیچ محدودیتی برای Sourceها نگذارید، پکیجمنیجر به طور پیشفرض نسخه تازهتر (ولو از سورس public) را دانلود و نصب میکند. این رخداد فرصت تزریق کد مخرب به Supply Chain پروژه را به مهاجم میدهد.
🔒 راهکارهای عملی:
1. Source Pinning/Scoping: مسیر جستجو و نصب پکیجها را به صورت Explicit تعیین کنید. برای NuGet:
- فقط سورس داخلی را برای پکیجهای حساس تعریف کنید.
- از ویژگی
PackageSourceMapping در NuGet 5.9+ استفاده کنید:
<packageSourceMapping>
<packageSource key="PrivateFeed">
<package pattern="YourCompany.*" />
</packageSource>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
2. Publish پکیجهای خصوصی به ریجستری عمومی را ممنوع کنید: هرگز حتی نام پکیجهای اختصاصی خود را 공개 نکنید.
3. افزودن نسخهبندی خاص: از Convention نامگذاری اختصاصی برای پکیجهای داخلی (پیشوند خاص، nonsemver versioning) استفاده کنید تا احتمال یافتن تصادفی نام در ریجستری بیرونی به صفر برسد.
4. مانیتورینگ ریجستریهای عمومی: ابزارهایی مثل oss-detect-backdoor یا سرویسهای سکیوریتی مبتنی بر ریجستریها را اعمال کنید تا اگر کسی نام پکیجهای شرکتتان را بیرون پابلیش کرد، سریع مطلع شوید.
5. فریز کردن lockfile: در سیستمهایی که lockfile مثل packages.lock.json دارید، آن را version-controlled نگه دارید و فقط یا Approval خاص بروزرسانی کنید.
پیادهسازی صحیح این راهکارها، دیوار دفاعی محکمی در برابر Dependency Confusion میسازد—آسیبی که حتی شرکتهای بزرگی چون Microsoft و Apple را هم هدف قرار داده است.
@DeveloperAdvocate 🥑1 035
📌 پستمورتِم بلِیملِس در DevOps: الگوی پیشنهادی برای یادگیری عمیق از رخدادهای پروداکشن
وقتی یک Incident در پروداکشن رخ میدهد، بهترین شرکتها رویکرد Blameless Postmortem را اتخاذ میکنند: تمرکز کامل بر پروسهها و سیستمها، نه سرزنش افراد. این الگوی پیشنهادی را میتوانید در تیم خود پیاده کنید:
🔸 ۱. گردآوری فکتها بدون تفسیر
در نخستین قدم، تایملاین محدودی از رخدادها استخراج کنید: شامل لاگها، دیپلویها، تغییرات کانفیگ، alertها و رخدادهای محیطی.
🔸 ۲. شفافیت درباره Impact
برای مثال، کدام مشتریان یا سرویسها متاثر شدند؟ چه میزان Downtime یا Loss رخ داده است؟ سعی کنید با دیتا اثباتشده صحبت کنید.
🔸 ۳. آنالیز علت ریشهای (Root Cause Analysis)
بهجای پیدا کردن مقصر، چرا ۵ مرتبه بپرسید (۵ Whys): چرا این اتفاق رخ داد... چرا مکانیزم Failover فعال نشد... چرا مانیتورینگ هشدار نداد و غیره.
🔸 ۴. DRY RUN رخداد برای بازآفرینی دقیق سناریو
چه اقداماتی یا هشدارهایی میتوانست از Incident جلوگیری کند؟ اینجا میتوانید از Chaos Engineering ایده بگیرید و شرایط مشابه را شبیهسازی کنید.
🔸 ۵. تدوین Action Items قابل اندازهگیری
دستکم یک Improvement فنی/پروسهای با Metric قابل سنجش تعریف شود (مثلاً افزودن alert به Application Insights یا نوشتن تست Integration جدید):
// Sample AlertQuery برای Application Insights در Azure Monitor
traces
| where severityLevel >= 3
| where customDimensions["Service"] == "Order-Api"
| project timestamp, message, operation_Name, customDimensions
🔸 ۶. اشتراکگذاری شفاف با تیم و Organization
تمام یافتهها و Actionها باید به صورت مستندسازیشده ویرایش و در دسترس کل تیم قرار گیرد (مثلاً در Confluence یا یک repo مستقل)، با تاکید روی Lessons Learned.
🟢 تیمهایی که فرهنگ Blameless Postmortem را جا میاندازند، سریعتر از رخدادها عبور میکنند، اجزای سیستم را مقاومتر میسازند و بهبودهای کوچک اما مکرر ایجاد میکنند—و این یعنی DevOps واقعی.
@DeveloperAdvocate 🥑1 035
📚 مدلسازی داده در MongoDB برای سیستم سفارش آنلاین (Order Management)
در سیستمهای مدیریت سفارش (Order Management)، یکی از چالشهای جدی انتخاب ساختار بهینه برای ذخیره سفارشات، مشتریان و آیتمهای هر سفارش است. بیایید یک سناریو واقعگرایانه را بررسی کنیم و مدلسازی مطلوب با MongoDB را تحلیل نماییم.
🔹 نیازمندیها
- هر سفارش متعلق به یک مشتری است
- هر سفارش شامل چندین آیتم (کالا) است
- آیتمها معمولا پس از ثبت سفارش ویرایش نمیشوند (immutable)
- اطلاعات مشتری ممکن است تغییر کند (مثلا تغییر آدرس)
🔹 مدل پیشنهادی: Embedding و Referencing ترکیبی
در این حالت بهتر است آیتمهای سفارش داخل سند Order (Embed)، و اطلاعات مشتری به صورت رفرنس (Reference) ذخیره شوند:
// orders collection
{
"_id": ObjectId("..."),
"customerId": ObjectId("..."),
"orderDate": ISODate("2024-06-21T10:00:00Z"),
"status": "Pending",
"items": [
{
"productId": ObjectId("..."),
"productName": "Logitech MX Master 3S",
"quantity": 2,
"unitPrice": 1200000
},
{
"productId": ObjectId("..."),
"productName": "Keychron K8 Pro",
"quantity": 1,
"unitPrice": 3700000
}
],
"totalAmount": 6100000
}
// customers collection
{
"_id": ObjectId("..."),
"fullName": "داریوش رضایی",
"contact": {
"email": "daryoush@example.com",
"phone": "0912xxxxxxx"
},
"addresses": [
{
"type": "home",
"city": "تهران",
"street": "مطهری",
"postalCode": "1569991111"
}
]
}
🔹 دلایل انتخاب این مدل
- هر تغییر روی آیتمهای سفارش نیازمند update کل سند نیست چون پس از ثبت، آیتمها ثابت میمانند (Immutability).
- ایزولهشدن دادههای مشتری (در صورت تغییر، سفارشهای قدیمی دچار ناسازگاری نمیشوند).
- کوئریهای رایج (جستجو سفارش یک مشتری یا لیست آیتمها در یک سفارش) بسیار سریع انجام میشود.
🔹 نمونه Query برای ساخت سفارش در .NET:
var orderCollection = database.GetCollection<Order>("orders");
var order = new Order
{
CustomerId = customerId,
OrderDate = DateTime.UtcNow,
Status = "Pending",
Items = new List<OrderItem>
{
new OrderItem { ProductId = pid1, ProductName = "Logitech MX Master 3S", Quantity = 2, UnitPrice = 1_200_000 },
new OrderItem { ProductId = pid2, ProductName = "Keychron K8 Pro", Quantity = 1, UnitPrice = 3_700_000 }
},
TotalAmount = 6_100_000
};
await orderCollection.InsertOneAsync(order);
🔹 نکته معمارانه:
در MongoDB همیشه باید به الگوی دسترسی فکر کنید. اگر فرضاً آیتمهای سفارش نیازمند تغییرات متعدد (مانند پردازش تکی ارسال یا بازگشت) بودند، Normalization (مدلسازی مبتنی بر Reference) توصیه میشود؛ ولی با فرض سناریو بالا، Embedding ساده و performant است.
#MongoDB #NoSQL #DataModeling #Architecture
@DeveloperAdvocate 🥑1 035
یک استراتژی شخصی برای بهروز ماندن با اخبار تکنیکال: هر هفته، فیدهای RSS منابع منتخب خودم را با استفاده از Inoreader یا Feedly جمعآوری میکنم. اما هدف فقط «اسکن» سریع تیترهاست، نه خواندن همه مطالب. برای فیلتر کردن محتوا، قوانین کاستوم تعریف کردهام—مثلاً هر خبری که شامل کلیدواژههایی مثل ".NET 9", "C# 13", یا "Roslyn" باشد، به لیست ویژه «Deep Dive» اضافه میشود. برای پیگیری اخبار عمیقتر (مانند RFCها، Breaking Changes و بررسیهای performance)، از قابلیت Saved Searches و حتی IFTTT بهمنظور ترایگر کردن نوتیفیکیشن اختصاصی در تلگرام بهره میبرم. رمز موفقیت اینجاست: تنها زمانی سراغ این لیستها میروم که یک پروژه واقعی دارم تا مفاهیم جدید را بلافاصله روی آن تست کنم—هیچچیز مثل لمس Real-World Use Case، باعث تثبیت عمیق دانش نمیشود.
یک نمونه از اتوماسیون فیلترینگ را میتوانید با این snippet ببینید که چطور عناوین خبرهای فنی را با LINQ پالایش میکنم:
var technicalKeywords = new[] {".NET", "C#", "Roslyn", "performance", "runtime"};
var recentNews = allRssItems
.Where(item => technicalKeywords.Any(kw => item.Title.Contains(kw, StringComparison.OrdinalIgnoreCase)))
.OrderByDescending(item => item.PublishDate)
.Take(10)
.ToList();
این استراتژی به شما اجازه میدهد اطلاعات ارزشمند را از نویز دیجیتال جدا کنید و عمق یادگیری خود را با تمرکز عملی افزایش دهید.
@DeveloperAdvocate 🥑1 035
در تایپاسکریپت پیشرفته، کنترل دقیق ساختار آبجکتها با استفاده از انواع جنریک، یکی از کلیدهای افزایش اطمینان و خوانایی کد است. فرض کنید میخواهید فقط آبجکتهایی با یک کلید خاص (مثلاً name، type و مواردی از این دست) و همچنین یک سری مقادیر با نوع خاص قابل قبول باشند. برای این کار میتوانیم از ترکیب mapped types و generic constraints استفاده کنیم. مثال زیر، نوعی جنریک به نام
EnforceObjectShape را نشان میدهد که یک ساختار خاص را الزامآور میکند:
// نوع جنریک که ساختار خاصی را enforce میکند.
type EnforceObjectShape<Keys extends string, Value> = {
[K in Keys]: Value
}
// استفاده: فقط آبجکتهایی دارای کلیدهای "name" و "age" با مقدار رشتهای معتبر است:
type Person = EnforceObjectShape<'name' | 'age', string>
// ✔️ درست
const a: Person = { name: 'Ali', age: '32' }
// ❌ خطا: کلید ناشناخته، یا مقدار ناصحیح
const b: Person = { name: 'Sara', age: 30 }
// خطا: Type 'number' is not assignable to type 'string'
const c: Person = { name: 'Reza' }
// خطا: Property 'age' is missing
استفاده از این الگو، در طراحی مدلهای پایدار (domain models)، API responses یا data contracts کمک شایانی میکند و مانع از نوشتن نوعهای تکراری و گسترشناپذیر میشود. این تکنیک به سادگی قابلیت ترکیب با utility types پیچیدهتر مانند Partial, Pick، یا حتی استخراج از اینترفیسها (مثلاً keyof MyInterface) را هم دارد. این یک گام به سمت Type Safety مقیاسپذیر و قابل اتکا در فرانتاندهای سازمانی است.
@DeveloperAdvocate 🥑1 035
🚨 نکته امنیتی حیاتی در کار با JWT:
هرگز اطلاعات حساس (مانند رمز عبور یا اطلاعات شخصی شناساییشده) را در payload توکن JWT قرار ندهید—even اگر توکن شما امضا شده باشد! امضای JWT تضمین میکند که محتوا تغییری نکرده، ولی رمزنگاریشده نیست و هرکسی که توکن را داشته باشد میتواند payload را (که base64-encoded است) بخواند.
برای افزایش امنیت:
۱. از ارسال هرگونه داده حساس در Claims خودداری کنید.
۲. زمان انقضای (exp) توکن را کوتاه نگه دارید.
۳. در صورت نیاز به محافظت از محتوا، به جای JWT معمولی از JWE (JWT رمزنگاریشده) یا کوکی HttpOnly استفاده کنید.
اگر در ASP.NET Core JWT Handler استفاده میکنید، تعیین expire را فراموش نکنید:
var token = new JwtSecurityToken(
issuer: "your-issuer",
audience: "your-audience",
claims: claims,
expires: DateTime.UtcNow.AddMinutes(15), // زمان انقضای کوتاه
signingCredentials: credentials
);
یادآوری: امضای JWT ≠ رمزنگاری داده! همیشه با فرض دیده شدن payload، آن را طراحی کنید.
@DeveloperAdvocate 🥑1 035
🎯 تضمین کیفیت کد با Roslyn Analyzers
استفاده از Roslyn Analyzers در پروژههای #CSharp چیزی فراتر از یک توصیه است—در پروژههای حرفهای باید این ابزار را الزاماً فعال کنیم. پردازشگرهای Roslyn در لایه کامپایلر قرار میگیرند و اجازه میدهند خطاها و کاستیهای کدنویسی، زودتر و بهصورت کاملاً اتوماتیک شناسایی شوند؛ چه با هدف enforce کردن coding style تیم (Naming, Formatting...) و چه الزام best practices (مانند IDisposable pattern یا استفادهی بهجای async).
🔬 راهاندازی سریع:
در فایل csproj این سطر را اضافه کنید:
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.2" PrivateAssets="All" />
</ItemGroup>
یا برای پروژههای مدرن از analyzers پیشفرض .NET استفاده کنید. همچنین با افزودن بستههای مخصوص (مانند StyleCop.Analyzers) قابلیت شخصیسازی بیشتر خواهید داشت.
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0" PrivateAssets="All" />
</ItemGroup>
⚡️ توصیه حرفهای:
الگوهای مورد نظر را در فایل EditorConfig پیادهسازی کنید؛ مثلاً:
dotnet_diagnostic.IDE0060.severity = warning dotnet_diagnostic.SA1300.severity = errorبدین شکل، هر dev به محض commit یا هنگام build دقیقاً خطاها و انحرافها را میبیند. حرفهایتر زمانی میشوید که custom analyzerهای تیم خود را بنویسید (برای enforce کردن business rules در لایه code). 👁🗨 کد تمیز، وحدت سبک و باگهای کمتر—Roslyn Analyzers یک انتخاب نیست، یک الزام است! @DeveloperAdvocate 🥑
1 035
در انتخاب بین SQL، NoSQL و NewSQL، عموماً بهترین مسیر، ارزیابی ویژگیهای پروژه و نیازمندیهای دقیق است. تصمیمگیری اشتباه میتواند منجر به تکنیکال دِبت جدی شود. درخت تصمیم زیر را برای انتخاب صحیح در نظر بگیرید و بر مبنای آن عمل کنید:
➖ ۱. آیا دادههای شما ساختارمند (Structured)، با اسکیما مشخص و قابل تغییر کمتعداد است؟
◽️ بله ⇨ SQL (PostgreSQL, SQL Server, MySQL)
◽️ خیر ⇨ برو به ۲
➖ ۲. آیا نیازمند تراکنشهای ACID و Consistency سطح بالا هستید؟
◽️ بله ⇨ SQL یا NewSQL
◾ اگر اسکیل عمودی کافی است و توزیعپذیری حیاتی نیست ⇨ SQL
◾ اگر به اسکیل افقی و توزیعپذیری نیاز دارید ⇨ NewSQL (CockroachDB, Google Spanner)
◽️ خیر ⇨ برو به ۳
➖ ۳. آیا نوع دادهها بسیار متنوع و پویا است (بدون اسکیما، یا اسکیما منعطف مثل JSON، Graph و...)؟
◽️ بله ⇨ NoSQL
◾ Document Store ⇨ MongoDB, Couchbase
◾ Key-Value ⇨ Redis, DynamoDB
◾ Wide-Column ⇨ Cassandra, HBase
◾ Graph ⇨ Neo4j, ArangoDB
◽️ خیر ⇨ برو به ۴
➖ ۴. آیا میخواهید در آینده حجم داده یا بار سیستم را بهصورت افقی (Sharding، Replication) اسکیل کنید؟
◽️ بله ⇨ NewSQL یا NoSQL
(اگر Consistency مهم است ⇨ NewSQL)
(اگر Eventual Consistency قابل قبول است ⇨ NoSQL)
◽️ خیر ⇨ SQL
✨ نکات عمیق معماری:
- اگر از الگوهای CQRS/Event Sourcing استفاده میکنید، ممکن است ترکیب SQL (برای Read) و NoSQL (برای Write) مفید باشد.
- در سناریوهای Real-Time (مانند چت یا بازی)، NoSQL (مانند Redis یا Cassandra) پاسخدهی بهتری دارد.
- وقتی قراردادهای دادهای سختگیرانه دارید و Queryهای پیچیده مورد نیاز است (مثل JOIN، CTE و Window Functions)، پایگاههای SQL بیرقیباند.
- NewSQL برای نرمافزارهای سازمانی مدرن که Consistency، Partition Tolerance و Scalability را توأمان طلب میکنند، انتخابی آیندهنگرانه است.
📝 تصمیم درست، وابسته به ترکیب use case، نیازمندیهای consistency، latency، scalability و تیم شماست؛ قبل از تعهد، پروفسازی و تست انجام دهید.
@DeveloperAdvocate 🥑
1 035
🔹 تفاوتهای کلیدی بین
IHostedService و BackgroundService در اجرای سرویسهای طولانیمدت .NET
در معماری مدرن .NET (به ویژه در ASP.NET Core)، اجرای تسکهای طولانیمدت (مثل پردازش صفها، cron jobها یا مانیتورینگ) از طریق استفاده از سرویسهای میزبان انجام میشود. در این زمینه، دو مفهوم پایهای داریم: IHostedService و مشتق آن BackgroundService. ولی کدام را باید انتخاب کنیم؟ تفاوتهایشان چیست؟
۱. IHostedService — اینترفیس پایهای سرویسهای میزبان
- قرارداد اولیه برای lifecycle سرویسها:
- StartAsync(CancellationToken cancellationToken)
- StopAsync(CancellationToken cancellationToken)
- شما باید کل منطق اجرای خود را از صفر پیادهسازی کنید. مناسب است وقتی نیاز به کنترل دقیق رفتار سرویس (مانند تخصیص thread، event-based execution یا مدیریت دقیق resources) دارید.
public class CustomQueueProcessor : IHostedService
{
private Task _backgroundTask;
private CancellationTokenSource _cts;
public Task StartAsync(CancellationToken cancellationToken)
{
_cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
_backgroundTask = Task.Run(() => ProcessQueue(_cts.Token));
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_cts.Cancel();
return _backgroundTask ?? Task.CompletedTask;
}
private async Task ProcessQueue(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
// منطق پردازش صف
}
}
}
۲. BackgroundService — پیادهسازی سادهتر سرویسهای بکگراند
- کلاسی abstract که خودش IHostedService را پیادهسازی کرده و مدیریت lifecycle را سادهتر میکند.
- کافی است فقط متد انتزاعی ExecuteAsync(CancellationToken stoppingToken) را override کنید؛ مدیریت شاتداون graceful و رفتار خطاها تسهیل شده است.
public class TimedWorker : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// اجرای کار زمانبندیشده
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
}
}
}
۳. چه زمانی کدام را انتخاب کنیم؟
- اگر فقط یک حلقه پسزمینه با منطق ساده نیاز دارید، ترجیحاً از BackgroundService استفاده کنید (کمتر شدن boilerplate و مدیریت سادهتر Cancellation/Shutdown)
- اگر نیاز به کنترل دقیق lifecycle یا integration با eventها و منابع غیرمعمول دارید (مثلاً شروع/توقف چند Job یا منابع native)، سراغ IHostedService بروید.
💡 نکته حرفهای: میتوانید چندین IHostedService (یا مشتقاتش مثل BackgroundService) در DI ثبت کنید. ترتیب شروع/توقفشان توسط DI container مدیریت میشود.
#dotnet #backgroundservice #architecture #professional
@DeveloperAdvocate 🥑1 035
🧠 عمیقتر بر useReducer در React: معماری مبتنی بر الگوهای پیام
در پروژههای پیچیده React که مدیریت state به سادگی useState قابل پاسخگویی نیست، استفاده از useReducer نقطه عطفی برای افزایش نظم و قابلیت تستپذیری است. بیایید useReducer را با الهام از الگوهای DDD و CQRS در context یک فرم پیچیده بررسی کنیم:
1️⃣ مدلسازی State و Action به سبک قرارداد محور
type FormState = {
name: string
email: string
errors: { [key: string]: string }
isSubmitting: boolean
}
type Action =
| { type: 'updateField', field: string, value: string }
| { type: 'setError', field: string, error: string }
| { type: 'startSubmit' }
| { type: 'endSubmit' }
2️⃣ پیادهسازی Reducer با رویکرد شفاف و Pure Function
function formReducer(state: FormState, action: Action): FormState {
switch (action.type) {
case 'updateField':
return { ...state, [action.field]: action.value }
case 'setError':
return {
...state,
errors: { ...state.errors, [action.field]: action.error }
}
case 'startSubmit':
return { ...state, isSubmitting: true }
case 'endSubmit':
return { ...state, isSubmitting: false }
default:
return state
}
}
3️⃣ استفاده ایدهآل از useReducer — تقسیم مسئولیت، تستپذیری بالا و امکان اعمال side effect با Middleware
const [state, dispatch] = useReducer(formReducer, initialFormState)
⏩ چرا این معماری برای پروژههای بزرگ ایدهآل است؟
- روشن بودن منطق تغییر State: هر action مانند یک command پویاییهای دامنه را کپسوله میکند.
- امکان پیادهسازی لاگگیری، آندوی و حتی تایملاین state مشابه Redux، بدون سربار اکوسیستم خارجی.
- امکان توسعه سیستم middleware اختصاصی مثل dispatch wrapper برای نظارت و یا اتصال به ابزاری شبیه Redux DevTools.
⚡️ تجربه شخصی: بکارگیری useReducer با شمای Action منسجم بهویژه در فرمهای پیچیده یا مراحل چندگانه (multi-step) منجر به کد تمیزتر، خوانایی بیشتر و تستپذیری عالی میشود؛ بهخصوص زمانی که نیاز به کار با side effect و async داریم.
---
👨💻 شما از چه الگوهایی برای مدیریت state در ریکت استفاده میکنید؟ تجربه معماری محور با useReducer داشتهاید؟
@DeveloperAdvocate 🥑1 035
وقتی بحث STRUCTURED OBJECTS در TypeScript پیش میآید، Type-Level Programming میتواند شما را به سطحی از دقت برساند که کمتر در زبانهای دیگر سراغ داریم. فرض کنید میخواهید نوعی بسازید که مشخص کند تنها آبجکتهایی معتبر هستند که کلیدهای دقیقی مطابق یک اینترفیس پایه دارند و هر کلید هم خودش ساختار مشخصی دارد (نه فقط شکل پایهای).
برای مثال: یک Entity باید دقیقاً شامل id (string)، createdAt (Date) و meta (یک آبجکت با کلیدهای دقیقا user و tags) باشد. هیچ کلید اضافی مجاز نیست.
راهحل: استفاده از Utility Types و Conditional Types در کنار
keyof, extends, و Record برای enforce کردن ساختار:
type StrictEntity<MetaKeys extends string> = {
id: string;
createdAt: Date;
meta: Record<MetaKeys, any>;
} & {
[K in keyof any as Exclude<K, 'id' | 'createdAt' | 'meta'>]?: never
};
type SpecificMeta = 'user' | 'tags';
type MyEntity = StrictEntity<SpecificMeta>;
// استفاده:
const valid: MyEntity = {
id: '123',
createdAt: new Date(),
meta: {
user: { name: 'Ali' },
tags: ['typescript']
}
};
// خطا: کلید extra مجاز نیست
const invalid: MyEntity = {
id: '123',
createdAt: new Date(),
meta: {
user: {},
tags: []
},
extra: 42 // ❌
};
در این الگو، هر تلاشی برای افزودن کلید اضافی به entity توسط TypeScript بهدرستی رد میشود. این قابلیتها رمز موفقیت معماریهای front-end مبتنی بر Type Safety هستند—بخش بزرگی از اتفاقات جالب دنیای TypeScript همینجاست.
@DeveloperAdvocate 🥑1 035
یکی از حیاتیترین نکات امنیتی در کار با JWT این است که هرگز به دادههای داخل توکن بدون اعتبارسنجی امضا اعتماد نکنید؛ حتی اگر فکر میکنید توکن فقط در سمت سرور تولید میشود.
مهاجم میتواند به سادگی، الگوریتم امضای توکن (مثلاً از
HS256 به none) را عوض کند یا حتی یک امضای جعلی بسازد و دادهها را تزریق کند. اگر امضای توکن بهدرستی و با کلید/گواهی معتبر بررسی نشود، این یعنی اجازه دادهاید هرکسی ادعای هر نقشی را داشته باشد—که عملاً کنترل احراز هویت و اعتبارسنجی شما شکست میخورد.
همیشه در پیادهسازی احراز هویت JWT سمت سرور .NET، گزینه اعتبارسنجی امضای توکن را فعال کنید:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true, // مهمترین بخش!
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your-secret-key")),
ValidateIssuer = true,
ValidIssuer = "your-issuer",
ValidateAudience = true,
ValidAudience = "your-audience",
// تنظیمات دیگر ...
};
});
مطمئن شوید که مقدار ValidateIssuerSigningKey همیشه برابر با true است و از یک کلید محرمانه و ذخیرهشده در محیط مناسب استفاده میکنید. یک اشتباه مرسوم: هیچ وقت به دادههای داخل JWT مثل نقش کاربر (role) یا شناسهها بدون تأیید امضا استناد نکنید—این اطلاعات بدون اعتبارسنجی، مطلقاً قابل اعتماد نیستند.
🔑 امنیت امضای JWT = امنیت کل سیستم.
@DeveloperAdvocate 🥑1 035
در پروژههای #DotNet حرفهای، توصیه جدی میکنم از Roslyn Analyzers برای تحکیم کیفیت کد و یکپارچگی سبک برنامهنویسی استفاده کنید. کافیست آنالایزرهای متن باز محبوب مثل StyleCop.Analyzers یا Microsoft.CodeAnalysis.FxCopAnalyzers را به عنوان NuGet package به solution اضافه کنید و دقیقاً تعیین کنید چه قوانینی اعمال شوند—مثلاً نامگذاری، مستندسازی، جلوگیری از anti-patternها یا حتی قواعد اختصاصی سازمان.
وقتی آنالایزرها را در سطح CI/CD پیکربندی میکنید، دیگر نیازی به review دستی بسیاری از خطاهای رایج نخواهد بود و حتی قبل از merge شدن Pull Requestها، مشکلات کد توسط خود ابزار شناسایی میشوند:
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="all"/>
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0" PrivateAssets="all"/>
</ItemGroup>
پشتیبانی از configuration پیشرفته (مثلاً با editorconfig.) این امکان را میدهد که برای هر پروژه یا حتی هر پوشه، سبک متفاوتی تعریف کنید—بدون نیاز به ابزار خارجی. حتی میتوانید Analyzer دلخواه خود را با Roslyn بنویسید تا قواعد سازمانی خاص را enforce کنید.
ضمناً توصیه میکنم severity قوانین مهم مثل CA1062 (بررسی null روی پارامترهای public) یا قوانین naming را در سطح Error بگذارید تا fail در Build رخ دهد:
dotnet_diagnostic.CA1062.severity = error
dotnet_diagnostic.SA1300.severity = error
این معماری ابزارمحور، فرهنگ کدنویسی تیم را پایدار و استاندارد نگه میدارد و باعث استقلال بیشتر مهندسان از ملاحظات سلیقه ای و انرژیبر کدریویو میشود.
@DeveloperAdvocate 🥑1 035
🔹 تصمیمگیری هوشمندانه بین SQL، NoSQL و NewSQL — یک درخت تصمیم معماری دیتابیس
انتخاب نوع دیتابیس، قلب هر معماری نرمافزاری است و تأثیر بلندمدتی بر مقیاسپذیری، انعطاف و نگهداشتپذیری سیستم دارد. در این تصمیمگیری، به نکات زیر توجه کنید:
1️⃣ دیتا چقدر ساختیافته و رابطهای است؟
• اگر داده دارای اسکیمای ساختیافته و روابط پیچیده (جداول نرمال، JOIN، تراکنش ACID کامل) است ➡️ SQL
• اگر داده نیمهساختیافته، سندمحور یا بدون ساختار است (JSON, Key-Value, Document, Graph) ➡️ NoSQL
2️⃣ الزامات مقیاسپذیری سیستم چیست؟
• اگر عمدتا مقیاسپذیری عمودی (vertical scaling) کافی است یا دیتابیس شما تا چند ترابایت رشد میکند ➡️ SQL
• اگر نیاز به مقیاسپذیری افقی و توزیع شده (sharding, replication) داریم ➡️ به مراحل بعدی بروید
3️⃣ سطح Consistency موردنیاز چقدر است؟
• اگر نیاز به Strong Consistency و تراکنشهای ACID در سطح distributed دارید ➡️ NewSQL
• اگر eventual consistency قابل پذیرش است (مثل سیستمهای لاگ، شبکههای اجتماعی) ➡️ NoSQL
4️⃣ الزامات Performance و Latency چیست؟
• حجم بالا از write/read با latency پایین و استعلامهای ساده ➡️ NoSQL (Cassandra, Redis, MongoDB, etc.)
• Queryهای پیچیده، analytics سنگین یا BI ➡️ SQL یا NewSQL (CockroachDB, Google Spanner, etc.)
5️⃣ آیا نیاز به انعطاف در Schema دارید؟
• تغییرات اسکیمای مداوم و فیلدهای منعطف ➡️ NoSQL
• اسکیمای ثابت و الزام درستی داده ➡️ SQL / NewSQL
📌 خلاصه نموداری:
آیا داده رابطهای و تراکنشمحور است؟
├─ بله ⇒ SQL (PostgreSQL, SQL Server, MySQL, ...)
└─ خیر
└─ آیا به مقیاسپذیری افقی و ACID نیاز دارید؟
├─ بله ⇒ NewSQL (CockroachDB, TiDB, Spanner)
└─ خیر
└─ آیا consistency قوی میخواهید؟
├─ بله ⇒ NewSQL یا برخی NoSQLها با تنظیم consistency (مانند MongoDB WiredTiger)
└─ خیر ⇒ NoSQL (MongoDB, Cassandra, Redis, DynamoDB, ...)
🔸 نکته معمارانه: حتما علاوه بر الگوهای فعلی، آینده محصول را در نظر داشته باشید (مثلا نیاز به تحمل خطا و geo-replication). ترکیبکردن چند دیتابیس (Polyglot Persistence) نیز گاهی بهترین پاسخ است؛ اما زیرساخت و تیم را برای پیچیدگی آن آماده کنید.
#Architecture #Database #DecisionTree
@DeveloperAdvocate 🥑1 035
🚩 عمیقتر به
useEffect و آرایه وابستگیها در React
یکی از ظریفترین بحثها برای مهندسین فنی ارشد، فهم درست آرایه وابستگیها در useEffect است. رفتار دقیق و درست افکتها به ارزیابی این آرایه گره خورده. بیایید لایههای پنهان این مفهوم را باز کنیم:
🔎 الگوهای مقایسهای در آرایه وابستگی
هرچیزی که در آرایه وابستگیها قرار میدهید با Object.is مقایسه میشود (در مورد آبجکتها و آرایهها، اشاره مقایسه میشود نه مقدار!). این یعنی هر بار که یک آرایه یا آبجکت جدید (حتی با همان مقدار) بسازید، وابستگی تغییر تلقی میگردد و افکت اجرا خواهد شد.
useEffect(() => {
// اجرا میشود هرگاه filter تغییر کند
}, [filter]);
اگر filter یک آبجکت باشد و در هر رندر جدید بازسازی شود، افکت هر بار اجرا خواهد شد—حتی اگر مقدار آن عوض نشده باشد.
⚠️ Pitfall 1: آبجکت و آرایه به عنوان Dependency
یکی از رایجترین اشتباهات:
useEffect(() => {
// ...
}, [options]); // BAD: options یک آبجکت است که هر بار رندر، جدید ایجاد میشود
🔗 راهحل: اطمینان حاصل کنید reference وابستگیها پایدار باشد یا مقدار primitive استفاده کنید یا از useMemo/useCallback برای تثبیت reference بهره ببرید.
const memoizedOptions = useMemo(() => ({...}), [deps]);
useEffect(() => {
// ...
}, [memoizedOptions]);
⚠️ Pitfall 2: عدم همراستایی وابستگیها با متغیرهای داخلی
همه متغیرهایی (state, props, یا توابع) که درون افکت استفاده میشوند و scope بیرونی دارند باید در آرایه وابستگی ذکر شوند. عدم درج آنها منجر به رفتار نامشخص و باگهای زمانی میشود:
function Example({ userId }) {
const [data, setData] = useState();
useEffect(() => {
fetchData(userId).then(setData);
}, []); // BAD: userId باید در dependency array باشد
}
⚡ Best Practice: همیشه قانون ESLint تحت مجموعهی react-hooks/exhaustive-deps را فعال کنید تا از کامل بودن وابستگیها اطمینان یابید.
🔍 درک کیفیت اجرای افکتها
آرایه dependency به React میگوید که افکت را پس از هر تغییر این وابستگیها اجرا کند. عبور آرایهٔ خالی ([]) معادل با componentDidMount است—افکت فقط یکبار اجرا میشود. بدون آرایه، افکت پس از هر رندر اجرا خواهد شد (تقریباً همیشه اشتباه است مگر از روی قصد).
🛡️ جمعبندی برای معماران
- همیشه به reference اشیاء و آرایهها در dependency array توجه داشته باشید.
- هر متغیر/تابع بیرونیِ استفادهشده را باید در آرایه درج نمود.
- برای جلوگیری از اجرای ناخواسته افکتها، از memoization یا مقادیر primitive بهره ببرید.
- ابزار lint را جدی بگیرید؛ اکثر باگهای subtle در useEffect با رعایت این اصول قابل اجتناباند.
#React #Frontend #useEffect #ReactHooks #EngineeringBestPractices
@DeveloperAdvocate 🥑1 035
🔥 #NET8 #Advanced #CollectionBuilder
📚 قدرت Literals دلخواه برای کالکشنها با
[CollectionBuilder] در .NET 8!
در .NET 8، کلکسیونهای سفارشی نیز میتوانند مانند لیست و آرایه، از سینتکس literal ([]) بهره ببرند! این قابلیت با Attribute جدید [CollectionBuilder] ارائه شده و با آن میتوانید برای Typeهای خود، literal مشخص کنید. به یک مثال پیشرفته دقت کنید:
using System.Collections.Frozen;
using System.Runtime.CompilerServices;
// CollectionBuilder را روی Type مقصد قرار دهید:
[CollectionBuilder(typeof(FrozenSetBuilder), nameof(FrozenSetBuilder.Create))]
public readonly struct MyFrozenSet<T>
{
private readonly FrozenSet<T> _set;
public MyFrozenSet(FrozenSet<T> set) => _set = set;
public bool Contains(T value) => _set.Contains(value);
}
// یک Builder کلاس میسازیم که Factory متدی با Signature مشخص داشته باشد:
public static class FrozenSetBuilder
{
public static MyFrozenSet<T> Create<T>(ReadOnlySpan<T> items)
=> new MyFrozenSet<T>(FrozenSet.ToFrozenSet(items.ToArray()));
}
حالا میتوانید با سنتکس راحت و خوانا، نوع خود را مقداردهی کنید:
MyFrozenSet<int> set = [1, 4, 6, 9];
if (set.Contains(6))
Console.WriteLine("عضو مجموعه است!");
📌 نکات حرفهای:
- متد استاتیک Builder باید public static <TCollection> Create<T>(ReadOnlySpan<T>) باشد (یا مشابه).
- این سینتکس برای Typesهایی با Decorator [CollectionBuilder] فقط در C# 12+ فعال است.
- میتوانید Collection Literals را با هر Type دلخواه (Dictionary, Tree, Immutable) ترکیب و سفارشیسازی کنید.
🚀 با [CollectionBuilder] امکانات DSL گونه برای کلکشنهای اختصاصی بسازید، سود ببرید و کد کلاینت را به شدت مختصر و امنتر کنید!✨
@DeveloperAdvocate 🥑1 035
🔒 Subresource Integrity (SRI): سطح جدیدی از امنیت برای بارگذاری داراییها از CDN
یکی از دغدغههای جدی معماری فرانتاند، اطمینان از صحت فایلهای لودشده از CDN است. با SRI میتوانید تضمین کنید فایلهای JS/CSS که از CDN بارگذاری میکنید، دقیقاً همان چیزی هستند که انتظار دارید و در صورت تغییر (بهدلیل هک CDN یا هر دلیل دیگری)، مرورگر از بارگذاری آنها جلوگیری میکند.
کافی است برای هر تگ
<script> یا <link> که کردن دارایی خارجی استفاده میکنید، هش SRI مربوطه را محاسبه و مشخص کنید:
<script src="https://cdn.jsdelivr.net/npm/luxon@3.4.4/build/global/luxon.min.js"
integrity="sha384-ufRTScD1iRHBIF08hWy6idMJFbtoFE8H0ItkPeqd18R8zxCqxSRurKyYQgW2O4A4"
crossorigin="anonymous"></script>
نکات فنی حرفهای:
- هش باید با الگوریتم قوی (ترجیحاً SHA-384 یا SHA-512) تولید شود.
- مقدار crossorigin معمولاً روی anonymous قرار میگیرد، مگر اینکه به کوکی یا credentials نیاز باشد.
- اگر فایل تغییر کند (مثلاً CDN آپدیت شود)، SRI mismatch رخ میدهد و مرورگر آن را reject میکند؛ پس SRI را همیشه با نسخه فایل sync کنید.
- ابزار تولید SRI: از دستور زیر جهت تولید Hash برای فایل دلخواه استفاده کنید:
openssl dgst -sha384 -binary luxon.min.js | openssl base64 -A
این رویکرد در پروژههایی که CDN سومشخص استفاده میشود، امنیت جدی اضافه میکند و بهویژه در تیمهایی که به امنسازی supply chain اهمیت میدهند، ضروری است.
#Security #Frontend #SRI #CDN
@DeveloperAdvocate 🥑1 035
وقتی صحبت از «Virtual DOM» در React میشود، بهتر است آن را با مدل سنتی مستقیمنویسی DOM مقایسه کنیم. تغییرات روی DOM مرورگر، ذاتاً عملیات سنگینی هستند؛ برای هر آپدیت، مرورگر باید ساختار درختی DOM را پیمایش، محاسبهٔ layout و رندر مجدد المانها را انجام دهد. این نقطه ضعف باعث میشود اپلیکیشنهای تعاملی کند و سنگین شوند.
معادله با Virtual DOM فرق میکند. React به جای دستزدن مستقیم به DOM، ابتدا تغییرات را در یک نسخهٔ انتزاعی و سبک از DOM (همان Virtual DOM) اعمال میکند؛ این یک درخت جاوااسکریپتی است که تقریباً شبیه DOM واقعی اما بسیار سریعتر پیمایش و پردازش میشود. حالا، هر بار وضعیت (state) یا props عوض میشود، React یک Virtual DOM جدید میسازد و با نسخه قبلی diff میکند (عملیات Diffing). نتیجه: فقط «تفاوتها» پیدا میشوند.
خلاصه: React فقط تغییرات ضروری (minimal set of updates) را روی DOM واقعی به کمک الگوریتمی مثل Reconciliation اعمال میکند. این مکانیزم باعث میشود حتی در پیجهای بسیار تعاملی یا صفحات با هزاران نود، عملکرد چشمگیری تجربه کنید.
جمعبندی برای آشنایی عمیقتر: اگر روزی کرت میل داشتید همین pattern را در سطح #ASP.NET Core پیادهسازی کنید—مثلاً هنگام ساخت یک موتور رندرینگ سمت کلاینت یا SSR—میتوانید الگوریتم diff را با data structureهایی مثل tree و queue تقویت کنید. بخش عمدهای از موفقیت رندرفریمورکها دقیقا در «بهینهسازی حداقلی تغییرات روی DOM» نهفته است؛ یعنی جایی که معماری front و back strong ذکر میشوند.
@DeveloperAdvocate 🥑
