C# Geeks (.NET)
413 subscribers
152 photos
4 videos
153 links
Download Telegram
#مهندس_فکر_کن
قسمت:2️⃣

🎯 مصاحبه‌کننده:
فرض کنید یک سیستم Microservice دارید.
سرویس A یک سفارش ثبت می‌کند.
سرویس B پرداخت را انجام می‌دهد.
سرویس C موجودی انبار را کم می‌کند.
سرویس D پیامک ارسال می‌کند.
در میانه کار، پرداخت موفق انجام می‌شود اما سرویس انبار از دسترس خارج می‌شود.

سؤال:
آیا باید یک Transaction سراسری تعریف کنیم تا اگر یکی از سرویس‌ها شکست خورد، همه چیز Rollback شود؟دلیل؟
Forwarded from .NET Internals
امروز تو شرکت داشتم با یکی از بچه ها درمورد نظریه پنجره شکسته صحبت میکردیم.

ChatGPT:

Broken Window Theory (نظریه پنجره شکسته)
در برنامه‌نویسی یعنی:

اگر یک مشکل کوچک، کد بد یا باگ جزئی را نادیده بگیریم، کم‌کم افراد بیشتری هم بی‌نظمی را عادی می‌بینند و کیفیت کل پروژه افت می‌کند.

مثال:

* یک فایل پر از کامنت‌های قدیمی و بی‌استفاده است.
* یک تابع اسم نامناسبی دارد.
* چند خط کد تکراری وجود دارد.

اگر این موارد اصلاح نشوند، برنامه‌نویس‌های بعدی هم با خیال راحت کدهای نامرتب بیشتری اضافه می‌کنند. بعد از مدتی پروژه پر از «بدهی فنی» (Technical Debt) می‌شود و نگهداری آن سخت خواهد شد.

🔹 اسم این نظریه از یک مثال شهری آمده: اگر پنجره یک ساختمان شکسته باشد و تعمیر نشود، افراد تصور می‌کنند کسی به آنجا اهمیت نمی‌دهد و خرابکاری‌های بیشتری رخ می‌دهد.

پیام اصلی برای برنامه‌نویس‌ها:

> مشکلات کوچک را زود برطرف کن؛ نگذار کد نامرتب و خراب به حالت عادی تبدیل شود.

به همین دلیل تیم‌های حرفه‌ای معمولاً:

* باگ‌های کوچک را سریع رفع می‌کنند.
* کد را مرتب و خوانا نگه می‌دارند.
* قبل از Merge شدن، Code Review انجام می‌دهند.
* تست‌ها را خراب رها نمی‌کنند.

خلاصه: یک «پنجره شکسته» در کد، اگر تعمیر نشود، می‌تواند کل پروژه را به‌تدریج به هم بریزد. 🚪🔧💻

---------------
کوتاهش اینه که باید حس مسولیت پذیری داشته باشید اگر میبینید کدی خرابه یا یه جایی درست نیست ازش رد نشید و بگید این کد رو من نزدم با لید خودتون درمیون بذارید و حلش کنید یا حداقل دلیلشو بپرسید.
در یکی از پروژه‌ها، یک توسعه‌دهنده جدید به تیم اضافه شده بود.
چند هفته بعد از شروع کارش، در یکی از جلسات گفت:
«چرا این بخش سیستم اینقدر پیچیده طراحی شده؟»
چند نفر از اعضای قدیمی تیم لبخند زدند.
یکی از آن‌ها گفت:
«چون تو هنوز داستانش رو نمی‌دونی.»
و واقعاً همین‌طور بود.پشت آن طراحی عجیب، چند سال تصمیم‌گیری، محدودیت، شکست، تغییر نیازمندی و تجربه پنهان شده بود.
اتفاق جالبی که بارها در پروژه‌ها دیده‌ام این است که ما معمولاً فقط نتیجه تصمیم‌ها را می‌بینیم.
نه شرایطی که آن تصمیم‌ها در آن گرفته شده‌اند.
برای همین گاهی قضاوت کردن خیلی آسان به نظر می‌رسد.
«من بودم اینطوری طراحی نمی‌کردم.»
«این معماری اشتباهه.»
«این کد باید از اول نوشته بشه.»
اما حقیقت این است که بیشتر سیستم‌ها در شرایط ایده‌آل ساخته نمی‌شوند.
در محدودیت زمان ساخته می‌شوند.
در محدودیت بودجه.
در فشار کسب‌وکار.
در شرایطی که اطلاعات کامل در دسترس نیست.
و شاید یکی از نشانه‌های بلوغ حرفه‌ای این باشد که قبل از قضاوت یک تصمیم، سعی کنیم زمینه شکل‌گیری آن را بفهمیم.
نه برای اینکه از هر تصمیمی دفاع کنیم.
برای اینکه بدانیم طراحی سیستم‌ها را فقط با نگاه کردن به کد نمی‌شود ارزیابی کرد.
باید داستان پشت آن کد را هم فهمید.
چون خیلی وقت‌ها چیزی که امروز اشتباه به نظر می‌رسد،
دیروز بهترین تصمیم ممکن بوده است.
Forwarded from tech-afternoon (Amin Mesbahi)
کوییز:

توی سیستم‌های توزیع‌شده، مخصوصا وقتی چند سرویس به واسطه پیام‌ها، eventها یا APIها با هم sync می‌شن، Reconciliation Job بیشتر برای چه کاری استفاده می‌شه؟
Final Results
13%
جایگزین کردن Message Broker با یک batch job
22%
تضمین exactly-once delivery بین همه سرویس‌ها
57%
مقایسه دوره‌ای وضعیت چند سیستم و پیدا کردن یا اصلاح مغایرت‌ها
5%
حذف نیاز به Outbox/Inbox Pattern
3%
افزایش سرعت response time در APIهای synchronous
Forwarded from .NET Fun
Media is too big
VIEW IN TELEGRAM
سال‌ها منتظرش بودیم؛ Union Types بالاخره به C# آمدند، اما...


@DotNetIsFun
🚀 یک Junior Developer باید ۵ الگوی معماری را بداند
⚙️ یک Middle Developer باید ۸ الگوی معماری را بداند
🏗 یک Senior Developer باید ۲۰ الگوی معماری را بداند

👉 Junior Developer 👨‍💻

در این مرحله، الگوها به شما کمک می‌کنند ساختار و جریان سیستم را درک کنید. 📚
1️⃣ Layered Architecture
🎨 UI → 🧠 Application → 📦 Domain → 🔧 Infrastructure
تفکیک شفاف مسئولیت‌ها.

2️⃣ Client–Server
🌐 Frontend در مقابل Backend
🔌 APIها به عنوان قرارداد
📨 درخواست‌های Stateless

3️⃣ Monolith
📦 یک واحد قابل استقرار
🛠 ساده برای توسعه، دیباگ و درک
بهترین انتخاب پیش‌فرض

4️⃣ Basic CRUD Architecture
Create
📖 Read
✏️ Update
Delete
پایه و اساس اکثر سیستم‌های کسب‌وکار.

5️⃣ Synchronous Request–Response
🔄 ارتباط ساده مبتنی بر HTTP
🔍 ردیابی و دیباگ آسان

👉 Middle Developer 👨‍💻

درک کنید که سیستم‌ها چگونه ساختاربندی و به یکدیگر متصل می‌شوند. 🏗

1️⃣ Modular Monolith
📦 مرزبندی شفاف درون یک Deployable
🧩 ماژول‌های مستقل بدون پیچیدگی سیستم‌های توزیع‌شده

2️⃣ API Gateway
🚪 نقطه ورود واحد برای کلاینت‌ها
🔀 Routing
🔐 Authentication
⏱️ Rate Limiting
📊 Aggregation
3️⃣ CQRS (Command Query Responsibility Segregation)
✍️ جداسازی مدل‌های خواندن و نوشتن
📈 بهبود Performance و Scalability در صورت نیاز واقعی

4️⃣ Event-Driven Architecture
📨 ارتباط ناهمگام (Asynchronous)
🔗 ءCoupling کمتر بین اجزا

5️⃣ Publish / Subscribe
📢 تولیدکننده‌ها مصرف‌کننده‌ها را نمی‌شناسند
📡 مناسب برای سناریوهای Fan-Out

6️⃣ Point-to-Point Async Integration
📬 صف‌ها بین سرویس‌ها
تحویل قابل اعتماد پیام‌ها

7️⃣ Outbox Pattern
📦 تضمین ارسال پیام همراه با سازگاری پایگاه داده
🚫 جلوگیری از مشکل Dual-Write

8️⃣ Replication Pattern
📖 ءRead Replica برای مقیاس‌پذیری خواندن
🟢 افزایش Availability

👉 Senior Developer 👨‍💻

در این سطح، سیستم‌هایی طراحی می‌کنید که در مقیاس بزرگ و تحت بار واقعی دوام بیاورند. 🚀
در اینجا معماری دیگر فقط درباره Patternها نیست؛
بلکه درباره Trade-offها و پیامدهای هر تصمیم است. ⚖️

1️⃣ Saga Pattern
🔄 مدیریت تراکنش‌های توزیع‌شده
🩹 جبران خطا (Compensation) به جای Rollback

2️⃣ Anti-Corruption Layer (ACL)
🛡 محافظت از Domain در برابر سیستم‌های خارجی
🚫 جلوگیری از آلودگی مدل دامنه

3️⃣ Strangler Fig Pattern
🌱 مدرن‌سازی تدریجی
🔄 جایگزینی امن سیستم‌های Legacy

4️⃣ Sidecar Pattern
📦 مدیریت Cross-Cutting Concernها خارج از Business Logic
📈 Observability
🔐 Security
🛡 Resilience
5️⃣ Service Discovery Pattern
📍 کشف پویا محل سرویس‌ها
⚙️ ضروری در مقیاس بزرگ

6️⃣ Sharding Pattern
🗂 تقسیم داده‌ها برای افزایش مقیاس‌پذیری نوشتن
⚠️ پیچیده اما قدرتمند

7️⃣ Replication + Sharding Trade-offs
⚖️ Consistency در برابر Availability
⚡️ Latency در برابر Correctness

8️⃣ چه زمانی نباید از یک Pattern استفاده کرد؟
Microservices خیلی زود
CQRS بدون نیاز واقعی
Eventها بدون Observability

9️⃣ مسیر تکامل سیستم‌ها
📦 Monolith
➡️ Modular Monolith
➡️ Microservices
🚀 تغییرات تدریجی، نه بازنویسی‌های بزرگ
💡 دانستن Patternها شما را Senior نمی‌کند.

ءSenior بودن یعنی بدانید:
چه زمانی از یک Pattern استفاده کنید.
چه زمانی از آن استفاده نکنید.
و هزینه هر تصمیم معماری را قبل از پرداختن آن بشناسید.
در یکی از پروژه‌ها یک تصمیم کوچک گرفتیم:
برای افزایش سرعت توسعه، 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 برای شرایط استثنایی.
نرم‌افزار هیچ‌وقت «تمام‌شده» نیست. و دقیقاً به همین دلیل است که باید در مسیر، موفقیت‌های کوچک را جشن گرفت.

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

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

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

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