C# Geeks (.NET)
413 subscribers
152 photos
4 videos
153 links
Download Telegram
در یکی از پروژه‌ها یک تصمیم کوچک گرفتیم:
برای افزایش سرعت توسعه، Validationها را از سمت Backend به Frontend منتقل کنیم.
در لحظه همه چیز منطقی بود.UI سریع‌تر بازخورد می‌داد.API ساده‌تر شد.تیم Frontend هم راضی بود.
اما چند ماه بعد، همان تصمیم کوچک خودش را در جای دیگری نشان داد.
چند کلاینت مختلف وجود داشت.موبایل.وب.سرویس‌های داخلی.
و هرکدام بخشی از Validation را به شکل متفاوتی پیاده‌سازی کرده بودند.
نتیجه چه شد؟
یک منطق تجاری در چند جای مختلف تکرار شد.
و هیچ‌کدام ۱۰۰٪ با دیگری هماهنگ نبود.
باگ‌هایی ظاهر می‌شد که در یک کلاینت دیده می‌شد اما در دیگری نه.
و دیباگ کردن آن‌ها به مرور سخت‌تر شد.
نکته جالب این بود که تصمیم اولیه اشتباه نبود.
در آن مقطع حتی بهترین گزینه به نظر می‌رسید.
اما فرض اصلی آن تصمیم این بود:
«یک نقطه مرکزی برای اعمال منطق وجود ندارد.»
و همین فرض بعداً تغییر کرد.
در مهندسی نرم‌افزار، بسیاری از مشکلات از جایی شروع می‌شوند که یک تصمیم کوچک بدون توجه به پیامدهای توزیع‌شده گرفته می‌شود.
چون سیستم‌ها فقط یک کدبیس نیستند.
یک اکوسیستم از کلاینت‌ها، سرویس‌ها و رفتارهای همزمان هستند.
شاید به همین دلیل است که طراحی خوب فقط درباره محل قرار گرفتن منطق نیست.
درباره این است که آن منطق در چند جا باید زندگی کند،
و چطور باید با تغییرات آینده کنار بیاید.
🔐 چرا شرکت‌های بزرگ یک Identity Provider جداگانه می‌سازند؟

اوایل پروژه همه چیز ساده است.
کاربر لاگین می‌کند، JWT می‌گیرد و تمام.
اما وقتی تعداد سرویس‌ها بیشتر می‌شود، ناگهان هر سرویس شروع می‌کند به مدیریت کاربران، نقش‌ها، دسترسی‌ها، Refresh Tokenها و Social Loginها.
همین‌جاست که مفهوم Identity Provider (IdP) وارد می‌شود.
به جای اینکه هر سرویس خودش مسئول احراز هویت باشد، یک سرویس مرکزی فقط روی Authentication و Authorization تمرکز می‌کند.
🏗 معماری سنتی
User

Service A

Database

User

Service B

Database

هر سرویس منطق احراز هویت خودش را دارد.
🏗 معماری با Identity Provider
User

Identity Provider

Access Token


┌─────────┼─────────┐
↓ ↓ ↓
Service A Service B Service C

تمام سرویس‌ها فقط اعتبار Token را بررسی می‌کنند و دیگر درگیر فرآیند Login نیستند.
مزایا
1️⃣ Single Sign-On (SSO)

کاربر یک بار وارد می‌شود و به همه سیستم‌ها دسترسی پیدا می‌کند.
2️⃣ تمرکز روی امنیت

تمام منطق امنیتی در یک نقطه قرار می‌گیرد:
• Password Policy
• MFA
• OAuth
• OpenID Connect
• Account Lockout
• Device Management
3️⃣ کاهش کد تکراری

دیگر لازم نیست هر سرویس:
• Login Endpoint
• Refresh Token Logic
• Password Reset
• Email Verification
را جداگانه پیاده‌سازی کند.
4️⃣ توسعه‌پذیری بیشتر

اضافه کردن Google Login، GitHub Login یا Azure AD فقط در IdP انجام می‌شود.
تمام سرویس‌ها به صورت خودکار از آن بهره می‌برند.
5️⃣ مدیریت متمرکز دسترسی‌ها

ءRoleها، Permissionها و Claimها در یک نقطه نگهداری می‌شوند.

معایب
1️⃣ Single Point Of Failure

اگر IdP از دسترس خارج شود، ورود کاربران مختل می‌شود.
2️⃣ پیچیدگی بیشتر

دیگر با یک پروژه ساده طرف نیستید.OAuth2، OIDC، Token Exchange و Security Flowها وارد سیستم می‌شوند.
3️⃣ نیاز به مانیتورینگ قوی

ءIdentity Provider یکی از حساس‌ترین سرویس‌های سازمان خواهد شد.
4️⃣ چالش Revocation

حذف دسترسی کاربران در معماری JWT توزیع‌شده همیشه ساده نیست.
🚀 Flow اصولی پیاده‌سازی
مرحله 1️⃣
کاربر درخواست Login ارسال می‌کند.
POST /connect/token

مرحله 2️⃣
ءIdentity Provider اعتبار کاربر را بررسی می‌کند.
مرحله 3️⃣
ءAccess Token و Refresh Token صادر می‌شود.
Access Token
Refresh Token

مرحله 4️⃣
ءClient توکن را به سرویس‌ها ارسال می‌کند.
Authorization: Bearer xxx

مرحله 5️⃣
هر سرویس فقط Signature و Claims را اعتبارسنجی می‌کند.
مرحله 6️⃣
در صورت انقضای Access Token، از Refresh Token برای دریافت Token جدید استفاده می‌شود.

📋 نکاتی که حتماً باید رعایت شوند

🔸 ءAccess Token کوتاه‌عمر باشد
(۵ تا ۱۵ دقیقه)
🔸 ءRefresh Token قابلیت Rotation داشته باشد
🔸 ءJWT Secret یا Private Key به صورت امن نگهداری شود
🔸 ءMFA برای حساب‌های حساس فعال شود
🔸 ءRate Limiting روی Endpointهای Login اعمال شود
🔸 ءAudit Log تمام عملیات امنیتی ذخیره شود
🔸 از OAuth2 و OpenID Connect استاندارد استفاده شود

⭐️ برای بهتر شدن Identity Provider چه کارهایی انجام دهیم؟
Key Rotation خودکار
MFA و Passwordless Authentication
Device Tracking
Session Management
Distributed Cache برای Token Validation
Audit Logging و Monitoring
Revocation List برای ابطال Tokenها
High Availability و Replicaهای متعدد
استفاده از OpenIddict یا Keycloak به جای ساخت همه چیز از صفر

💡 مهم‌ترین نکته:
ءIdentity Provider فقط یک سرویس Login نیست.
در سیستم‌های بزرگ، IdP به قلب امنیت کل سازمان تبدیل می‌شود.
هرچه زودتر احراز هویت را از Business Domainها جدا کنید، توسعه سرویس‌ها ساده‌تر و امنیت سیستم قابل مدیریت‌تر خواهد شد.

🔖هشتگ‌ها:
#identityserver #oauth2 #jwt #security #microservices #modularmonolith #aspnetcore
#مهندس_فکر_کن
قسمت:3️⃣

🎯مصاحبه‌کننده:
فرض کنید یک سرویس دارید که روزانه میلیون‌ها درخواست را پردازش می‌کند.
ناگهان یکی از اعضای تیم متوجه می‌شود که در بعضی شرایط نادر، دو Thread همزمان وارد یک بخش حساس از کد می‌شوند و باعث ایجاد داده ناسازگار می‌شوند.
برای حل این مشکل چه مراحلی را باید طی کرد؟
Forwarded from thisisnabi.dev [Farsi]
4. Batch Processing

من این منابع برام جذاب بوده.
1. Designing Data-Intensive Applications E2
- Storage and Retrieval [4]
- Batch Processing [11]

2. Fundamentals of Data Engineering
- Designing Good Data Architecture [3]
- Storage [6]
- Ingestion [7]


Update 1:
تعطیلات سردتون نکنه، اگر چالش های قبلی رو نرسیدید بهترین وقته برای ریبیس شدن.

@thisisnabi_dev
Forwarded from thisisnabi.dev [Farsi]
This media is not supported in your browser
VIEW IN TELEGRAM
احتمالا همه تون این پست هایی مصاحبه کننده سوال میکند فلان رو دیده باشید‌
سوالات خیلی سطحی و یک خطی که احتمالا در جای درست و حسابی هم از شما پرسیده نمیشه.

جدای از اینکه خوب هست یا بد، نگاه من اینه که در مصاحبه جزئیات رو از سوال کننده بخواید، خیلی از تصمیم ها بخاطر یک جزئیات کوچک می توانند رد بشن.

در کنار این پرداختن به جزییات سطح آگاهی شما رو به تصویر میکشه.

البته صحبت اولم به معنای این نیست که به این سوالات جواب ندید، بلکه خیلی هم خوبه که بعنوان یک تمرین ذهنی بهش بپردازید، صحبتم اینکه که بدون آگاهی از جزئیات سعی کنید جواب نهایی رو ندید.

@thisisnabi_dev
یکی از اشتباه‌ ترین تصور هایی که اوایل مسیر داشتم این بود که فکر می‌کردم هر مسئله‌ای باید یک راه‌ حل درست داشته باشد.
یک جواب.
یک تصمیم صحیح.
یک انتخاب واضح.
اما هرچه بیشتر در پروژه‌ های واقعی کار کردم، بیشتر فهمیدم که بسیاری از تصمیم‌ های مهندسی این‌ گونه نیستند.
مثلاً:
آیا باید Monolith بمانیم یا به سمت Microservice برویم؟
آیا باید Cache اضافه کنیم؟
آیا باید سیستم را Rewrite کنیم؟
آیا باید این Feature را Generic طراحی کنیم؟

جالب است که برای همه این سوال‌ ها می‌توان مثال‌ هایی پیدا کرد که پاسخ «بله» درست باشد.
و مثال‌ هایی که پاسخ «خیر» درست باشد.
همان‌ جا بود که فهمیدم بخش بزرگی از مهندسی نرم‌ افزار، پیدا کردن پاسخ درست نیست.فهمیدن سوال درست است.
چون خیلی وقت‌ ها قبل از اینکه راه‌ حل اشتباهی انتخاب کنیم،
در حال حل کردن مسئله اشتباهی هستیم.
ساعت‌ ها روی Performance کار می‌کنیم، در حالی که گلوگاه اصلی جای دیگری است.
معماری را پیچیده می‌کنیم، در حالی که مشکل واقعی فرآیند های تیم است.
سیستم را Scale می‌کنیم، در حالی که مسئله از یک Query اشتباه شروع شده است.
شاید به همین دلیل است که مهندسان باتجربه، معمولاً زودتر از بقیه راه‌ حل ارائه نمی‌دهند.
آن‌ها مدت بیشتری روی فهمیدن مسئله وقت می‌گذارند.
چون می‌دانند انتخاب بهترین راه‌ حل برای یک مسئله اشتباه،
هنوز هم یک اشتباه است.
The best abstraction is the one you don't have to write. 😉☕️
یکی از اشتباهاتی که در بسیاری از پروژه‌های NET. دیده می‌شود، استفاده از "throw" برای خطاهای قابل انتظار (Expected Errors) است.

فرض کنید کاربر می‌خواهد سفارشی ثبت کند، اما موجودی کالا کافی نیست.

این یک Exception نیست:
throw new OutOfStockException();

این یک نتیجه‌ی قابل انتظار از منطق کسب‌وکار است:
return Result.Failure(Errors.Product.OutOfStock);

چرا؟
🔹 ساختن Exception شامل ایجاد Stack Trace است که هزینه‌ی CPU و حافظه دارد.
🔹 پرتاب و Catch کردن Exception مسیر اجرای برنامه را کندتر می‌کند.
🔹 درواقع Exception برای اتفاقات غیرمنتظره طراحی شده است، نه اعتبارسنجی قوانین کسب‌وکار.

قاعده‌ای که همیشه از آن استفاده می‌کنیم:
Expected Error → "Result"


• موجودی کافی نیست.
• کاربر پیدا نشد.
• اعتبارسنجی ناموفق بود.
• سفارش قبلاً پرداخت شده است.

Unexpected Error → "throw"


• قطع شدن Database
• خطای شبکه
• باگ برنامه
• شرایطی که هرگز نباید رخ دهند

به این ترتیب:
✔️ کد خواناتر می‌شود.
✔️ جریان اجرای برنامه شفاف‌تر است.
✔️ از هزینه‌ی غیرضروری Exceptionها جلوگیری می‌شود.
✔️ و Performance در سناریوهای پرتکرار بهتر خواهد بود.

پس Result برای کنترل جریان عادی برنامه است و Exception برای شرایط استثنایی.
نرم‌افزار هیچ‌وقت «تمام‌شده» نیست. و دقیقاً به همین دلیل است که باید در مسیر، موفقیت‌های کوچک را جشن گرفت.

همیشه یک نسخه‌ی جدید وجود دارد. یک باگ دیگر. یک فیچر دیگر در بک‌لاگ. خط پایان فقط جابه‌جا نمی‌شود — اصلاً وجود ندارد. پس اگر تا امروز به خودت گفته‌ای «وقتی همه‌چیز آرام شد جشن می‌گیرم»، باید بپذیری که این آرامش احتمالاً هیچ‌وقت نمی‌رسد.

من دیده‌ام افرادی که اجازه داده‌اند یک یا دو سال از زندگی کاری‌شان در یک حالت کاریِ بی‌وقفه و فرسایشی محو شود و آن را «طبیعی» بنامند. خودم هم این را تجربه کرده‌ام. یک روز نگاه می‌کنی و می‌بینی بخش بزرگی از مسیر شغلی‌ات گذشته، بدون اینکه حتی لحظه‌ای توقف کرده باشی تا احساس خوبی نسبت به آن داشته باشی.

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

کار قرار نیست متوقف شود تا به تو تبریک بگوید. پس این کار را باید خودت برای خودت بسازی.
🚫 چه زمانی نباید از Design Patternها استفاده کنیم؟

یکی از بزرگ‌ترین اشتباهات برنامه‌نویس‌ها این است که فکر می‌کنند هر مسئله‌ای باید با یک Design Pattern حل شود.
در حالی که بسیاری از Patternها برای حل مشکلات پیچیده طراحی شده‌اند، نه برای پیچیده‌تر کردن کدهای ساده.
گاهی یک متد ساده یا یک if دقیقاً همان چیزی است که نیاز دارید. 👇
1️⃣ Adapter

زیاده‌روی است وقتی:
فقط قرار است یک یا دو نوع داده را تبدیل (Map) کنید.
به‌جای آن:
یک Mapper ساده یا Helper Method بنویسید.

2️⃣ Decorator

زیاده‌روی است وقتی:
فقط می‌خواهید یک Validation یا Log ساده اضافه کنید.
به‌جای آن:
از Pipeline، Middleware یا حتی منطق مستقیم استفاده کنید.

3️⃣ Facade

زیاده‌روی است وقتی:
زیرسیستم شما API واضح و ساده‌ای دارد.
به‌جای آن:
مستقیماً همان Service را فراخوانی کنید.

4️⃣ Abstract Factory

زیاده‌روی است وقتی:
فقط یک یا دو نوع شیء می‌سازید.
به‌جای آن:
از Constructor یا Factory Method ساده استفاده کنید.

5️⃣ Strategy

زیاده‌روی است وقتی:
رفتار فقط چند حالت ساده دارد.
به‌جای آن:
یک if/else یا switch کاملاً کافی است.

6️⃣ Builder

زیاده‌روی است وقتی:
شیء فقط چند Property اختیاری دارد.
به‌جای آن:
از Object Initializer یا پارامترهای Optional استفاده کنید.

7️⃣ Factory Method

زیاده‌روی است وقتی:
منطق ساخت شیء بسیار ساده است.
به‌جای آن:
از new یا یک Helper استفاده کنید.

8️⃣ Chain of Responsibility

زیاده‌روی است وقتی:
جریان اجرای شما کوتاه و ثابت است.
به‌جای آن:
یک Pipeline ساده از متدها بسازید.

9️⃣ Template Method

زیاده‌روی است وقتی:
فقط بخش کوچکی از الگوریتم تغییر می‌کند.
به‌جای آن:
از Delegate یا یک متد مشترک استفاده کنید.

🔟 Bridge

زیاده‌روی است وقتی:
فقط یک بُعد تغییر در سیستم دارید.
به‌جای آن:
ءComposition یا یک Interface ساده کافی است.

1️⃣1️⃣ Command

زیاده‌روی است وقتی:
عملیات ساده هستند و نیازی به Queue، Undo یا Retry ندارند.
به‌جای آن:
مستقیماً متد موردنظر را صدا بزنید.

1️⃣2️⃣ State

زیاده‌روی است وقتی:
فقط چند State ساده دارید.
به‌جای آن:
یک enum همراه با switch استفاده کنید.

1️⃣3️⃣ Proxy

زیاده‌روی است وقتی:
فقط یک Wrapper کوچک نیاز دارید.
به‌جای آن:
یک Helper یا Wrapper ساده بنویسید.

1️⃣4️⃣ Observer

زیاده‌روی است وقتی:
فقط یک یا دو Receiver دارید.
به‌جای آن:
از Callback یا فراخوانی مستقیم متد استفاده کنید.

1️⃣5️⃣ Composite

زیاده‌روی است وقتی:
قرار نیست با Itemها و Groupها رفتار یکسانی داشته باشید.
به‌جای آن:
منطق List و Item را جدا نگه دارید.

1️⃣6️⃣ Visitor

زیاده‌روی است وقتی:
مدل دائماً تغییر می‌کند و تعداد عملیات کم است.
به‌جای آن:
ءPattern Matching یا switch انتخاب بهتری است.

1️⃣7️⃣ Prototype

زیاده‌روی است وقتی:
کپی گرفتن از اشیاء ساده است.
به‌جای آن:
از Copy Constructor یا Mapper استفاده کنید.

1️⃣8️⃣ Flyweight

زیاده‌روی است وقتی:
مصرف حافظه مشکل اصلی سیستم نیست.
به‌جای آن:
از Objectهای معمولی و Cache هدفمند استفاده کنید.

1️⃣9️⃣ Interpreter

زیاده‌روی است وقتی:
قوانین سیستم کم و ثابت هستند.
به‌جای آن:
از Parser ساده یا Configuration Table استفاده کنید.

2️⃣0️⃣ Singleton

زیاده‌روی است وقتی:
فقط یک سرویس مشترک می‌خواهید.
به‌جای آن:
آن را به‌صورت Singleton در DI Container ثبت کنید، نه اینکه الگوی Singleton را پیاده‌سازی کنید.

2️⃣1️⃣ Mediator

زیاده‌روی است وقتی:
فقط چند سرویس محدود با هم تعامل دارند.
به‌جای آن:
از فراخوانی مستقیم Service به Service استفاده کنید.

🎯 جمع بندی

ءDesign Patternها ابزار هستند، نه هدف.
بهترین معماری، معماری‌ای نیست که بیشترین Pattern را داشته باشد؛ بلکه معماری‌ای است که ساده‌ترین راه‌حل ممکن را برای مسئله‌ی واقعی انتخاب کند.
همان‌طور که Martin Fowler می‌گوید:
"Any fool can write code that a computer can understand. Good programmers write code that humans can understand." 💡
گاهی بهترین Pattern، استفاده نکردن از Pattern است. 😉
تا حالا دقت کرده‌ای چرا بعضی کتابخانه‌ها این‌قدر آرام هستند؟
نه به خاطر اینکه کسی آنجا کتاب نمی‌خواند.
به خاطر اینکه هر کتاب، جای مشخصی دارد.
اگر دنبال کتابی باشی، لازم نیست از ده نفر سؤال بپرسی.
لازم نیست حدس بزنی کدام قفسه مناسب است.
همه چیز بر اساس یک نظم مشخص پیدا می‌شود.
حالا یک پروژه نرم‌افزاری را تصور کن که بعد از چند سال توسعه، هر کلاس می‌تواند هر چیزی را صدا بزند.
هر ماژول به هر ماژول دیگری وابسته است.
برای تغییر یک قابلیت ساده، باید پنج بخش مختلف را بررسی کنی.
پروژه هنوز کار می‌کند.
اما دیگر شبیه کتابخانه نیست.
بیشتر شبیه انباری است.
انباری که همه چیز داخل آن هست.
اما پیدا کردن هر چیزی، زمان می‌برد.
جالب اینجاست که هیچ پروژه‌ای از روز اول انباری نبوده است.
فقط هر بار یک وابستگی کوچک اضافه شده.
یک میانبر دیگر.
یک دسترسی مستقیم دیگر.
تا جایی که مرزها از بین رفته‌اند.
در مهندسی نرم‌افزار،معماری فقط برای این نیست که سیستم امروز کار کند.
برای این است که شش ماه بعد،هنوز بدانی هر چیز دقیقاً باید کجا باشد.
#مهندس_فکر_کن
قسمت:4️⃣

🎯مصاحبه‌کننده:
ءCache Stampede چیست؟
فرض کن یک کلید در Cache داری:
Product:123

و TTL آن 5 دقیقه است.
در لحظه‌ای که این کلید منقضی می‌شود، همزمان 5000 درخواست برای همان محصول می‌آید.
اتفاقی که می‌افتد:
Client 1 ----\
Client 2 -----\
Client 3 -------> Cache Miss
Client 4 -----/
Client 5 ----/
...
5000 Requests

همه همزمان Cache Miss می‌خورند.
سپس همه به Database می‌روند.
5000 Query

Database

در نتیجه Database Overload، افزایش شدید Latency ،احتمال Down شدن سرویس.
به این حالت Cache Stampede می‌گویند.

حالا بگو ببینم برای حل این مشکل چه راهکار هایی میدی؟🤔
"صرفا فقط برای اینکه لیبل خارج خورده روش"😑
Forwarded from Learning With M
This media is not supported in your browser
VIEW IN TELEGRAM
دستور مصرف: هر صبح، حداقل یک بار بلافاصله بعد از بیدار شدن از خواب

پ.ن: الان تو کدوم ظرف برنجیم؟
یا شاید ظرف ها؟
Forwarded from thisisnabi.dev [Farsi]
کتاب خوب بخونیم.

این کتابه کمی گم هست و کم دیدم در موردش صحبت کنن، ولی واقعا کتاب خوبی هست. دیشب توی میت های 10x developer معرفیش کردم، گفتم اینجا هم برای شما هم بذارمش شاید کسی علاقه داشت بخوندش.

کلا این سری کتاب های pragmatic خیلی ارزش خوندن دارن و نگاه آدم رو نسبت به موضوعی که قرار هست مطرح کنن، تغییر می دن.

@thisisnabi_dev