<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های میر مجتبی هاشمی جنتی</title>
        <link>https://virgool.io/feed/@s.mojtaba-hashemi</link>
        <description>دانش آموخته مهندسی نرم افزار | فعال در صنعت | یک برنامه نویس ساده</description>
        <language>fa</language>
        <pubDate>2026-04-14 09:07:29</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/941132/avatar/sg5Pav.jpg?height=120&amp;width=120</url>
            <title>میر مجتبی هاشمی جنتی</title>
            <link>https://virgool.io/@s.mojtaba-hashemi</link>
        </image>

                    <item>
                <title>Isolation Level در SQL</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/isolation-level-%D8%AF%D8%B1-sql-jneeec4vaa0x</link>
                <description>مقدمهبا گسترش سامانه‌های نرم‌افزاری مبتنی بر پایگاه داده و افزایش همزمانی در دسترسی کاربران و سرویس‌ها به داده‌ها، مسئله‌ی مدیریت صحیح همزمانی به یکی از چالش‌های بنیادین در طراحی سیستم‌های اطلاعاتی تبدیل شده است. در چنین محیط‌هایی، عملیات مختلفی به‌طور همزمان اقدام به خواندن و تغییر داده‌های مشترک می‌کنند و در صورت نبود سازوکارهای کنترلی مناسب، احتمال بروز ناسازگاری، نتایج غیرقابل پیش‌بینی و نقض یکپارچگی داده‌ها به‌طور قابل توجهی افزایش می‌یابد.برای پاسخ به این چالش، پایگاه‌های داده رابطه‌ای از مفهوم تراکنش (Transaction) استفاده می‌کنند. تراکنش مجموعه‌ای از عملیات پایگاه داده است که باید به‌صورت یک واحد منطقی و غیرقابل تجزیه اجرا شود و ویژگی‌های شناخته‌شده‌ی ACID—شامل اتمی بودن (Atomicity)، سازگاری (Consistency)، جداسازی (Isolation) و ماندگاری (Durability)—را رعایت کند. در میان این ویژگی‌ها، «جداسازی» نقشی کلیدی در تعیین نحوه‌ی تعامل تراکنش‌های همزمان با یکدیگر ایفا می‌کند.Isolation Level یا سطح جداسازی، مشخص می‌کند که یک تراکنش تا چه حد از تغییرات ایجادشده توسط سایر تراکنش‌های همزمان آگاه باشد و چه نوع ناهنجاری‌هایی در خواندن داده‌ها مجاز یا غیرمجاز تلقی شوند. استاندارد SQL با هدف ایجاد تعادل میان صحت داده‌ها و کارایی سیستم، چهار سطح اصلی برای جداسازی تراکنش‌ها تعریف کرده است. هر یک از این سطوح، درجه‌ای متفاوت از محدودیت را بر دسترسی همزمان به داده‌ها اعمال می‌کند و در نتیجه، رفتار سیستم را در شرایط بار کاری مختلف تغییر می‌دهد.اهمیت Isolation Level تنها به مباحث نظری محدود نمی‌شود، بلکه تأثیر آن به‌طور مستقیم در طراحی سیستم‌های واقعی، به‌ویژه سامانه‌های مالی، بانکی، تحلیلی و توزیع‌شده قابل مشاهده است. انتخاب نادرست سطح جداسازی می‌تواند منجر به بروز خطاهایی شود که شناسایی و بازتولید آن‌ها دشوار است، در حالی که انتخاب بیش از حد محافظه‌کارانه نیز ممکن است کارایی و مقیاس‌پذیری سیستم را به‌شدت کاهش دهد. از این رو، درک دقیق سطوح مختلف Isolation Level و پیامدهای عملی هر یک، برای طراحان و توسعه‌دهندگان سیستم‌های مبتنی بر پایگاه داده امری ضروری محسوب می‌شود.در این مقاله سعی میکنم با رویکردی تحلیلی، سطوح مختلف Isolation Level در SQL را معرفی کرده و با ارائه‌ی مثال‌های عملی و کوئری‌های واقعی، تأثیر هر سطح را بر رفتار تراکنش‌های همزمان بررسی کنیم. هدف اصلی، ایجاد درکی عمیق و کاربردی از این مفهوم به‌منظور اتخاذ تصمیم‌های آگاهانه در طراحی و پیاده‌سازی سیستم‌های پایگاه داده است.سطوح Isolation Level در SQL استاندارد SQL برای مدیریت همزمانی تراکنش‌ها، چهار سطح اصلی Isolation Level را تعریف می‌کند که هر یک میزان مشخصی از جداسازی را میان تراکنش‌های همزمان اعمال می‌کنند. هدف از این سطوح، ایجاد تعادل میان صحت داده‌ها و کارایی سیستم است؛ به این معنا که هرچه محدودیت‌ها سخت‌گیرانه‌تر باشند، احتمال بروز ناهنجاری‌های همزمانی کاهش می‌یابد، اما در مقابل، سربار پردازشی و کاهش همزمانی افزایش پیدا می‌کند. علاوه بر این سطوح استاندارد، برخی سامانه‌های مدیریت پایگاه داده مفهومی به نام Snapshot Isolation را نیز ارائه می‌دهند که اگرچه جزو سطوح رسمی استاندارد SQL نیست، اما در عمل نقش مهمی در معماری سیستم‌های مدرن ایفا می‌کند.سطح Read Uncommittedپایین‌ترین سطح جداسازی، Read Uncommitted است. در این سطح، یک تراکنش می‌تواند تغییرات اعمال‌شده توسط سایر تراکنش‌ها را حتی پیش از commit شدن مشاهده کند. این رفتار منجر به پدیده Dirty Read می‌شود؛ حالتی که در آن داده‌ی خوانده‌شده ممکن است در ادامه rollback شود و هرگز به وضعیت پایدار نرسد. Read Uncommitted کمترین سطح تضمین سازگاری را فراهم می‌کند و معمولاً تنها در سناریوهایی با حساسیت پایین و تمرکز بر بیشینه‌سازی کارایی مورد استفاده قرار می‌گیرد.ابتدا Transaction A را در نظر بگیرید:SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN TRANSACTION;

UPDATE accounts
SET balance = 2000
WHERE id = 1;در این لحظه، مقدار balance به 2000 تغییر کرده اما هنوز commit نشده است. حال Transaction B به‌صورت همزمان اجرا می‌شود:SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN TRANSACTION;

SELECT balance
FROM accounts
WHERE id = 1;خروجی این کوئری مقدار 2000 خواهد بود، در حالی که این مقدار هنوز نهایی نشده است. حال اگر Transaction A دچار خطا شود و rollback گردد:ROLLBACK;داده به مقدار قبلی یعنی 1000 بازمی‌گردد، اما Transaction B عددی را مشاهده کرده که در نهایت هرگز وجود نداشته است. این مثال به‌صورت عملی نشان می‌دهد چرا Dirty Read می‌تواند منجر به تحلیل‌های نادرست شود.سطح Read Committedسطح Read Committed تضمین می‌کند که هر تراکنش فقط داده‌هایی را مشاهده کند که commit شده‌اند. در این سطح، Dirty Read حذف می‌شود و هر خواندن داده مبتنی بر وضعیت پایدار پایگاه داده است. با این حال، Read Committed همچنان اجازه‌ی بروز Non-Repeatable Read را می‌دهد؛ به این معنا که یک تراکنش ممکن است در خواندن‌های مکرر از یک ردیف مشخص، مقادیر متفاوتی دریافت کند. این رفتار نتیجه‌ی commit شدن تغییرات توسط تراکنش‌های دیگر در فاصله‌ی بین دو عملیات خواندن است.Transaction A را به شکل زیر در نظر بگیرید:SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN TRANSACTION;

SELECT balance
FROM accounts
WHERE id = 1;خروجی در این مرحله مقدار 1000 است. حال Transaction B اجرا می‌شود:SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN TRANSACTION;

UPDATE accounts
SET balance = 1500
WHERE id = 1;

COMMIT;پس از commit شدن Transaction B، Transaction A دوباره همان داده را می‌خواند:SELECT balance
FROM accounts
WHERE id = 1;این بار خروجی 1500 خواهد بود. در نتیجه، یک تراکنش واحد در دو بار خواندن یک ردیف، دو مقدار متفاوت دریافت کرده است. این دقیقاً مصداق Non-Repeatable Read است و می‌تواند در محاسبات تجمعی یا گزارش‌گیری مشکل‌ساز شود.سطح Repeatable Readدر سطح Repeatable Read، پایگاه داده تضمین می‌کند که ردیف‌هایی که یک تراکنش آن‌ها را خوانده است، تا پایان عمر آن تراکنش تغییر نخواهند کرد. به این ترتیب، Non-Repeatable Read حذف می‌شود و ثبات داده‌های خوانده‌شده افزایش می‌یابد. با وجود این، Repeatable Read لزوماً مانع بروز Phantom Read نمی‌شود. در چنین شرایطی، ممکن است یک تراکنش در اجرای مجدد یک کوئری شرطی، ردیف‌های جدیدی را مشاهده کند که در اجرای اولیه وجود نداشته‌اند.Transaction A به‌صورت زیر آغاز می‌شود:SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION;

SELECT balance
FROM accounts
WHERE id = 1;خروجی مقدار 1500 است. حال Transaction B تلاش می‌کند مقدار را تغییر دهد:SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION;

UPDATE accounts
SET balance = 3000
WHERE id = 1;

COMMIT;Transaction A دوباره داده را می‌خواند:SELECT balance
FROM accounts
WHERE id = 1;خروجی همچنان 1500 خواهد بود و تغییر انجام‌شده توسط Transaction B تا پایان Transaction A قابل مشاهده نیست. این رفتار نشان می‌دهد که Non-Repeatable Read در این سطح حذف شده است.اما حال سناریوی Phantom Read را بررسی می‌کنیم. فرض کنید Transaction A کوئری زیر را اجرا می‌کند:SELECT *
FROM accounts
WHERE balance &gt; 1000;در همین زمان، Transaction B یک ردیف جدید درج می‌کند:INSERT INTO accounts (id, balance)
VALUES (2, 5000);

COMMIT;اگر Transaction A مجدداً همان کوئری را اجرا کند، ممکن است ردیف جدید را مشاهده کند. این ردیف در اجرای اول وجود نداشته و اکنون ظاهر شده است؛ این پدیده Phantom Read نام دارد.سطح Serializableبالاترین سطح استاندارد جداسازی، Serializable است. در این سطح، پایگاه داده تضمین می‌کند که نتیجه‌ی اجرای تراکنش‌های همزمان معادل اجرای کاملاً ترتیبی آن‌ها باشد. در نتیجه، تمامی ناهنجاری‌های شناخته‌شده از جمله Dirty Read، Non-Repeatable Read و Phantom Read حذف می‌شوند. این سطح بالاترین میزان سازگاری را فراهم می‌کند، اما هزینه‌ی آن کاهش همزمانی، افزایش قفل‌گذاری و احتمال افت محسوس کارایی سیستم است. به همین دلیل، Serializable معمولاً برای عملیات‌های حیاتی و بسیار حساس مورد استفاده قرار می‌گیرد.Transaction A به شکل زیر شروع می‌شود:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;

SELECT *
FROM accounts
WHERE balance &gt; 1000;در این حالت، پایگاه داده معمولاً قفل‌هایی اعمال می‌کند که مانع از درج یا تغییر داده‌های مؤثر بر این نتیجه می‌شود. اگر Transaction B تلاش کند رکورد جدیدی درج کند:INSERT INTO accounts (id, balance)
VALUES (3, 7000);این دستور یا تا پایان Transaction A بلاک می‌شود یا با خطا مواجه خواهد شد (بسته به DBMS). در نتیجه، نه Dirty Read، نه Non-Repeatable Read و نه Phantom Read رخ نخواهد داد. این رفتار بالاترین سطح سازگاری داده را تضمین می‌کند، هرچند به قیمت کاهش همزمانی.موضوع Snapshot Isolationدر کنار این سطوح، Snapshot Isolation رویکردی متفاوت برای مدیریت همزمانی ارائه می‌دهد. در این مدل، هر تراکنش هنگام شروع، یک «تصویر ثابت» یا Snapshot از وضعیت پایگاه داده دریافت می‌کند و تمامی عملیات خواندن خود را بر اساس همان تصویر انجام می‌دهد. به این ترتیب، داده‌هایی که یک تراکنش می‌خواند در طول عمر آن ثابت باقی می‌مانند، بدون آنکه نیاز به قفل‌گذاری گسترده برای عملیات خواندن وجود داشته باشد. Snapshot Isolation معمولاً مبتنی بر مکانیزم کنترل همزمانی چندنسخه‌ای (MVCC) است و باعث می‌شود خواندن‌ها مسدودکننده‌ی نوشتن‌ها نباشند و بالعکس. در مورد مکانیزم کنترل همزمانی چندنسخه‌ای (MVCC) بیشتر صحبت خواهیم کرد. Snapshot Isolation از بروز Dirty Read و Non-Repeatable Read جلوگیری می‌کند و در عمل Phantom Read را نیز در بسیاری از سناریوها حذف می‌نماید. با این حال، این سطح به‌طور کامل معادل Serializable نیست و ممکن است ناهنجاری‌هایی مانند Write Skew در آن رخ دهد؛ حالتی که در آن دو تراکنش بر اساس یک Snapshot مشترک تصمیم‌گیری می‌کنند و در نهایت، نتیجه‌ای ناسازگار با منطق کسب‌وکار ایجاد می‌شود. به همین دلیل، Snapshot Isolation را می‌توان سطحی میانی میان Repeatable Read و Serializable دانست که با هدف افزایش کارایی و کاهش قفل‌گذاری طراحی شده است.Isolation Levelدر مجموع، Isolation Level‌ها و Snapshot Isolation چارچوب‌های متفاوتی برای مدیریت همزمانی در پایگاه‌های داده فراهم می‌کنند. انتخاب میان این گزینه‌ها باید بر اساس نیازمندی‌های سازگاری، الگوی دسترسی به داده و الزامات کارایی سیستم انجام شود. درک دقیق تفاوت‌های مفهومی و عملی این سطوح، پیش‌نیاز طراحی سیستم‌های پایگاه داده‌ی قابل اعتماد و مقیاس‌پذیر است.</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Sat, 28 Feb 2026 10:29:34 +0330</pubDate>
            </item>
                    <item>
                <title>OKR و KPI؛ دو ابزار، دو نگاه متفاوت به مدیریت عملکرد</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/okr-%D9%88-kpi-%D8%AF%D9%88-%D8%A7%D8%A8%D8%B2%D8%A7%D8%B1-%D8%AF%D9%88-%D9%86%DA%AF%D8%A7%D9%87-%D9%85%D8%AA%D9%81%D8%A7%D9%88%D8%AA-%D8%A8%D9%87-%D9%85%D8%AF%DB%8C%D8%B1%DB%8C%D8%AA-%D8%B9%D9%85%D9%84%DA%A9%D8%B1%D8%AF-jafh7no8ffpd</link>
                <description>سلام. در این مقاله به بررسی دو موضوع و ابزار مهم KPI و OKR خواهیم پرداخت و روابط این دو رو باهم دیگه بررسی میکنیم. در فایل صوتی، مثال های مصداقی برای شرکت های نرم افزاری رو آوردم که بهتر بتونیم موضوع رو درک کنیم. در سال‌های اخیر، مفاهیمی مثل OKR و KPI بیش از هر زمان دیگری وارد ادبیات مدیریتی، استارتاپی و حتی تیم‌های کوچک شده‌اند. تقریباً همه اسمشان را شنیده‌اند، اما در عمل هنوز ابهام‌های زیادی درباره‌ی تفاوت، کاربرد و حتی ترتیب استفاده از آن‌ها وجود دارد. بعضی تیم‌ها KPI دارند اما نمی‌دانند چرا اندازه‌گیری می‌کنند، بعضی دیگر OKR تعریف می‌کنند اما نمی‌دانند قرار است چه تغییری ایجاد شود. این مقاله تلاشی است برای روشن کردن مفاهیم پایه‌ای OKR و KPI، بدون پیچیدگی‌های آکادمیک، اما با دقت مفهومی.سنجش عملکردKPI چیست و چرا به وجود آمد؟KPI یا Key Performance Indicator به‌طور ساده یعنی «شاخص کلیدی عملکرد». KPI به ما می‌گوید که وضعیت فعلی ما چگونه است و آیا عملکردمان در یک مسیر مشخص، قابل قبول هست یا نه. KPIها معمولاً عددی هستند و با اندازه‌گیری مداوم، به ما تصویر نسبتاً دقیقی از سلامت یک فرایند، تیم یا کسب‌وکار می‌دهند.ریشه‌ی KPI بیشتر در مدیریت کلاسیک و کنترل عملکرد است. زمانی که سازمان‌ها بزرگ‌تر شدند، مدیران دیگر نمی‌توانستند صرفاً با مشاهده‌ی مستقیم تصمیم بگیرند. آن‌ها نیاز داشتند بدانند فروش چقدر است، هزینه‌ها چطور تغییر کرده، نرخ خطا چقدر شده و بهره‌وری کارکنان در چه وضعیتی است. KPIها دقیقاً برای پاسخ به همین نیاز به وجود آمدند: دیدن واقعیت عملکرد، آن هم به زبان عدد.ویژگی‌های اصلی KPIKPIها معمولاً پایدار هستند و به‌راحتی تغییر نمی‌کنند. اگر KPI «نرخ تبدیل کاربران» یا «درآمد ماهانه» باشد، این شاخص‌ها قرار نیست هر هفته عوض شوند. KPI بیشتر شبیه یک دماسنج است؛ قرار نیست تب را درمان کند، فقط نشان می‌دهد بدن در چه وضعیتی است.نکته‌ی مهم این است که KPI الزاماً انگیزه‌بخش نیست. دانستن اینکه نرخ ریزش مشتریان ۱۲٪ است، لزوماً به تیم نمی‌گوید چه تغییری باید ایجاد کند. KPI بیشتر ابزار «پایش» است تا «تحول».OKR چیست و چه مسئله‌ای را حل می‌کند؟OKR مخفف Objectives and Key Results است. برخلاف KPI که از دل نیاز به کنترل بیرون آمده، OKR از دل نیاز به جهت‌دهی و تمرکز متولد شده است. OKR تلاش می‌کند به این سؤال پاسخ دهد: «می‌خواهیم به کجا برسیم و از کجا بفهمیم به آن رسیده‌ایم؟»OKR از دو بخش تشکیل می‌شود. Objective یک هدف کیفی، الهام‌بخش و جهت‌دهنده است. چیزی که قرار است وضعیت آینده را بهتر از امروز کند. Key Resultها معیارهای سنجش رسیدن به آن هدف هستند؛ اما نه هر معیاری، بلکه معیارهایی که نشان‌دهنده‌ی تغییر واقعی باشند.تفاوت نگاه OKR با KPIیکی از تفاوت‌های اساسی OKR و KPI در نگاه آن‌ها به «تغییر» است. KPI معمولاً وضعیت موجود را اندازه‌گیری می‌کند، اما OKR برای تغییر وضعیت موجود طراحی شده است. وقتی تیمی OKR تعریف می‌کند، در واقع می‌پذیرد که وضعیت فعلی کافی نیست و باید بهتر شود.OKRها معمولاً بازه‌ی زمانی مشخص دارند (مثلاً فصلی) و انتظار نمی‌رود همه‌ی آن‌ها ۱۰۰٪ محقق شوند. حتی در فلسفه‌ی OKR، رسیدن به حدود ۶۰ تا ۷۰ درصد یک OKR نشانه‌ی خوب طراحی شدن آن است. این موضوع کاملاً برخلاف KPI است که معمولاً باید به‌طور کامل محقق شود.Key Result با KPI چه فرقی دارد؟در نگاه اول، Key Result خیلی شبیه KPI به نظر می‌رسد، چون هر دو عددی هستند. اما تفاوت در «نقش» آن‌هاست. KPI یک شاخص پایدار برای پایش عملکرد است، اما Key Result یک معیار موقتی برای سنجش موفقیت یک هدف مشخص است.ممکن است یک KPI سال‌ها ثابت بماند، اما Key Resultها با تغییر Objective عوض می‌شوند. KPI به ما می‌گوید «اوضاع چطور است»، اما Key Result به ما می‌گوید «آیا به هدفمان نزدیک شده‌ایم یا نه».آیا OKR جایگزین KPI است؟یکی از رایج‌ترین سوءتفاهم‌ها این است که OKR آمده تا KPI را حذف کند. در واقع، این دو ابزار نه رقیب هم هستند و نه جایگزین یکدیگر. KPI بدون OKR ممکن است به اندازه‌گیری بی‌هدف منجر شود، و OKR بدون KPI ممکن است از واقعیت فاصله بگیرد.در عمل، KPIها می‌توانند به‌عنوان ورودی برای طراحی OKR استفاده شوند. یعنی ابتدا بفهمیم وضعیت فعلی چگونه است (با KPI) و بعد تصمیم بگیریم چه چیزی باید تغییر کند (با OKR). همچنین بعضی KPIها می‌توانند در طول اجرای OKR به‌عنوان شاخص‌های سلامت کسب‌وکار پایش شوند.اشتباهات رایج در استفاده از OKR و KPIیکی از اشتباهات رایج این است که سازمان‌ها OKR را به KPI تبدیل می‌کنند. یعنی به‌جای هدف‌گذاری تحول‌محور، فقط اعداد آشنا را در قالب جدید می‌نویسند. اشتباه دیگر این است که KPIهای زیادی تعریف می‌شود، بدون اینکه کسی واقعاً از آن‌ها برای تصمیم‌گیری استفاده کند.همچنین، استفاده از OKR به‌عنوان ابزار ارزیابی عملکرد فردی، یکی از خطرناک‌ترین انحراف‌هاست. OKR برای یادگیری و هم‌راستاسازی طراحی شده، نه برای تنبیه و پاداش مستقیم.جمع‌بندیKPI و OKR هر دو ابزارهای مهمی هستند، اما برای دو هدف متفاوت. KPI به ما کمک می‌کند بفهمیم کجا ایستاده‌ایم، OKR به ما کمک می‌کند تصمیم بگیریم به کجا برویم. ترکیب درست این دو، می‌تواند هم شفافیت ایجاد کند و هم حرکت.</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Tue, 24 Feb 2026 12:15:24 +0330</pubDate>
            </item>
                    <item>
                <title>کارکنان و نیرو های ستادی و صف</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/%DA%A9%D8%A7%D8%B1%DA%A9%D9%86%D8%A7%D9%86-%D9%88-%D9%86%DB%8C%D8%B1%D9%88-%D9%87%D8%A7%DB%8C-%D8%B3%D8%AA%D8%A7%D8%AF%DB%8C-%D9%88-%D8%B5%D9%81-eqrazyx2czhu</link>
                <description>سلام. دوستان امروز میخوام در مورد بحث مدیریت و کارکنان نیرو های ستادی و صف در سازمان ها یک گفتگویی داشته باشیم. بیشتر مد نظرم روی سازمان های نرم افزاری خواهد بود.در ادبیات مدیریت سازمان، تفکیک میان «نیروهای صف» و «نیروهای ستادی» یکی از مفاهیم بنیادینی است که اگر به‌درستی فهم نشود، به‌ویژه در سازمان‌های نرم‌افزاری، می‌تواند به سوء‌برداشت‌های مدیریتی، تنش‌های درون‌سازمانی و تصمیم‌گیری‌های نادرست منجر شود. این مفاهیم در ظاهر ساده‌اند، اما در عمل لایه‌های عمیق‌تری دارند که شناخت آن‌ها برای مدیران، خصوصاً مدیرانی که از مسیر فنی به مدیریت رسیده‌اند، ضروری است.نیروهای صف به‌طور کلاسیک به آن دسته از افراد و تیم‌ها گفته می‌شود که مستقیماً در تحقق مأموریت اصلی سازمان نقش دارند. اگر سازمان را یک زنجیره ارزش در نظر بگیریم، نیروهای صف همان حلقه‌هایی هستند که خروجی نهایی سازمان بدون آن‌ها معنا ندارد. در یک سازمان نرم‌افزاری، توسعه‌دهندگان، تیم‌های محصول، تست، و گاهی حتی پشتیبانی فنی، معمولاً در این دسته قرار می‌گیرند؛ چرا که محصول نرم‌افزاری، که دلیل وجودی سازمان است، مستقیماً توسط آن‌ها خلق، تکمیل و تحویل می‌شود. قدرت نیروهای صف نه از جایگاه سازمانی، بلکه از نزدیکی آن‌ها به «کار واقعی» ناشی می‌شود.در مقابل، نیروهای ستادی نقش پشتیبان، تسهیل‌گر و تنظیم‌کننده دارند. این نیروها مستقیماً محصول نهایی را تولید نمی‌کنند، اما شرایطی را فراهم می‌کنند که نیروهای صف بتوانند مؤثرتر، پایدارتر و با ریسک کمتر فعالیت کنند. واحد منابع انسانی، مالی، حقوقی، برنامه‌ریزی، تحلیل فرایندها و حتی در بسیاری از سازمان‌های نرم‌افزاری، تیم‌های PMO یا DevOps در نقش ستادی قرار می‌گیرند. نقش ستاد نه انجام کار اصلی، بلکه افزایش کیفیت تصمیم‌گیری، هماهنگی و کنترل هوشمندانه است.کارکنان و نیرو های ستادی و صفنکته‌ای که اغلب در سازمان‌های نرم‌افزاری نادیده گرفته می‌شود، این است که ستاد و صف نه در تقابل، بلکه در یک رابطه‌ی وابستگی متقابل قرار دارند. مشکل زمانی آغاز می‌شود که نیروهای ستادی تصور کنند «مالک تصمیم» هستند و نیروهای صف را صرفاً مجری ببینند، یا برعکس، نیروهای صف ستاد را سربار، کندکننده یا جدا از واقعیت پروژه تلقی کنند. این شکاف معمولاً ریشه در نبود تعریف شفاف از نقش‌ها و مرز اختیارات دارد، نه در ضعف ذاتی یکی از این دو.در سازمان‌های نرم‌افزاری، این چالش به شکل خاص‌تری بروز می‌کند. بسیاری از مدیران ارشد سابقه فنی دارند و ناخودآگاه ذهنیت صف‌محور دارند؛ یعنی ارزش را فقط در کدنویسی، معماری و تحویل فیچر می‌بینند. در چنین فضایی، نقش‌های ستادی مانند منابع انسانی، برنامه‌ریزی ظرفیت، یا حتی مستندسازی، کم‌اهمیت جلوه داده می‌شوند. نتیجه این نگاه، فرسودگی نیروهای صف، تصمیم‌گیری‌های کوتاه‌مدت و ناپایداری سازمان در مقیاس‌پذیری است.از سوی دیگر، ستاد در سازمان نرم‌افزاری اگر از زبان و منطق صف فاصله بگیرد، به‌سرعت اعتبار خود را از دست می‌دهد. ستادی که بدون درک واقعیت اسپرینت‌ها، بدهی فنی، فشار ددلاین‌ها و پیچیدگی توسعه نرم‌افزار، سیاست‌گذاری می‌کند، ناخواسته به مانع تبدیل می‌شود. بنابراین بلوغ سازمانی زمانی رخ می‌دهد که ستاد، عمیقاً کار صف را بفهمد و صف، ارزش نگاه سیستمی ستاد را درک کند.برای مدیران، درک این تمایز به معنای انتخاب طرف نیست، بلکه به معنای طراحی درست ساختار و روابط است. مدیر بالغ می‌داند که صف بدون ستاد، سریع اما شکننده است، و ستاد بدون صف، منظم اما بی‌اثر. هنر مدیریت، ایجاد تعادلی پویا میان این دو است؛ تعادلی که در آن ستاد قدرت خود را از تخصص و تحلیل می‌گیرد، نه از دستور، و صف نقش خود را نه صرفاً اجرا، بلکه مشارکت آگاهانه در تصمیم‌سازی می‌داند.در نهایت، سازمان نرم‌افزاری موفق سازمانی است که نیروهای صف را قهرمانان تولید ارزش بداند و نیروهای ستادی را معماران پایداری. هرگاه یکی جای دیگری بنشیند یا حذف شود، سازمان یا به هرج‌ومرج می‌رسد یا به بوروکراسی. فهم دقیق نیروهای صف و ستاد، نه یک بحث تئوریک، بلکه پیش‌نیاز رشد سالم و حرفه‌ای سازمان است.</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Mon, 23 Feb 2026 16:07:09 +0330</pubDate>
            </item>
                    <item>
                <title>عارضه‌یابی سازمانی؛</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/%D8%B9%D8%A7%D8%B1%D8%B6%D9%87-%DB%8C%D8%A7%D8%A8%DB%8C-%D8%B3%D8%A7%D8%B2%D9%85%D8%A7%D9%86%DB%8C-sunglvquvcc5</link>
                <description>از مفهوم تا واقعیت در شرکت‌های نرم‌افزاریسلام. امروز میخوام در مورد عارضه یابی سازمانی کمی صحبت کنم و یک مقدمه کوتاهی رو در این مقاله بهش بپردازم و بیشترِ تمرکزم روی این موضوع، در شرکت های مهندسی نرم افزاری باشد.عارضه یابی سازمانی و جلوگیری از فروپاشی هادر طول حدود چندین سال فعالیت مدیریتی‌ام در سازمان‌های مختلف، بارها با این تصور مواجه شده‌ام که مشکل اصلی یک سازمان، کمبود تخصص، نیروی انسانی یا منابع مالی است. اما تجربه به من نشان داده که در بسیاری از موارد، مسئله نه کمبود است و نه ناتوانی؛ بلکه نشناختن درست مسئله‌ی واقعی است. سازمان‌ها اغلب درد دارند، اما محل درد را اشتباه تشخیص می‌دهند. اینجاست که عارضه‌یابی سازمانی معنا پیدا می‌کند.عارضه‌یابی سازمانی، پیش از آن‌که یک ابزار یا مدل باشد، یک نوع نگاه است. نگاهی که تلاش می‌کند قبل از ارائه‌ی راه‌حل، مسئله را در بستر واقعی خود ببیند. بسیاری از مشکلاتی که ما در سازمان‌ها می‌بینیم، در واقع «عارضه» هستند؛ نشانه‌هایی بیرونی از اختلالات عمیق‌تر. تأخیر در تحویل پروژه، نارضایتی مشتری، افت کیفیت محصول یا حتی فرسودگی تیم، معمولاً خودِ مشکل نیستند، بلکه پیامد تصمیم‌ها، ساختارها و رفتارهایی هستند که شاید سال‌ها قبل شکل گرفته‌اند.یکی از اشتباهات رایجی که در سازمان‌ها دیده‌ام، عجله برای درمان است. مدیران، به‌محض دیدن نشانه‌ها، سراغ نسخه می‌روند: تغییر فرایند، اضافه‌کردن نیرو، خرید ابزار جدید یا حتی جابه‌جایی افراد. اما وقتی تشخیص درست نباشد، درمان نه‌تنها مؤثر نیست، بلکه گاهی وضعیت را پیچیده‌تر می‌کند. عارضه‌یابی، در اصل تمرینی برای «صبر مدیریتی» است؛ ایستادن، دیدن و فهمیدن، قبل از اقدام.در شرکت‌های نرم‌افزاری، عارضه‌یابی اهمیت دوچندانی پیدا می‌کند. ماهیت ناملموس محصول، وابستگی شدید به نیروی انسانی و سرعت بالای تغییرات تکنولوژیک، باعث می‌شود مشکلات خیلی زود پنهان شوند یا به‌اشتباه تفسیر شوند. در بسیاری از تیم‌های نرم‌افزاری که با آن‌ها کار کرده‌ام، تا زمانی که پروژه‌ها به‌زور جلو می‌روند، کسی متوجه عمق مسائل نمی‌شود. اما به‌محض اینکه رشد متوقف می‌شود یا خروج نیروها آغاز می‌شود، تازه مشخص می‌شود که عارضه‌ها مدت‌ها قبل شکل گرفته بودند.در شرکت‌های نرم‌افزاری کوچک، عارضه‌یابی اغلب به‌طور مستقیم با خودِ مدیر یا مؤسس گره خورده است. این سازمان‌ها معمولاً وابستگی شدیدی به چند نفر کلیدی دارند و تصمیم‌ها به‌صورت متمرکز گرفته می‌شود. در چنین فضایی، مرز نقش‌ها شفاف نیست و فشار برای تحویل سریع، به‌تدریج تیم را فرسوده می‌کند. تجربه‌ی شخصی من نشان می‌دهد که بسیاری از مشکلات این شرکت‌ها، نه فنی هستند و نه منابعی؛ بلکه ریشه در سبک تصمیم‌گیری و اولویت‌گذاری مدیر دارند. در یکی از تیم‌هایی که با آن کار می‌کردم، مسئله‌ی اصلی تأخیرهای مداوم بود، اما بعد از بررسی مشخص شد که مشکل واقعی، ناتوانی مسئولین مربوطه در مدیریت انتظارات و «نه گفتن» به درخواست‌های غیرواقعی است.با بزرگ‌تر شدن سازمان و ورود به مقیاس متوسط، جنس عارضه‌ها تغییر می‌کند. در این مرحله، سازمان از حالت صمیمی و غیررسمی عبور کرده، اما هنوز به بلوغ ساختاری نرسیده است. ناهماهنگی بین تیم‌ها، فاصله‌ی میان مدیریت و اجرا، و شکل‌گیری مدیران میانی بدون آمادگی لازم، از نشانه‌های رایج این مرحله است. در چنین سازمان‌هایی، مدیران اغلب تصور می‌کنند مشکل از عملکرد تیم‌هاست، در حالی که بررسی عمیق‌تر نشان می‌دهد پیام‌های متناقض مدیریتی، اولویت‌های نامشخص و فرایندهای نیمه‌کاره، عامل اصلی اختلال هستند. عارضه‌یابی در این سطح، بدون گفت‌وگوی صادقانه با تیم‌ها و بدون پذیرش خطا از سوی مدیریت ارشد، عملاً بی‌نتیجه خواهد بود.در سازمان‌های نرم‌افزاری بزرگ، عارضه‌ها معمولاً کمتر دیده می‌شوند، اما اثر مخرب‌تری دارند. بروکراسی، کندی تصمیم‌گیری، از بین رفتن حس مالکیت و فاصله‌ی بین استراتژی و اجرا، به‌تدریج انرژی سازمان را تحلیل می‌برد. در این مقیاس، اگر عارضه‌یابی صرفاً از بالا انجام شود، معمولاً به نتایج سطحی می‌رسد. تجربه‌ی من نشان داده که واقعیت سازمان‌های بزرگ، در لایه‌های عملیاتی جریان دارد، نه در گزارش‌ها و اسلایدها. در یکی از این سازمان‌ها، تصور مدیران این بود که تیم‌ها چابک نیستند، اما عارضه‌یابی نشان داد که سیستم ارزیابی عملکرد، هرگونه ریسک‌پذیری را تنبیه می‌کند و عملاً مانع نوآوری شده است.اگر بخواهم جمع‌بندی شخصی خودم را از این سال‌ها بگویم، عارضه‌یابی سازمانی بدون صداقت، به یک نمایش مدیریتی تبدیل می‌شود. سازمان‌ها معمولاً دقیقاً در همان نقطه‌ای آسیب‌پذیر هستند که دوست ندارند به آن نگاه کنند. هیچ فریم‌ورک، ابزار یا مدلی جای گفت‌وگوی واقعی با آدم‌ها را نمی‌گیرد و عارضه‌یابی درست، اغلب ناراحتی کوتاه‌مدت ایجاد می‌کند، اما سلامت بلندمدت سازمان را تضمین می‌کند.در نهایت، عارضه‌یابی سازمانی نشانه‌ی بحران نیست؛ نشانه‌ی بلوغ است. سازمان‌هایی که حاضرند خودشان را همان‌طور که هستند ببینند، نه آن‌طور که دوست دارند باشند، شانس بیشتری برای رشد پایدار دارند. در صنعت نرم‌افزار، که همه‌چیز به انسان و تصمیم‌های او گره خورده، این شجاعت دیدن، شاید مهم‌ترین مزیت رقابتی باشد.</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Mon, 23 Feb 2026 15:48:59 +0330</pubDate>
            </item>
                    <item>
                <title>مفاهیم پایه برای معماران و تحلیلگران نرم‌افزار</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/%D9%85%D9%81%D8%A7%D9%87%DB%8C%D9%85-%D9%BE%D8%A7%DB%8C%D9%87-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%D8%A7%D9%86-%D9%88-%D8%AA%D8%AD%D9%84%DB%8C%D9%84%DA%AF%D8%B1%D8%A7%D9%86-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-dow1uhtxa6ng</link>
                <description>مفاهیم و موضوعات پایه و اساسی برای معماران و تحلیلگران نرم‌افزار در دنیای مهندسی نرم‌افزارانتقال تجربیات و منتورینگ جوانان در شرکتسلام دوستان. این مقاله بیشتر جنبه اشتراک تجربه رو خواهد داشت. میخوام موضوعاتی رو در این مقاله بیان کنم که برای افرادی که میخوان وارد دنیای مهندسی نرم افزار بشوند یا کسانی که وارد شده اند و در این دنیا دارند کار میکنند، بسیار حائز اهمیت خواهد بود. احتمالاً لحن مقاله برای اکثرا عزیزان مخصوصاً دوستانی که زیاد با فرهنگ اصلاح ساختار های فنی-سازمانی آشنا نیستند، شیرین نخواهد بود. سعی میکنم موضوعات رو به صورت بولت پوینت های کوتاه و قابل فهم در مقاله بنویسم تا مطالعه کمی اسان تر بشود.در سال‌های اخیر، عناوینی مانند Software Architect و Software Analyst به‌سرعت در بازار کار رواج پیدا کرده‌اند. کافی‌ هست چند سالی برنامه‌نویسی کرده باشید، چند نمودار کشیده باشید یا چند کتاب معماری خوانده باشید تا بعضاً خودتان یا دیگران شما را شایسته‌ی این عناوین بدانند.اما واقعیت مهندسی نرم‌افزار، بسیار سخت‌گیرانه‌تر، عمیق‌تر و بی‌رحم‌تر از چیزی است که در رزومه‌ها و لینکدین دیده می‌شود.معماری و تحلیل نرم‌افزار شغل نیستند، مسئولیت‌اند؛ مسئولیت تصمیم‌هایی که می‌توانند یک شرکت را به رشد برسانند یا سال‌ها عقب بیندازند.و اینجاست که سن، تجربه، بلوغ فکری و فهم سازمانی اهمیت حیاتی پیدا می‌کند.تجربه، نه فقط دانشمعماری و تحلیل، نقطه‌ی شروع نیستندیکی از بزرگ‌ترین خطاهای رایج در شرکت‌های نرم‌افزاری این است که معماری و تحلیل را به‌عنوان نقطه‌ی شروع مسیر حرفه‌ای می‌بینند، نه نقطه‌ی بلوغ آن.معمار نرم‌افزار کسی نیست که:فقط الگوها را بشناسدفقط دیاگرام بکشدفقط درباره‌ی تکنولوژی‌ها نظر بدهدمعمار کسی است که:پیامد تصمیم‌ها را سال‌ها بعد پیش‌بینی کندبداند کِی نه بگویدبداند کِی سکوت کندبداند کِی نظر افراد باتجربه‌تر از خود را بپذیرداین‌ها با چند سال کدنویسی یا چند پروژه‌ی کوچک به‌دست نمی‌آیند.سن کم معنیش ناتوانی نیست،ولی میتونه این باشه که صلاحیت کامل ندارید.دوستان من، کم‌سن‌بودن به‌خودیِ‌خود ایراد نیست؛ایراد زمانی شروع می‌شود که کم‌تجربگی با اعتمادبه‌نفس کاذب ترکیب شود.افراد جوان معمولاً:سریع یاد می‌گیرندانرژی بالایی دارندبا تکنولوژی‌های جدید آشناترنداما اغلب:هزینه‌ی تصمیم اشتباه را تجربه نکرده‌اندبا پیچیدگی‌های سازمانی آشنا نیستنداثر تصمیم‌ها بر تیم‌های دیگر را دست‌کم می‌گیرندمعماری نرم‌افزار جایی نیست که «آزمون و خطا» در مقیاس سازمان انجام شود. این رو باید کسی که در این پوزیشن قرار میگیرد، به صراحت بداند.فاجعه‌ی اول: بازنویسی خودسرانه سیستمیک تحلیلگر/معمار جوان تصمیم می‌گیرد:«سیستم فعلی قدیمی است، بیایید کل آن را Microservice کنیم.»بدون:درک محدودیت‌های تیمشناخت فرآیندهای واقعی کسب‌وکارتوجه به توان DevOpsبررسی هزینه‌های عملیاتینتیجه:چندین سرویس نیمه‌کارهافزایش شدید باگتیمی فرسوده و بی‌انگیزهبازگشت دوباره به معماری قبلی با هزینه‌ی چندبرابریاین فاجعه نه به‌خاطر Microservice،بلکه به‌خاطر تصمیم‌گیری بدون تجربه و بدون گوش‌دادن رخ داد.Ownership؛ مالکیت، نه خودخواهییکی از مفاهیم کلیدی در مهندسی نرم‌افزار Ownership است؛اما این مفهوم اغلب اشتباه فهمیده می‌شود.Ownership یعنی:مسئولیت‌پذیری در برابر نتیجهپاسخ‌گویی در برابر شکستدر نظر گرفتن کل سیستم، نه فقط بخش خودتOwnership یعنی من صاحب مسئله‌ام، نه صاحب قدرت.وقتی فرد کم‌تجربه ownership را با «هرکاری دلم خواست انجام بدهم» اشتباه می‌گیرد:ساختار سازمانی نادیده گرفته می‌شودتصمیم‌ها شخصی می‌شوندسیستم قربانی خواسته های فردی می‌شودفاجعه‌ی دوم: نادیده‌گرفتن سلسله‌مراتب سازمانییک معمار جوان:بدون هماهنگی با مدیر فنیبدون هم‌راستاسازی با تیم عملیاتبدون اطلاع تیم‌های دیگرتصمیم معماری کلیدی می‌گیرد.نتیجه:تضاد بین تیم‌هابی‌اعتمادی مدیریتیشکست پروژه، حتی اگر تصمیم فنی درست بوده باشددر مهندسی نرم‌افزار،تصمیم درست در زمان و جای غلط = تصمیم اشتباه است. لازمه درک همین جمله ای که نوشتم، چندین سال کار و تجربه مستمر هست. این رو که به اشتراک میگذاریم، افراد در نظر نمیگیرند.گوش‌دادن، مهارتی مهم‌تر از طراحیافراد باتجربه:کمتر حرف می‌زنندبیشتر سؤال می‌پرسندعجله نمی‌کنندیکی از نشانه‌های بلوغ حرفه‌ای این است که:بدانی همیشه کسی هست که بیشتر از تو می‌داند.نصیحت‌پذیری از افراد باتجربه:نشانه ضعف نیستنشانه حرفه‌ای‌بودن استاین مسئله فقط مخصوص معماران نیستاین الگو در تمام نقش‌ها دیده می‌شود:برنامه‌نویس‌های تازه‌کار که استانداردها را نادیده می‌گیرندلیدهای کم‌تجربه که تیم را فرسوده می‌کنندتحلیلگرانی که مسئله را درست نمی‌فهمند ولی راه‌حل می‌دهندهرجا:ego (مَنیت - مًن مَن کردن) جای تجربه را بگیردسرعت جای بلوغ راتکنولوژی جای فهم مسئله رافاجعه محتمل است.جمع‌بندیمعماری و تحلیل نرم‌افزار:با سال‌ها کار واقعی ساخته می‌شوندبا گوش‌دادن رشد می‌کنندبا احترام به ساختار سازمانی معنا پیدا می‌کننداگر کم‌سن هستید:عجله نکنیدیاد بگیریدگوش بدهیدکار واقعی انجام بدهیدو اگر مسئول تصمیمی هستی که آینده‌ی یک سیستم را می‌سازد یا خراب می‌کند:یادتان باشد، هزینه‌ی اشتباه شما را فقط خودتان نمی‌دهید.دوستان هر کلمه ای که در این مقاله بیان کردم، دارای اهمیت ویژه در پوزیشن معمار و تحلیل گر نرم افزار بود. دوستانی که میخواهند در این مسیر قدم بگذارند یا میخواهند با واقعیت های مسیر رو به رو شوند، دوباره مقاله را بخوانند. با تشکر</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Sun, 22 Feb 2026 14:28:29 +0330</pubDate>
            </item>
                    <item>
                <title>معرفی Apache Lucene</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/%D9%85%D8%B9%D8%B1%D9%81%DB%8C-apache-lucene-aavxceyqpci4</link>
                <description>هسته‌ای که خیلی از موتورهای جستجو روش ایستادن؛سلام. وقتی با حجم زیادی دیتا سر و کار داشته باشیم و بخواهیم روش جستجوی درست‌وحسابی پیاده کنیم، احتمالاً یا مستقیم یا غیرمستقیم کارمون به Lucene خواهد خورد.Lucene در اصل یه کتابخونه‌ی جستجوی متن (Full-Text Search) هست که توسط Apache توسعه داده می‌شه و تمرکزش روی اینه که «جستجو سریع، دقیق و قابل‌کنترل» باشه. نه بیشتر، نه کمتر. یعنی خودش ادعا نمی‌کنه یه محصول آماده‌ست، بلکه ابزار میده دستتمون تا خودمون سیستم جستجویی که میخواهیم رو بسازیم.Lucene به زبان Java نوشته شده و از همون اول هم با این ذهنیت طراحی شده که بتونه روی حجم خیلی زیاد داده جواب بده. چیزی که تو مستندات رسمی خیلی روش تأکید شده اینه که Lucene قراره هسته‌ی جستجو باشه، نه کل سیستم. یعنی خبری از UI، REST API یا دیپلوی آماده نیست؛ اینا چیزاییه که باید خودمون بسازیم یا ابزارهای بالادستی برامون فراهم کنند. معماری lucenceLucene دقیقاً چه کاری انجام میده؟Lucene کارش دو تا چیز اصلیه:ایندکس‌کردن و جستجو کردن.اول داده‌ها (معمولاً متن) رو می‌گیری، بهش میگی چجوری تحلیل بشن (Analyzer)، بعد Lucene ازشون یه ساختار بهینه می‌سازه که اسمش هست Index. بعدش هر وقت کاربر یا سیستم یه Query می‌فرسته، Lucene خیلی سریع میره سراغ همون ایندکس و نتایج مرتبط رو برمی‌گردونه. نکته‌ی مهم اینه که Lucene اصلاً قرار نیست مثل دیتابیس رکورد به رکورد بگرده؛ کل فلسفه‌اش اینه که از قبل همه‌چی رو آماده کرده.موضوع Inverted Index؛ قلب تپنده‌ی Luceneهسته‌ی اصلی Lucene چیزی به اسم Inverted Index هست. اگر بریم برای بررسی عمیق موضوع:به‌جای اینکه بگه «این سند چه کلماتی داره»، می‌گه «این کلمه تو کدوم سندها اومده».مثلاً به‌جای اینکه برای کلمه‌ی «معماری» کل دیتاست رو بگرده، مستقیم می‌دونه این کلمه تو چه سندهایی وجود داره و حتی چند بار تکرار شده. همین نگاه معکوسه که باعث می‌شه Lucene بتونه روی میلیون‌ها سند هم جستجوی سریع انجام بده، بدون اینکه به CPU و I/O فشار زیادی بیاد. میدونه که «aardvark» در اسناد 1، 5، 400 و 900 وجود دارهAnalyzer؛ جایی که متن رو می‌فهمیمLucene فرض نمی‌کنه متن خام همون چیزیه که باید ایندکس بشه. قبل از ایندکس، متن میره توی یه مرحله به اسم Analysis. اینجا اتفاقاتی مثل اینا می‌افته:شکستن متن به Tokenحذف Stop Wordهانرمال‌سازی حروفStemming یا LemmatizationStemming یعنی اینکه کلمه رو با یه‌سری قواعد مکانیکی کوتاه کنی تا به یه ریشه‌ی تقریبی برسی. مثل این میمونه که واژه «running» رو تبدیل میکنه به «run» یا واژه «connection» رو تبدیل میکنه به «connect».Lemmatization یه قدم جلوتره. اینجا سیستم سعی می‌کنه کلمه رو به شکل پایه‌ی واقعی زبان (lemma) برسونه، با در نظر گرفتن:نقش دستوری (اسم، فعل، صفت)ساختار زبانیگاهی context جملهمثل این میمونه که واژه «better» رو تبدیل میکنه به «good». اینجا خروجی حتماً یه کلمه‌ی معتبر زبانیه. کندتر از stemming هست و اینکه Lucene به صورت پیش فرض از Stemming استفاده میکنه. همه‌ی اینا با چیزی به اسم Analyzer کنترل می‌شه. چیزی که مستندات Lucene روش خیلی تأکید دارن اینه که انتخاب Analyzer درست، نصف کیفیت جستجوی شماست. اگه اینجا اشتباه کنی، بهترین Queryها هم نجاتمون نمیدن. باید دقت کافی رو داشته باشیم. Document ها، لیست Stop Word و Inverted indexQuery در Lucene فقط «جستجو» نیستبرخلاف چیزی که خیلی‌ها فکر می‌کنن، Query تو Lucene فقط یه match ساده نیست. Query می‌تونه شامل کلی منطق باشه:منطق phrase queryمنطق fuzzy searchمنطق  wildcardمنطق range queryمنطق Boolean logicLucene این Queryها رو می‌گیره، می‌بره روی Index، بعد با الگوریتم‌های رتبه‌بندی (مثل BM25) نتایج رو امتیازدهی می‌کنه. یعنی خروجی صرفاً «پیدا شد / نشد» نیست؛ یه لیست مرتب‌شده از مرتبط‌ترین نتایجه. یکم بیشتر در مورد الگوریتم BM25  صحبت کنیم بچه ها. BM25 (که اسم کامل‌ترش Okapi BM25 هست) یه الگوریتم رتبه‌بندی نتایج جستجوئه.یعنی وقتی کاربر یه Query می‌زنه و چند تا سند match می‌شن، BM25 تصمیم می‌گیره:«کدوم نتیجه واقعاً به درد این کاربر می‌خوره و باید بالاتر نمایش داده بشه؟»تو دنیای واقعی، این یعنی تفاوت بین:«یه چیزی پیدا شد»و «همونی که دنبالش بودم پیدا شد»خب Lucene داره از BM25 استفاده میکنه و دوستانی که میخوان اطلاعات بیشتری در این حوزه کسب کنند، پیشنهاد میکنم این رو بررسی کنند. Segmentها؛ دلیل سرعت و مقیاس‌پذیریIndex تو Lucene یه فایل بزرگ یک‌تکه نیست. از چند تا Segment تشکیل شده که هر کدوم مستقلن. این طراحی باعث می‌شه:نوشتن و خواندن همزمان راحت‌تر بشهایندکس‌سازی Incremental باشهPerformance تو حجم بالا حفظ بشهLucene خودش مدیریت merge شدن segmentها رو انجام میده، ولی مستندات رسمی بارها می‌گن که اگه تو محیط production هستی، باید بدونی این mergeها چه زمانی و با چه هزینه‌ای انجام می‌شن.Lucene قراره کِی استفاده بشه؟ما معمولا Lucene رو باید در جایی استفاده کنیم که:دیتاهامون زیاده، جستجو مهم‌تر از CRUD ساده‌ست، می‌خواییم روی relevance و ranking کنترل داشته باشیم، حاضر هستیم معماری جستجو رو خودمون طراحی کنی از ابتدا. به همین خاطره که خیلی از سیستم‌های معروف‌تر مثل Elasticsearch یا Solr، در واقع اومدن روی Lucene سوار شدن و لایه‌های بالادستی بهش اضافه کردن.</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Sun, 22 Feb 2026 13:32:55 +0330</pubDate>
            </item>
                    <item>
                <title>جبر رابطه‌ای در پایگاه داده</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/%D8%AC%D8%A8%D8%B1-%D8%B1%D8%A7%D8%A8%D8%B7%D9%87-%D8%A7%DB%8C-%D8%AF%D8%B1-%D9%BE%D8%A7%DB%8C%DA%AF%D8%A7%D9%87-%D8%AF%D8%A7%D8%AF%D9%87-dzovhibvllal</link>
                <description>بیایید ببینیم دیتابیس واقعاً چطور فکر می‌کندسلام. اول میخوام یک موضوعی رو بیان کنم اینکه این مقاله کمی طولانی هست و در سه قسمت اصلی تقسیم بندی شده است. پس اگر معمار سیستم های پایگاه داده هستید و یا علاقه زیادی به مباحث پایگاه داده دارید، این مقاله برای شما مفید خواهد بود.در قسمت اول: مشروحی بر چیستی موضوع رو خواهیم داشت. در قسمت دوم: تعاریف دقیق کلید واژه ها رو خواهیم داشت. در قسمت سوم: به بررسی مقاله آقای Edgar Frank Codd مطروحه در سال 1970خواهیم پرداخت.اگر در مورد آقای کاد اطلاعات کافی ندارید، پیشنهاد میکنم این مقاله رو حتما بررسی کنید.قسمت اول: مشروحی بر چیستی موضوع؛دوستان اگر چند سالی با دیتابیس‌های رابطه‌ای سر و کله زده باشید، احتمالاً SQL شده رفیق شفیق‌تان. کوئری می‌زنیم، دیتا می‌گیریم، کار راه می‌افتد و تمام. اما معمولاً یک سؤال مهم را از خودمان نمی‌پرسیم: «پایگاه داده این دستوراتی که من نوشتم را چطور می‌فهمد؟»جواب کوتاه این است: نه با SQL، بلکه با چیزی عمیق‌تر به اسم جبر رابطه‌ای.ایده‌ی جبر رابطه‌ای اولین‌بار توسط Edgar Frank Codd در مقاله‌ی معروفش در سال ۱۹۷۰ مطرح شد؛ زمانی که هنوز خبری از SQL به شکل امروزی‌اش نبود. آقای کاد دنبال یک زبان برنامه‌نویسی نبود؛ دنبال یک مدل فکری بود. مدلی که به ما اجازه بده درباره داده‌ها بدون وابستگی به فایل، دیسک و جزئیات فیزیکی فکر کنیم.برای شروع، باید یک سوءتفاهم رایج را کنار بگذاریم:در مدل رابطه‌ای، «جدول» فقط یک جدول نیست. چیزی که ما بهش می‌گیم جدول، از دید کاد یک Relation است. Relation یعنی یک رابطه‌ی ریاضی؛ چیزی شبیه یک مجموعه. داخل این مجموعه، هر سطر یک Tuple است. اگر بخواهم خیلی خودمونی بگم، Tuple یعنی «یک رکورد کامل». هر ستون هم یک Attribute است؛ یعنی یک ویژگی از داده، مثل نام، سن یا ایمیل.نکته‌ی مهم اینجاست که Relation مجموعه است، نه لیست. یعنی ترتیب سطرها مهم نیست و تکرار معنی ندارد. شاید در SQL این‌ها را حس نکنیم، ولی در ذهن جبر رابطه‌ای، این‌ها اصول پایه‌اند.حالا که داده را مجموعه دیدیم، طبیعی است که بخواهیم روی آن عملیات انجام بدهیم. این‌جاست که جبر رابطه‌ای وارد بازی می‌شود. جبر رابطه‌ای مجموعه‌ای از عملگرهاست که روی Relationها کار می‌کنند و یک ویژگی خیلی مهم دارند: هر عملی که انجام می‌دهیم، خروجی‌اش باز هم یک Relation است. آقای کاد روی این موضوع خیلی تأکید داشت، چون همین ویژگی است که اجازه می‌دهد عملیات‌ها را ترکیب کنیم و بهینه‌سازی منطقی انجام بدهیم.مثلاً عملگر Selection را در نظر بگیر. Selection یعنی انتخاب بعضی سطرها بر اساس یک شرط. اگر بخواهم ساده بگویم، همان کاری که WHERE در SQL می‌کند. فرض کن یک Relation داریم به اسم Students. وقتی می‌گوییم «دانشجوهایی که معدل‌شان بالای ۱۸ است»، در جبر رابطه‌ای داریم یک Selection انجام می‌دهیم. خروجی این کار نه یک لیست، نه یک فایل، بلکه یک Relation جدید است که فقط بعضی از Tupleها را دارد.یا عملگر Projection. Projection یعنی انتخاب بعضی ستون‌ها. مثلاً می‌گویی «از جدول دانشجوها فقط نام و ایمیل را می‌خواهم». این دقیقاً Projection است. باز هم خروجی یک Relation جدید است؛ با Attributeهای کمتر، ولی همچنان یک Relation کامل.علائم عملگر ها و معانی اونها در جبر رابطه ای پایگاه دادهجایی که داستان جالب‌تر می‌شود، وقتی چند Relation را با هم ترکیب می‌کنیم. مثلاً Cartesian Product که معمولاً اسمش ترسناک به نظر می‌رسد، در اصل یعنی ترکیب هر Tuple از یک Relation با هر Tuple از Relation دیگر. Joinهایی که ما هر روز می‌نویسیم، در نگاه جبر رابطه‌ای، چیزی نیستند جز ضرب دکارتی به اضافه‌ی Selection. اگر ضرب دکارتی، جبر، رابطه یا جبر رابطه ای رو دقیق نمیتونیم درک کنیم معانیش رو، معنیش این نیست باید این فیلد رو رها کنیم. در بخش بعدی (قسمت 2)، سعی کردم تعاریف دقیق از این کلید واژه ها رو داشته باشیم. آقای کاد در مقاله‌های بعدی‌اش (اوایل دهه‌ی ۷۰ میلادی) دقیقاً روی همین تجزیه‌ی Join تأکید می‌کند تا نشان بدهد این‌ها مفاهیم مستقل نیستند، بلکه از چند عمل ساده ساخته شده‌اند.یک نکته‌ی مهم این وسط هست که معمولاً کمتر بهش توجه می‌کنیم: SQL خودِ جبر رابطه‌ای نیست. SQL از جبر رابطه‌ای الهام گرفته، ولی کاملاً با آن یکی نیست. SQL تکرار دارد، ترتیب دارد، NULL دارد و به پیاده‌سازی وابسته است. اما جبر رابطه‌ای کاملاً صوری و تمیز است. در واقع، وقتی شما یک کوئری SQL می‌نویسید، موتور پایگاه داده آن را به یک نمایش داخلی شبیه به جبر رابطه‌ای تبدیل می‌کند و بعد تازه می‌رود سراغ اجرا.و این دقیقاً همان چیزی است که آقای کاد دنبالش بود. او در مقالات سال‌های ۱۹۷۰ تا ۱۹۷۲ بارها روی مفهوم استقلال منطقی داده تأکید می‌کند. یعنی برنامه‌نویس باید بتواند بگوید «چه دیتایی می‌خواهم»، نه اینکه مجبور باشد بداند «دیتا دقیقاً کجای دیسک و با چه ساختاری ذخیره شده». جبر رابطه‌ای ابزار این نوع فکر کردن است. البته این موضوع برای معمارها نیست ها. معمار پایگاه داده باید دقیق بدونه که سیستم چگونه کار میکنه. برنامه نویس ما، تاکید دارم که صرفاً برنامه نویس و توسعه دهنده ما نیاز به عمیق شدن زیاد در این حوزه ندارد.اگر بخواهم تجربه‌ی شخصی‌ام را بگم، جبر رابطه‌ای چیزی نیست که هر روز بنشینیم و فرمول‌هایش را بنویسیم. اما وقتی آن را بفهمید، یک اتفاق جالب می‌افتد:کوئری‌های بد را زودتر تشخیص می‌دهید، Joinهای بی‌منطق توی ذوق‌تان می‌زند، و طراحی دیتابیس برایتان از «حس شهودی» تبدیل می‌شود به «تصمیم آگاهانه».در نهایت، جبر رابطه‌ای میراث فکری کسی است که قبل از همه فهمید پایگاه داده فقط محل ذخیره‌ی اطلاعات نیست، بلکه یک سیستم فکری است. اگر بخواهیم معماری داده را جدی بگیریم، راهی نداریم جز اینکه حداقل یک بار، عمیق و درست، این زبان زیرساختی را بفهمیم؛ زبانی که پایگاه داده واقعاً با آن فکر می‌کند.قسمت دوم: حالا بریم مفاهیم پایه و کلید واژه ها رو بررسی کنیم؛اول: «جبر» یعنی چی؟وقتی از «جبر» حرف می‌زنیم، منظورمان یک سری عمل و قانون است که روی یک‌سری اشیاء مشخص انجام می‌شود و نتیجه‌ی قابل پیش‌بینی می‌دهد.مثلاً در جبر عددی، ما عدد داریم و عملگرهایی مثل جمع و ضرب. در جبر رابطه‌ای هم دقیقاً همین اتفاق می‌افتد، فقط به‌جای عدد، با «رابطه» کار می‌کنیم.پس جبر یعنی:مجموعه‌ای از عملگرها + قوانینی که مشخص می‌کنند این عملگرها چطور روی داده‌ها اعمال شوند.دوم: «رابطه» یعنی چی؟اینجا معمولاً اولین سوءتفاهم شکل می‌گیرد.رابطه از دید پایگاه داده رابطه‌ای فقط یک جدول ظاهری نیست.در تعریف رسمی، رابطه یک مجموعه از تاپل‌هاست.Tuple (تاپل) یعنی یک رکورد کامل؛ چیزی که ما معمولاً بهش می‌گیم «یک سطر»Attribute (ویژگی) یعنی یک ستون؛ مثل نام، سن، ایمیلکل رابطه یعنی یک مجموعه از این تاپل‌هایک نکته‌ی مهم رو داشته باشیم اینجا اینکه رابطه مجموعه است، نه لیست. یعنی ترتیب سطرها اهمیتی نداره و تکرار مفهومی ندارد (حتی اگر در SQL ببینیم). این نگاه دقیقاً همان چیزی است که آقای کاد روی آن تأکید داشت.سوم: تعریف دقیق «جبر رابطه‌ای»حالا که جبر و رابطه را جداگانه فهمیدیم، تعریف مشهور جبر رابطه‌ای خیلی تمیز می‌شود. من اینطور میگم:جبر رابطه‌ای مجموعه‌ای از عملگرهای صوری است که روی روابط تعریف می‌شوند و نتیجه‌ی هر عملگر نیز یک رابطه است.این جمله شاید ساده به نظر برسد، اما یک مفهوم بسیار مهم داخلش خوابیده:خاصیت بسته‌بودن (Closure)یعنی هر کاری روی داده انجام بدیم، باز هم در دنیای Relation باقی می‌مانیم. همین ویژگی است که امکان ترکیب عملیات، بازنویسی و بهینه‌سازی را می‌دهد.چهارم: جبر رابطه‌ای در پایگاه داده یعنی چه؟در سیستم‌های پایگاه داده رابطه‌ای، جبر رابطه‌ای زبان اجرایی نیست؛ زبان فکری است. یعنی چی؟ یعنی اینکه شما با SQL حرف می‌زنید، اما پایگاه داده در لایه‌های داخلی، کوئری شما را به چیزی شبیه جبر رابطه‌ای تبدیل می‌کند و بعد تصمیم می‌گیرد چطور آن را اجرا کند.به بیان ساده‌تر:SQL چیزی است که شما می‌نویسید،جبر رابطه‌ای چیزی است که دیتابیس می‌فهمد.این دقیقاً همون چیزی است که آقای کاد در مقاله‌ معروفش در سال ۱۹۷۰ و مقالات بعدی‌اش در اوایل دهه‌ی ۷۰ میلادی مطرح کرد؛ برای رسیدن به استقلال منطقی داده. اصلاً موضوع داره به صراحت در مقاله بیان میشه.خب حالا بریم یک مثال واقعی داشته باشیم برای درک بهتر موضوع؛فرض کنیم یک Relation داریم به اسم Students با این Attributeها:StudentID, Name, Age, GPASelection (انتخاب)Selection یعنی فیلتر کردن تاپل‌ها بر اساس شرط.اگر بگیم:«دانشجوهایی که معدل‌شان بالای ۱۸ است»از دید جبر رابطه‌ای:ما داریم روی Relation Students یک Selection انجام می‌دهیم و خروجی‌اش یک Relation جدید است که فقط بعضی تاپل‌ها را دارد.از نظر ذهنی خیلی شبیه این است:SELECT * FROM Students WHERE GPA &gt; 18;Projection (تصویر)Projection یعنی انتخاب بعضی Attributeها.مثلاً می‌گیم:«از دانشجوها فقط نام و معدل را می‌خواهم»در این حالت:تعداد ستون‌ها کم می‌شودولی همچنان خروجی یک Relation معتبر استمعادل ذهنی SQL:SELECT Name, GPA FROM Students;Union (اجتماع)Union یعنی ترکیب دو Relation هم‌ساختار.فرض کنیم:PassedStudentsExcellentStudentsاگر Union بگیریم:همه‌ی دانشجوهایی که یا پاس شده‌اند یا ممتازند، در یک Relation جدید جمع می‌شوند.Difference (تفاضل)Difference یعنی کم کردن یک Relation از Relation دیگر.مثلاً:«دانشجوهایی که ثبت‌نام کرده‌اند ولی هنوز پاس نشده‌اند»از نظر مفهومی:یک Relation را از دیگری کم می‌کنیم و خروجی باز هم یک Relation است.Cartesian Product (ضرب دکارتی)دوستان اینجا معمولاً همه می‌ترسند، ولی در اصل مفهومش ساده است. همون موضوعی که در بخش قبلی گفتم یک مثال میزنیم تا راحت تر بشه فهمش.اگر Relation Students و Relation Courses را ضرب دکارتی کنیم:هر دانشجو با هر درس ترکیب می‌شودخروجی شامل تمام ترکیب‌های ممکن استJoinهایی که هر روز می‌نویسیم، در واقع:ضرب دکارتی + Selection هستندکاد دقیقاً برای همین این عملگر را پایه‌ای در نظر گرفت.Rename (تغییر نام)Rename برای وقتی است که:اسم Relation یا Attributeها را عوض می‌کنیمتا در ترکیب عملیات‌ها دچار ابهام نشویماین عملگر ساده، ولی در جبر رابطه‌ای بسیار حیاتی است.خب حالا اطلاعات بالا رو داشته باشیم. میخوام یک مثال دیگر بزنم ببینیم موضوع چی هست. فرض میکنیم دو تا جدول داریم به نام های Students برای نگهداری اطلاعات دانشجو ها مشتمل بر ستون های شناسه، نام و رشته. جدول دیگری داریم بنام Enrollments برای نگهداری اطلاعات دروس دانشجو ها مشتمل بر ستون های شناسه، شناسه ارتباطی دانشجو، درس و نمره.حالا بریم سراغ اطلاعات موجود در جداول:در جدول Students داریم: 1) شناسه: 1 | نام: علی | رشته: کامپیوتر 2) شناسه: 2 | نام: سارا | رشته: برق3) شناسه: 3 | نام: سحر | رشته: کامپیوتردر جدول Enrollments داریم: 1) دانشجو: 1 | درس: پایگاه داده | نمره: 182) دانشجو: 2 | درس: سیستم عامل | نمره: 153) دانشجو: 3 | درس: پایگاه داده | نمره: 19سوال: اسم دانشجوهایی که رشته‌شون کامپیوتر هست و درس پایگاه داده رو گرفتن رو پیدا کن.SELECT S.name
FROM Students S
JOIN Enrollments E ON S.sid = E.sid
WHERE S.major = &#039;CS&#039;
  AND E.course = &#039;DB&#039;;توجه داشته باشیم که name یعنی نام دانشجو، CS یعنی رشته کامپیوتر Computer Science و DB یعنی درس پایگاه داده و sid شناسه دانشجو ها میباشد.حالا میریم معادل در جبر رابطه‌ای (Relational Algebra) ها رو داشته باشیم:انتخاب دانشجویان رشته کامپیوتر: 
σ major=′CS′​(Students)انتخاب ثبت‌ نام‌های درس پایگاه داده
σ course=′DB′​(Enrollments)جوین روی شناسه ها
σ major=′CS′​(Students) ⋈ Students.sid=Enrollments.sid​ σ course=′DB′​(Enrollments)پروجکشن روی نام ها
π name​(σ major=′CS′​(Students) ⋈ Students.sid=Enrollments.sid​ σ course=′DB′​(Enrollments))دوستان خواهش میکنم عنایت ویژه بفرمایید ببینید چه اتفاقی در حال رخ دادن هست. معادل و تطبیق مفهومی SQL و جبر رابطه‌ای رو باهم داشته باشیم:WHERE در SQL معادل σ (selection) در جبر رابطه ایSELECT ستون‌ها در SQL معادل π (projection) در جبر رابطه ایJOIN در SQL معادل ⨝ (join) در جبر رابطه ایFROM در SQL معادل رابطه‌های ورودی در جبر رابطه ایپس در نتیجه اگر یک معمار با مطالعه موضوعات فوق بخواهد برای خودش یک جمع بندی داشته باشه، میگه:SQL در اصل یک زبان اعلانی (Declarative) است،اما در پشت صحنه، موتور دیتابیس کوئری را به جبر رابطه‌ای تبدیل می‌کند.یعنی:ما در SQL میگیم«چی می‌خوام» و دیتابیس با جبر رابطه‌ای تصمیم می‌گیره «چطوری بهش برسم»جمع‌بندی بخش دوم رو داشته باشیم؛جبر رابطه‌ای به ما یاد می‌دهد چطور درباره داده فکر کنیم، نه فقط چطور از آن اطلاعات بکشیم بیرون. این همان دیدی است که آقای کاد بیش از پنجاه سال پیش مطرح کرد و هنوز هم ستون فقرات پایگاه داده‌های رابطه‌ای است. اگر این لایه را بفهمید، SQL برایتان شفاف‌تر می‌شود، طراحی دیتابیس‌تان منطقی‌تر می‌شود و مهم‌تر از همه، دیگر با پایگاه داده «کورکورانه» کار نمی‌کنید.قسمت سوم: بررسی مقاله مطروحه در سال 1970؛این مقاله اساساً می‌گه:کاربر نباید درگیر این باشه که داده‌ها توی کامپیوتر چطوری ذخیره شدن.آقای ادگار کاد از همون اول دست می‌ذاره روی یه مشکل جدی سیستم‌های دیتابیس قدیمی:اگه ساختار ذخیره‌سازی داده‌ها عوض بشه (مثلاً ترتیب، ایندکس، یا مسیر دسترسی)، کلی برنامه و گزارش از کار می‌افتن و این فاجعه‌ست.مشکل دیتابیس‌های قدیمی چی بود؟سیستم‌های قدیمی (درختی و شبکه‌ای مثل IMS و IDS) سه تا وابستگی خطرناک داشتن:وابستگی به ترتیب داده‌هابرنامه‌ها فرض می‌کردن داده‌ها به یه ترتیب خاص ذخیره شدن. ترتیب عوض می‌شد؟ برنامه می‌ترکید.وابستگی به ایندکس‌هاایندکس که باید فقط برای سرعت باشه، عملاً می‌شد جزئی از منطق برنامه.وابستگی به مسیر دسترسی (Access Path)برنامه‌ها دقیقاً می‌دونستن از چه مسیری به داده برسن.ساختار عوض می‌شد؟ برنامه دیگه نمی‌فهمید داده کجاست.راه‌حل آقای کاد: مدل رابطه‌ایاقای کاد پیشنهاد میده داده‌ها رو نه به شکل درخت و شبکه، بلکه به شکل Relation (رابطه) ببینیم؛ چیزی شبیه جدول:هر سطر = یک رکوردهر ستون = یک ویژگیترتیب سطرها مهم نیستحتی ترتیب ستون‌ها هم برای کاربر نباید مهم باشهمیگه کاربر فقط باید بدونه:اسم جدول چیهستون‌هاش چی‌انمعنی داده‌ها چیهو نه این‌که «چطوری ذخیره شدن». خیلی روی این موضوع تاکید داره.کلیدهای اصلی و خارجیآقای کاد خیلی شفاف مفاهیمی رو تعریف می‌کنه که امروز بدیهی‌ان:کلید اصلی (Primary Key): چیزی که هر رکورد رو یکتا می‌کنهکلید خارجی (Foreign Key): اشاره به کلید اصلی یه جدول دیگهو می‌گه رابطه‌ها می‌تونن به هم ارجاع بدن، بدون این‌که ساختار فیزیکی به کاربر تحمیل بشه. همین حرف شده امروز استاندارد اصلی پایگاه داده های رابطه ای.نرمال‌سازی (Normal Form)یکی از بخش‌های مهم مقاله همینه:آقای کاد می‌گه:جدول خوب، جدولی‌ هست که توش ستون‌های چندمقداری و تو‌در‌تو نداشته باشیم.راه‌حل:داده‌های تودرتو رو جدا کنکلیدها رو بیار پایینجدول‌ها رو ساده و اتمیک کننتیجه چی میشه اون موقع؟ مشخص هست دیگه؛ذخیره‌سازی ساده‌تر میشهانتقال داده راحت‌تر میشهناسازگاری کمتر به وجود میادزبان پرس‌وجوی ایده‌آل (همون کوئری گیری خودمون)آقای کاد یه ایده‌ی جسورانه میده:یه زبان عمومی برای کار با داده‌ها، بر پایه منطق و Predicate Calculusچیزی که بعدها الهام‌بخش SQL شد.ویژگی مهمش؟هر رابطه رو می‌تونی از هر سمتی پرس‌وجو کنیلازم نیست مسیر دسترسی رو حفظ باشیفقط بگو چی می‌خوای، نه چطور بهش برسیافزونگی و ناسازگاری داده‌هاآقای کاد بین دو نوع افزونگی فرق می‌ذاره:افزونگی قوی: داده‌ای که کاملاً از داده‌های دیگه قابل محاسبه‌ستافزونگی ضعیف: داده‌ای که همیشه وابسته‌ست، ولی دقیقاً قابل محاسبه نیستو تأکید می‌کنه:سیستم باید بتونه ناسازگاری‌ها رو تشخیص بده، حتی اگه موقت باشن.در نهایت من یک جمع بندی داشته باشماین مقاله پایه‌ی دیتابیس‌های مدرنه. حرف اصلیش اینه:کاربر نباید اسیر جزئیات ذخیره‌سازی بشهداده باید مستقل از برنامه باشهرابطه‌ها از مسیرها مهم‌ترنسادگی منطقی، از بهینه‌سازی فیزیکی مهم‌ترهتقریباً هر چیزی که امروز تو دیتابیس‌های رابطه‌ای بدیهیه، اولین‌بار تو همین مقاله گفته شده. معماران و تحلیل گران عزیزمون، این اطلاعات پایه، حتما در کَرییر کاری شما تاثیر گذار خواهند بود.</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Sun, 22 Feb 2026 12:42:21 +0330</pubDate>
            </item>
                    <item>
                <title>Event-Driven در نرم افزار</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/event-driven-%D8%AF%D8%B1-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-utucodxodqvq</link>
                <description>Event-Driven Architecture؛ ستون فقرات سیستم‌های توزیع‌شده مدرنسلام دوستان؛ در این مقاله موضوع Event-Driven Architecture رو بررسی میکنیم. احتمالاً این مقاله برای افراد غیرفنی زیاد جالب نباشه و مخاطب خاصش، معمار ها و تحلیل گر ها، توسعه دهنده و علاقه مندان به برنامه نویسی خواهد بود.با رشد سیستم‌ها، افزایش نیاز به مقیاس‌پذیری و حرکت به سمت معماری‌های توزیع‌شده، الگوهای سنتی request/response و coupling مستقیم بین سرویس‌ها به‌تدریج به گلوگاه تبدیل می‌شوند. در چنین فضایی، Event-Driven Architecture (EDA) نه به‌عنوان یک انتخاب فانتزی، بلکه به‌عنوان یک ضرورت معماری مطرح می‌شود. EDA مدلی است که در آن سیستم‌ها به‌جای فراخوانی مستقیم یکدیگر، با انتشار و مصرف Eventها با هم تعامل می‌کنند.در این مقاله، Event-Driven Architecture را به‌صورت کاملاً فنی و عمیق بررسی می‌کنیم؛ از مدل ذهنی و مفاهیم پایه گرفته تا پیاده‌سازی واقعی با ابزارهای رایج. هدف این است که اگر بخواهیم یک سیستم واقعی مبتنی بر EDA طراحی و پیاده‌سازی کنیم، بتوانیم از مفاهیم پایه ای که در اینجا خواهم گفت، استفاده کنیم.مسئله اصلی؛ چرا معماری‌های سنتی مقیاس‌پذیر نیستند؟در معماری‌های synchronous، هر سرویس برای انجام کار خود به پاسخ سرویس‌های دیگر وابسته است. این وابستگی زنجیره‌ای باعث افزایش latency، شکنندگی سیستم و propagation خطا می‌شود. اگر یکی از سرویس‌ها down شود یا کند پاسخ دهد، کل زنجیره تحت تأثیر قرار می‌گیرد.علاوه بر این، coupling زمانی (temporal coupling) مشکل بزرگی است. سرویس‌ها باید هم‌زمان در دسترس باشند تا یک سناریوی بیزینسی اجرا شود. Event-Driven Architecture دقیقاً برای شکستن این وابستگی طراحی شده است.Event-Driven Architecture چیست؟ابتدا میخوام یک تعریف دقیق داشته باشیم. Event-Driven Architecture سبکی از معماری است که در آن Event به‌ عنوان واحد اصلی ارتباط بین اجزای سیستم استفاده می‌شود. Event بیانگر این است که «چیزی در سیستم اتفاق افتاده است»، نه اینکه «چه کاری باید انجام شود».در EDA، Producer یک Event را منتشر می‌کند، بدون اینکه بداند چه کسی یا چند Consumer آن را دریافت خواهند کرد. Consumerها هم بر اساس Eventهایی که دریافت می‌کنند، واکنش نشان می‌دهند. این مدل باعث loose coupling، scalability بالا و انعطاف‌پذیری در توسعه می‌شود.Event چیست و چه چیزی Event نیست؟با توجه به اینکه باید بدونیم Event دقیقا به چه معنایی هست، میخوام جمله دقیقی رو بیان کنم. Event یک واقعیت immutable است که در گذشته رخ داده و قابل تغییر نیست. نام‌گذاری Event بسیار مهم است و معمولاً باید به صورت past tense باشد، مثل OrderCreated یا PaymentCompleted. یعنی جریانی که در گذشته رخ داده است.در مقابل، Command یا Request بیانگر نیت انجام یک کار است. اشتباه رایج این است که Commandها را به‌عنوان Event منتشر کنیم. این کار مرز مسئولیت‌ها را مخدوش می‌کند و coupling منطقی ایجاد می‌کند. معمار ها، تحلیل گر ها و توسعه دهنده ها باید دقت کافی رو داشته باشند.اجزای اصلی Event-Driven ArchitectureEDA معمولاً از چند جزء کلیدی تشکیل می‌شود. Producer که Event را تولید می‌کند، Broker یا Message Bus که Event را منتقل می‌کند و Consumerهایی که Event را مصرف می‌کنند. هر کدام از این اجزا مسئولیت مشخص و محدودی دارند.Broker نقش بسیار مهمی در تضمین ordering، delivery و durability Eventها دارد. انتخاب Broker مناسب تأثیر مستقیمی بر قابلیت اطمینان سیستم دارد.Message Broker؛ قلب تپنده EDAپیاده‌سازی EDA بدون Message Broker عملاً غیرممکن است. ابزارهایی مثل Apache Kafka، RabbitMQ و Pulsar رایج‌ترین گزینه‌ها هستند. Kafka معمولاً برای throughput بالا و event streamهای بزرگ استفاده می‌شود، در حالی که RabbitMQ بیشتر برای messaging کلاسیک و routing پیچیده کاربرد دارد.در معماری Event-Driven، باید تفاوت بین queue و topic را به‌خوبی درک کرد. Queue معمولاً برای load balancing استفاده می‌شود، اما topic امکان fan-out و مصرف چندگانه Event را فراهم می‌کند. در مورد این بخش بیشتر صحبت خواهیم کرد.Event Schema و ContractEventها contract بین سرویس‌ها هستند. تغییر نادرست در schema Event می‌تواند چندین سرویس را هم‌زمان بشکند. به همین دلیل، versioning و backward compatibility در Eventها حیاتی است.استفاده از schema registry (مثلاً در Kafka) و فرمت‌هایی مثل Avro یا Protobuf به‌شدت توصیه می‌شود. Event باید self-descriptive باشد و حداقل اطلاعات لازم برای Consumer را فراهم کند.احتمالاً برخی دوستان ندونن بحث Avro چی هست پس همینجا یک اشاره کوچیکی داشته باشم بهش که Avro یک فرمت serialization باینری هست و مزیتش این است که schema جدا از payload ذخیره می‌شود و فقط ID آن داخل پیام قرار می‌گیرد. این کار باعث کاهش حجم پیام می‌شود. موضوع Protobuf هم که احتمالاً با gRPC کار کرده باشند، آشنا هستند دیگه. مال گوگل هست و ساختار strongly typed داره. معمار های با تجربه مون میدونن که در سیستم های high-throughput ، معمولا یا Avro یا protobuf استفاده میشه.بزارید این رو هم بگم بریم سراغ موضوعات بعدی. اگر نمیدونید schema registry چی هست در کافکا، Schema Registry یک سرویس مرکزی است که:ساختار (Schema) هر Event را ذخیره می‌کندVersionهای مختلف آن را نگه می‌داردقبل از publish شدن Event، اعتبار آن را بررسی می‌کند (مهم هست خیلی)سازگاری (compatibility) نسخه جدید با نسخه‌های قبلی را enforce می‌کند. حالا دیگه نمیخوام خیلی وارد مباحث Backward، Forward بشم. Backward میگه نسخه جدید باید بتواند داده‌های نسخه قبلی را بخواند اون یکی هم برعکش رو میگه.Delivery Semantics؛ از At-Most-Once تا Exactly-Onceبریم سراغ سمانتیک های تحویل و ببینیم که موضوع چی هست. در EDA باید صراحتاً مشخص شود که semantics تحویل Event چیست. At-most-once ساده ولی غیرقابل‌اعتماد است. At-least-once رایج‌ترین انتخاب است، اما نیازمند idempotent consumerهاست. Exactly-once پیچیده و معمولاً پرهزینه است و فقط در سناریوهای خاص ارزش دارد.طراحی Consumerها باید بر اساس at-least-once انجام شود و duplicate Eventها به‌عنوان واقعیت سیستم پذیرفته شوند.Eventual Consistency؛ واقعیت اجتناب‌ناپذیرEDA معمولاً به consistency آنی منجر نمی‌شود. بین انتشار Event و واکنش Consumerها تأخیر وجود دارد. این موضوع باید از ابتدا در طراحی بیزینسی پذیرفته شود. Eventual Consistency trade-off آگاهانه‌ای برای دستیابی به availability و scalability است که معمار ما باید حواسش باشه.نمایش وضعیت موقت، handling stateهای intermediate و طراحی UX مناسب بخشی از پیامدهای این انتخاب معماری هستند.طراحی Consumerها؛ Stateless یا Stateful؟Consumerها می‌توانند stateless یا stateful باشند. Consumerهای stateless ساده‌تر و مقیاس‌پذیرترند، اما در برخی سناریوها نیاز به نگه‌داشت state وجود دارد. در این حالت، مدیریت offset و persistence state اهمیت زیادی پیدا می‌کند. همچنین error handling و retry باید با دقت طراحی شوند تا از infinite loop جلوگیری شود.EDA و CQRSEvent-Driven Architecture به‌صورت طبیعی با CQRS هم‌راستا است. Commandها باعث تغییر state می‌شوند و Eventها نتیجه این تغییرات را منتشر می‌کنند. Read Modelها معمولاً با مصرف Eventها ساخته و به‌روزرسانی می‌شوند.این ترکیب امکان scale مستقل read و write را فراهم می‌کند و برای سیستم‌های با read-heavy workload بسیار مناسب است.مثال واقعی؛ سیستم فروش آنلاین Event-Drivenدر یک سیستم فروش آنلاین، Order Service بعد از ایجاد سفارش، OrderCreatedEvent منتشر می‌کند. Payment Service با دریافت این Event فرآیند پرداخت را آغاز می‌کند. Inventory Service موجودی را به‌روزرسانی می‌کند و Notification Service ایمیل یا پیام ارسال می‌کند.Order Service هیچ اطلاعی از Consumerها ندارد و همین موضوع باعث می‌شود اضافه یا حذف سرویس‌های جدید بدون تغییر در Producer انجام شود.Observability در Event-Driven ArchitectureDebug کردن EDA سخت‌تر از معماری synchronous است. Trace کردن یک Event در چند سرویس نیازمند logging ساختاریافته، correlation id و ابزارهای observability مثل OpenTelemetry است.بدون observability مناسب، EDA به‌سرعت به یک black box تبدیل می‌شود و واقعا نمیشه دقیق فهمید چه اتفاقاتی داره در داخل سیستم رخ میده. همیشه به معمار ها و تحلیل گر ها میگم که موضوع observability باید جدی در نظر گرفته بشه منتها کو گوش شنوا 😄چالش‌ها و Anti-Pattern هااستفاده افراطی از Event، انتشار Eventهای بسیار granular یا برعکس بیش‌ازحد coarse، و تبدیل Event Broker به دیتابیس از anti-pattern های رایج هستند. این موضوع رو هم باید دقت داشته باشیم روش.EDA باید با هدف مشخص و در جای درست استفاده شود، نه به‌عنوان پاسخ همه مشکلات.جمع‌بندیEvent-Driven Architecture یکی از پایه‌ای‌ترین الگوهای معماری برای ساخت سیستم‌های مدرن، scalable و resilient است. این معماری با حذف coupling مستقیم، امکان رشد مستقل سرویس‌ها و واکنش‌پذیری بالا را فراهم می‌کند. حالا خیلی از عزیزان میان و این موضوع رو با SAGA هم ترکیب میکنن و یه سیستم ترتمیزی میاد بیرون ازش. اگر در مورد SAGA اطلاعات بیشتری نیاز دارید، میتونید این مقاله رو مطالعه کنید. یک Cheat Sheet کلی هم در زیر قرار میدم برای پترن های رایج مثل outbox و event sourcing و aggregation.Cheat Sheet | Event-Driven Architectureاگر سیستم شما نیاز به مقیاس‌پذیری، انعطاف‌پذیری و توسعه‌پذیری بلندمدت دارد، EDA نه یک انتخاب اختیاری، بلکه یک تصمیم استراتژیک معماری است.</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Wed, 11 Feb 2026 09:24:00 +0330</pubDate>
            </item>
                    <item>
                <title>الگوی SAGA در نرم افزار</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/%D8%A7%D9%84%DA%AF%D9%88%DB%8C-saga-%D8%AF%D8%B1-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-c8p1wtyal0gw</link>
                <description>SAGA Pattern؛ مدیریت تراکنش‌های توزیع‌شده از تئوری تا پیاده‌سازی عملیسلام دوستان. میخوام مقدمه ای در مورد SAGA بدم و سپس بریم جلو. احتمالاً این مقاله برای افراد غیرفنی زیاد جالب نباشه چون خیلی تخصصی داریم در مورد این پترن، یوزکیس ها، Event ها و Message ها صحبت میکنیم و اساساً مخاطب هدف، معمار ها و تحلیل گران نرم افزار، توسعه دهندگان و علاقه مندان به این موضوعات است.شروع کنیم. بسم الله.با حرکت سیستم‌ها به سمت معماری مایکروسرویس و Event-Driven، یکی از اولین مفاهیمی که به بن‌بست می‌رسد، «تراکنش» به معنای کلاسیک آن است. دیگر خبری از یک دیتابیس واحد و Transactionهای ACID نیست که بتوان با یک commit یا rollback همه‌چیز را کنترل کرد. در چنین فضایی، نیاز به الگویی داریم که بتواند تراکنش‌های بلندمدت و توزیع‌شده را مدیریت کند؛ جایی که چند سرویس مستقل باید در نهایت به یک نتیجه بیزینسی سازگار برسند. اینجاست که SAGA Pattern وارد می‌شود.SAGA صرفاً یک الگوی تئوریک نیست، بلکه ستون فقرات بسیاری از سیستم‌های بزرگ و واقعی است. در این مقاله، سعی خواهم کرد SAGA را عمیق، فنی و کاملاً عملی بررسی کنیم؛ از مدل ذهنی و مفاهیم پایه تا پیاده‌سازی واقعی با ابزارهایی مثل Axon Framework و دیگر ابزار ها. مسئولیت پوزیشن های مختلف رو هم درگیر خواهم کرد تا درک موضوع راحت تر بشه. یعنی مسئولیت های معمار و تحلیل گر نرم افزار رو به همراه مسئولیت های توسعه دهنده ها، معماران و ادمین های پایگاه داده و ... بررسی میکنیم.SAGA Patternمسئله اصلی؛ چرا Transaction کلاسیک جواب نمی‌دهد؟در یک سیستم توزیع‌شده، هر سرویس دیتابیس خودش را دارد. فرض کنید ثبت سفارش شامل چند مرحله است: ایجاد سفارش، پرداخت، رزرو موجودی و ارسال. هر کدام از این مراحل در سرویس جداگانه‌ای انجام می‌شود. اگر مرحله سوم شکست بخورد، دیگر نمی‌توان به‌سادگی Payment را rollback کرد، چون آن تراکنش مدت‌ها پیش commit شده است.استفاده از Distributed Transaction و Two-Phase Commit در تئوری ممکن است، اما در عمل باعث coupling شدید، کاهش availability و مشکلات جدی در scale می‌شود. اگر بخواهید، میتونید اطلاعات بیشتری در مورد این موضوعات (تئوری CAP در پایگاه داده) در این مقاله بخونید. SAGA دقیقاً برای حل این مشکل طراحی شده است.SAGA چیست؟ تعریف دقیق و فنیSAGA یک تراکنش بیزینسی بلندمدت است که از چندین Local Transaction تشکیل می‌شود. هر Local Transaction در محدوده یک سرویس اجرا می‌شود و دیتابیس همان سرویس را تغییر می‌دهد. اگر تمام مراحل با موفقیت انجام شوند، SAGA کامل می‌شود. اما اگر یکی از مراحل شکست بخورد، SAGA با اجرای Compensating Transaction‌ها، اثر مراحل قبلی را خنثی می‌کند.نکته کلیدی این است که Compensating Transaction الزاماً rollback فنی نیست، بلکه یک عملیات بیزینسی معکوس است. مثلاً Refund کردن پرداخت، نه undo کردن یک row در دیتابیس.مدل ذهنی SAGA؛ State Machine بیزینسیاز دید فنی، SAGA را می‌توان به‌عنوان یک State Machine در نظر گرفت. هر SAGA یک state دارد و با دریافت Eventها بین stateها جابه‌جا می‌شود. این state نشان‌دهنده پیشرفت فرآیند بیزینسی است، نه وضعیت دیتابیس.برای مثال، یک OrderSaga می‌تواند stateهایی مثل CREATED، PAID، INVENTORY_RESERVED و COMPLETED داشته باشد. هر transition با یک Event رخ می‌دهد و ممکن است باعث ارسال Command به سرویس دیگر شود.انواع SAGA؛ Orchestration در مقابل ChoreographySAGA معمولاً به دو سبک اصلی پیاده‌سازی می‌شود. در مدل Choreography، هیچ موجودیت مرکزی وجود ندارد. هر سرویس به Eventهای سایر سرویس‌ها گوش می‌دهد و بر اساس آن تصمیم می‌گیرد. این مدل ساده‌تر به نظر می‌رسد، اما با بزرگ شدن سیستم، جریان بیزینسی پراکنده و سخت‌قابل‌درک می‌شود.Choreography Based SAGAدر مقابل، مدل Orchestration یک Coordinator مرکزی دارد که معمولاً خود SAGA است. این Coordinator تصمیم می‌گیرد مرحله بعدی چیست و چه Commandی باید ارسال شود. این مدل کنترل، مانیتورینگ و Debug بهتری فراهم می‌کند و در سیستم‌های پیچیده انتخاب رایج‌تری است.Orchestration Based SAGASAGA و Event-Driven ArchitectureSAGA به‌شدت با Event-Driven Architecture گره خورده است. ارتباط بین مراحل SAGA معمولاً از طریق Event انجام می‌شود. یک سرویس پس از انجام Local Transaction، Event مربوطه را منتشر می‌کند و SAGA با دریافت آن Event تصمیم می‌گیرد قدم بعدی چیست.این رویکرد باعث loose coupling بین سرویس‌ها می‌شود و امکان scale مستقل هر سرویس را فراهم می‌کند.Compensating Transaction؛ قلب تحمل خطا در SAGAمهم‌ترین بخش SAGA، طراحی Compensating Transaction است. اگر این بخش به‌درستی طراحی نشود، کل SAGA عملاً بی‌معنا می‌شود. Compensating Transaction باید idempotent باشد و بتواند در شرایط retry و failure به‌درستی کار کند. اگر در مورد idempotency میخواهید اطلاعات بیشتری کسب کنید، در این مقاله، مفاهیم پایه رو مورد بررسی قرار دادیم.همچنین باید پذیرفت که SAGA معمولاً به Eventual Consistency می‌رسد، نه Consistency آنی. این یک trade-off آگاهانه برای دستیابی به availability و scalability است. پیاده‌سازی SAGA با Axon FrameworkAxon Framework یکی از فریم‌ورک‌هایی است که SAGA را به‌صورت first-class پشتیبانی می‌کند. در Axon، SAGA با annotation تعریف می‌شود و lifecycle آن بر اساس Eventها مدیریت می‌شود.هر SAGA instance معمولاً با یک Event شروع می‌شود، مثلاً Axon.OrderCreatedEvent .  این Event را با یک association key به SAGA متصل می‌کند. از آن لحظه به بعد، هر Event مرتبط می‌تواند state داخلی SAGA را تغییر دهد و باعث ارسال Command جدید شود.State داخلی SAGA می‌تواند در دیتابیس ذخیره شود یا حتی Event Sourced باشد، بسته به نیاز سیستم.مثال واقعی؛ Order Processing Sagaفرض کنید کاربر سفارشی ثبت می‌کند. OrderCreatedEvent منتشر می‌شود و OrderSaga شروع می‌شود. Saga با ارسال ProcessPaymentCommand پرداخت را آغاز می‌کند. اگر PaymentSucceededEvent دریافت شود، Saga وارد مرحله رزرو موجودی می‌شود. اگر PaymentFailedEvent رخ دهد، Saga مستقیماً پایان می‌یابد.اگر در مرحله رزرو موجودی خطا رخ دهد، Saga با ارسال RefundPaymentCommand اثر مرحله پرداخت را جبران می‌کند. در نهایت، سیستم بدون هیچ Transaction توزیع‌شده‌ای به وضعیت بیزینسی سازگار می‌رسد.Persistence و Reliability در SAGAاز آنجا که SAGA معمولاً طول عمر بالایی دارد، persistence آن حیاتی است. State SAGA باید بعد از crash یا restart سیستم قابل بازیابی باشد. بسیاری از فریم‌ورک‌ها، از جمله Axon، این persistence را به‌صورت built-in فراهم می‌کنند. همچنین Message Delivery باید حداقل once باشد و SAGA باید برای duplicate Eventها آماده باشد.چالش‌ها و Anti-PatternهاSAGA اگر اشتباه استفاده شود، می‌تواند به پیچیدگی بیش از حد منجر شود. استفاده از SAGA برای عملیات ساده، طراحی Compensating ضعیف یا تبدیل SAGA به God Object از جمله anti-pattern های رایج هستند.SAGA باید نماینده یک فرآیند بیزینسی مشخص باشد، نه جایگزین مستقیم Transaction دیتابیس.جمع‌بندیSAGA Pattern راه‌حل استاندارد و عملی برای مدیریت تراکنش‌های توزیع‌شده در سیستم‌های مدرن است. این الگو با پذیرش Eventual Consistency و استفاده از Compensating Transaction، امکان ساخت سیستم‌هایی scalable، resilient و قابل توسعه را فراهم می‌کند.اگر سیستم و پروژه ما شامل چند سرویس مستقل با فرآیندهای بیزینسی پیچیده است، SAGA نه یک انتخاب، بلکه یک ضرورت معماری است و معمار ما باید در مرحله معماری و تحلیل، اون رو در نظر بگیرد.</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Wed, 11 Feb 2026 08:54:09 +0330</pubDate>
            </item>
                    <item>
                <title>اسناد SRS ،BRS و FRS در مهندسی نرم‌افزار</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/%D8%A7%D8%B3%D9%86%D8%A7%D8%AF-srs-brs-%D9%88-frs-%D8%AF%D8%B1-%D9%85%D9%87%D9%86%D8%AF%D8%B3%DB%8C-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-mshzwi9ccmhi</link>
                <description>سلام. اگر در فضای مهندسی نرم‌افزار، تحلیل سیستم یا مدیریت پروژه فعالیت داشته باشید، احتمالاً بارها با مفاهیمی مثل SRS، BRS و FRS مواجه شده‌اید. اسنادی که در تئوری نقش مشخص و تفکیک‌شده‌ای دارند، اما در عمل یا با یکدیگر اشتباه گرفته می‌شوند یا به‌دلیل محدودیت زمان و منابع، برخی از آن‌ها نادیده گرفته می‌شوند.در این مقاله تلاش خواهم کرد به این موضوع بپردازم که این سه سند چه هستند، چه تفاوت‌هایی با یکدیگر دارند و به‌طور خاص سند BRS چه نقشی در فرآیند نیازسنجی و امکان‌سنجی پیش از تدوین SRS ایفا می‌کند. هدف ارائه‌ی تعاریف صرفاً تئوریک نیست، بلکه تمرکز بر درک کاربردی این اسناد در پروژه‌های واقعی نرم‌افزاری است.بررسی، مطالعه، تحلیل و ایجاد اسناد و مستندسازی در مهندسی نرم افزارBRS؛ نقطه‌ی شروع درک نیاز کسب‌وکارBRS (Business Requirements Specification) معمولاً اولین سند رسمی در مسیر شکل‌گیری یک محصول نرم‌افزاری محسوب می‌شود. تمرکز این سند نه بر سیستم و جزئیات فنی، بلکه بر نیازهای کسب‌وکار است. BRS مشخص می‌کند چرا یک نرم‌افزار باید توسعه داده شود و قرار است چه مسئله‌ای را در سطح بیزنس حل کند.در این سند به موضوعاتی مانند اهداف کلان، چالش‌های فعلی کسب‌وکار، انتظارات ذی‌نفع‌ها و معیارهای موفقیت پروژه پرداخته می‌شود. زبان BRS عمداً غیر فنی انتخاب می‌شود تا مدیران، کارفرما و سایر تصمیم‌گیرندگان بتوانند بدون درگیری با اصطلاحات تخصصی، آن را بررسی و تأیید کنند.اهمیت BRS در این است که پایه‌ی اصلی نیازسنجی و امکان‌سنجی پروژه را شکل می‌دهد. در این مرحله مشخص می‌شود آیا نیاز مطرح‌شده واقعی است، آیا اجرای آن از نظر اقتصادی و عملی توجیه دارد و اولویت‌ها از دید کسب‌وکار چگونه تعریف می‌شوند. در بسیاری از پروژه‌ها، ضعف در BRS باعث می‌شود محصول نهایی از نظر فنی قابل قبول باشد، اما مسئله‌ی اصلی کسب‌وکار را به‌درستی پوشش ندهد.SRS؛ تبدیل نیاز بیزنس به نیازمندی نرم‌افزاریپس از شفاف شدن نیازهای کسب‌وکار در قالب BRS، نوبت به تدوین SRS (Software Requirements Specification) می‌رسد. این سند نقش پل ارتباطی بین دیدگاه بیزنس و تیم فنی را ایفا می‌کند.SRS به‌صورت دقیق مشخص می‌کند که سیستم نرم‌افزاری چه قابلیت‌هایی باید داشته باشد، چه رفتارهایی از آن انتظار می‌رود و چه محدودیت‌هایی بر آن حاکم است. در این مرحله وارد جزئیات می‌شویم، اما همچنان تلاش می‌شود نیازمندی‌ها تا حد امکان مستقل از تکنولوژی و پیاده‌سازی بیان شوند.یک SRS مناسب باید شفاف، بدون ابهام و قابل تست باشد؛ به‌گونه‌ای که تیم توسعه، تیم تست و حتی اعضای جدید پروژه بتوانند با مراجعه به آن، درک مشترکی از سیستم داشته باشند. اگر BRS پاسخ‌گوی «چرایی» پروژه باشد، SRS به «چیستی» سیستم پاسخ می‌دهد.FRS؛ تشریح دقیق رفتارهای عملکردیFRS (Functional Requirements Specification) سطح جزئی‌تری از نیازمندی‌ها را پوشش می‌دهد و معمولاً زمانی اهمیت بیشتری پیدا می‌کند که پروژه پیچیدگی بالاتری داشته باشد یا رفتار هر قابلیت نیاز به تشریح دقیق‌تری داشته باشد.در FRS، تمرکز بر نحوه‌ی عملکرد هر قابلیت است؛ از ورودی‌ها و خروجی‌ها گرفته تا قوانین اعتبارسنجی، سناریوها و وابستگی‌ها. مخاطب اصلی این سند توسعه‌دهندگان هستند و به همین دلیل زبان آن فنی‌تر و اجرایی‌تر است. در بسیاری از پروژه‌ها، FRS به‌عنوان سندی مجزا تدوین نمی‌شود و محتوای آن در قالب بخش‌های عملکردی SRS ارائه می‌گردد.نقش BRS پیش از تدوین SRSیکی از خطاهای رایج در پروژه‌های نرم‌افزاری، شروع مستقیم تدوین SRS بدون داشتن یک BRS دقیق و تأییدشده است. این موضوع معمولاً در ادامه‌ی پروژه باعث تغییرات گسترده، بازنگری نیازمندی‌ها و حتی اختلاف نظر میان ذی‌نفع‌ها و تیم فنی می‌شود.وجود BRS پیش از SRS کمک می‌کند نیازها از منظر کسب‌وکار به‌درستی تحلیل شوند، محدوده‌ی پروژه واقع‌بینانه تعریف شود و امکان‌سنجی فنی و اقتصادی در زمان مناسب انجام گیرد. به بیان ساده، BRS ریسک طراحی و پیاده‌سازی راه‌حل برای مسئله‌ای نادرست را به‌طور قابل توجهی کاهش می‌دهد.جمع‌بندیدر یک نگاه کلی می‌توان گفت BRS جهت و هدف پروژه را مشخص می‌کند، SRS چارچوب و قابلیت‌های سیستم را تعریف می‌کند و FRS به تشریح دقیق نحوه‌ی عملکرد این قابلیت‌ها می‌پردازد. هرچه پروژه بزرگ‌تر و پیچیده‌تر باشد، اهمیت تفکیک و تدوین صحیح این اسناد بیشتر می‌شود. اگرچه در پروژه‌های کوچک ممکن است برخی از این اسناد با یکدیگر ادغام شوند، اما حذف یا نادیده گرفتن BRS معمولاً ریسک‌های جدی در ادامه‌ی مسیر پروژه ایجاد می‌کند.</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Wed, 11 Feb 2026 07:36:19 +0330</pubDate>
            </item>
                    <item>
                <title>امکان‌سنجی و نیازسنجی در مهندسی نرم‌افزار</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/%D8%A7%D9%85%DA%A9%D8%A7%D9%86-%D8%B3%D9%86%D8%AC%DB%8C-%D9%88-%D9%86%DB%8C%D8%A7%D8%B2%D8%B3%D9%86%D8%AC%DB%8C-%D8%AF%D8%B1-%D9%85%D9%87%D9%86%D8%AF%D8%B3%DB%8C-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-ungkhflvusub</link>
                <description>چیزهایی که قبل از نوشتن SRS باید جدی بگیریمسلام عزیزان؛ تقریباً همه‌ی ما حداقل یک بار توی پروژه‌ای بودیم که خیلی پرانرژی شروع شد، اما وسط راه یا به بن‌بست خورد یا محصول نهایی چیزی نبود که کارفرما واقعاً می‌خواست. جالب اینجاست که در بیشتر این شکست‌ها مشکل نه تکنولوژی بوده، نه تیم، نه حتی زمان‌بندی؛ مشکل از خیلی قبل‌تر شروع شده، جایی قبل از اینکه حتی یک خط از سند SRS نوشته شود. جایی به نام «نیازسنجی و امکان‌سنجی».فرایند امکان‌سنجی و نیازسنجی در مهندسی نرم‌افزاردر مهندسی نرم‌افزار، قبل از اینکه وارد فاز تحلیل رسمی نیازمندی‌ها بشویم، معمار و تحلیل‌گر نرم‌افزار باید یک قدم عقب‌تر بایستند و از خودشان بپرسند: آیا اصلاً این پروژه شدنی است؟ و اگر شدنی است، دقیقاً چه نیازی قرار است برطرف شود؟ این دو سؤال ساده، هسته‌ی اصلی امکان‌سنجی و نیازسنجی را تشکیل می‌دهند، اما پاسخ دادن به آن‌ها اصلاً ساده نیست.نیازسنجی، برخلاف تصور رایج، فقط لیست کردن خواسته‌های کارفرما نیست. خیلی وقت‌ها چیزی که کارفرما بیان می‌کند، راه‌حل ذهنی اوست نه مسئله‌ی واقعی. اینجاست که نقش تحلیل‌گر نرم‌افزار پررنگ می‌شود؛ کسی که بتواند با سؤال‌های درست، گفتگوهای هدفمند و حتی گاهی به چالش کشیدن فرضیات اولیه، به نیاز واقعی کسب‌وکار برسد. نیازی که اگر درست فهمیده نشود، بهترین SRS دنیا هم فقط یک سند خوش‌فرم ولی بی‌خاصیت خواهد بود.از آن طرف، امکان‌سنجی بیشتر به زمین واقعیت‌ها وصل است. اینکه آیا با منابع فعلی، محدودیت‌های زمانی، بودجه، تیم، فناوری و زیرساخت موجود، می‌توان این نیاز را به یک محصول قابل استفاده تبدیل کرد یا نه. معمار نرم‌افزار اینجا نقش کلیدی دارد؛ کسی که باید تصویر کلان سیستم را ببیند و تشخیص دهد آیا این ایده با این شرایط، عملی است یا فقط روی کاغذ قشنگ به نظر می‌رسد.نکته‌ی مهم اینجاست که امکان‌سنجی و نیازسنجی کاملاً به هم گره خورده‌اند. گاهی در فرآیند امکان‌سنجی مشخص می‌شود که بخشی از نیازها باید بازتعریف شوند، ساده‌تر شوند یا حتی حذف شوند. این اتفاق نه شکست است و نه عقب‌گرد؛ اتفاقاً نشانه‌ی یک فرآیند بالغ و حرفه‌ای است که قبل از ورود به فاز تولید، جلوی هزینه‌های سنگین آینده را می‌گیرد.قبل از تولید SRS، تحلیل‌گر و معمار نرم‌افزار باید به یک درک مشترک با ذی‌نفعان برسند. درکی که فقط شامل «چه چیزی بسازیم» نباشد، بلکه «چرا می‌سازیم»، «برای چه کسی»، و «با چه محدودیت‌هایی» را هم پوشش دهد. اگر این لایه‌ی مفهومی شکل نگیرد، SRS بیشتر شبیه ترجمه‌ی عجولانه‌ی حرف‌ها می‌شود تا یک سند مهندسی.واقعیت این است که خیلی از مشکلات پروژه‌ها نه در کدنویسی، نه در تست، بلکه در همین فاز خاکستریِ قبل از SRS شکل می‌گیرند. فازی که معمولاً عجله داریم زود از آن رد شویم تا برسیم به بخش‌های «باحال‌تر» پروژه. اما تجربه نشان داده هر ساعتی که اینجا با دقت و حوصله صرف شود، می‌تواند ده‌ها ساعت دوباره‌کاری و تنش را در ادامه‌ی مسیر کم کند.در نهایت، امکان‌سنجی و نیازسنجی بیشتر از اینکه یک فعالیت مستندسازی باشند، یک فعالیت انسانی‌اند. پر از گفتگو، شنیدن، تحلیل، تردید و تصمیم‌گیری. شاید به همین دلیل است که هیچ وقت نمی‌شود آن‌ها را کاملاً به یک قالب یا چک‌لیست خشک محدود کرد. اینجا جایی است که مهندسی نرم‌افزار، واقعاً مهندسی می‌شود؛ ترکیبی از منطق، تجربه و درک انسان‌ها.برای ملموس‌تر شدن نقش امکان‌سنجی و نیازسنجی، بد نیست به دو سناریوی رایج در دنیای واقعی نگاه کنیم؛ سناریوهایی که تقریباً همه‌ی ما حداقل یکی از آن‌ها را تجربه کرده‌ایم.در سناریوی اول؛کارفرما یک موجود بیرونی است؛ یک سازمان، استارتاپ یا کسب‌وکار که با یک ایده یا درخواست سراغ تیم نرم‌افزاری می‌آید. معمولاً کارفرما با یک تصویر ذهنی نسبتاً مشخص وارد می‌شود: «یک سیستم شبیه فلان»، «یک اپ مثل بهمان» یا «چیزی که این مشکل را حل کند». در این حالت، اگر تیم تحلیل بدون نیازسنجی عمیق وارد نوشتن SRS شود، احتمال زیادی وجود دارد که سند نهایی صرفاً بازنویسی خواسته‌های اولیه‌ی کارفرما باشد، نه بازتاب نیاز واقعی او.اینجا نیازسنجی یعنی کشف مسئله‌ی اصلی پشت درخواست. شاید کارفرما اپلیکیشن می‌خواهد، اما مشکل واقعی‌اش فرآیند ناکارآمد داخلی است. شاید داشبورد مدیریتی می‌خواهد، اما داده‌ی قابل اتکا ندارد. هم‌زمان، امکان‌سنجی هم وارد بازی می‌شود؛ اینکه آیا با بودجه، زمان و سطح بلوغ سازمان کارفرما، پیاده‌سازی چنین سیستمی منطقی است یا نه. خیلی وقت‌ها خروجی درست این فاز، حتی می‌تواند پیشنهاد یک راه‌حل ساده‌تر یا فازبندی پروژه باشد، چیزی که اگر بعداً و وسط توسعه کشف شود، هزینه‌اش چند برابر خواهد بود.در سناریوی دوم؛کارفرما خود شرکت است؛ یعنی تیم نرم‌افزار قرار است روی یک محصول داخلی یا یک محصول بازارمحور کار کند. در نگاه اول ممکن است فکر کنیم نیازسنجی اینجا ساده‌تر است، چون «خودمان هستیم و خودمان». اما تجربه نشان داده این سناریو گاهی حتی پیچیده‌تر هم می‌شود. چون فرضیات نانوشته زیادند و همه فکر می‌کنند بقیه دقیقاً همان چیزی را می‌فهمند که خودشان در ذهن دارند.در این حالت، نیازسنجی بیشتر به شفاف‌سازی دیدگاه‌ها برمی‌گردد. اینکه این محصول دقیقاً چه مشکلی را حل می‌کند، مخاطب اصلی‌اش کیست، و قرار است چه ارزشی ایجاد کند. امکان‌سنجی هم فقط فنی نیست؛ باید دید آیا این محصول با استراتژی شرکت هم‌راستاست، آیا منابع انسانی لازم برای نگه‌داری آن در بلندمدت وجود دارد، و آیا زمان ورود به بازار منطقی انتخاب شده یا نه. خیلی از محصول‌های داخلی شکست می‌خورند نه به‌خاطر بد بودن ایده، بلکه چون این سؤال‌ها قبل از شروع جدی گرفته نشده‌اند.در هر دو سناریو، یک نکته مشترک وجود دارد:اگر امکان‌سنجی و نیازسنجی به‌درستی انجام نشوند، SRS بیشتر شبیه یک تعهدنامه‌ی پرریسک می‌شود تا یک سند راهنما. اما اگر این فاز با دقت و گفت‌وگوی واقعی جلو برود، SRS می‌تواند نتیجه‌ی یک فهم مشترک و محکم باشد؛ فهمی که هم تیم فنی و هم ذی‌نفعان روی آن ایستاده‌اند.این همه از اسناد SRS صحبت کردیم باهم. معماران و تحلیل گران عزیز میدانند؛ ما یکسری اسناد دیگری داریم تحت عناوین BRS و FRS که به نوعی در فرایند امکان سنجی و نیازسنجی تولید میشوند. در این مقاله بیشتر در مورد این اسناد و مقایسه اونها صحبت کردم.امیدوارم مفید بوده باشه ...</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Wed, 11 Feb 2026 07:13:08 +0330</pubDate>
            </item>
                    <item>
                <title>تفاوت REST، gRPC و GraphQL</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/%D8%AA%D9%81%D8%A7%D9%88%D8%AA-rest-grpc-%D9%88-graphql-o4ilqsl2p8qx</link>
                <description>کدام معماری API برای ما مناسب‌تر است؟اگر چند سالی است که با توسعه‌ی نرم‌افزار، مخصوصاً بک‌اند یا سیستم‌های توزیع‌شده سر و کار دارید، احتمالاً اسم REST، GraphQL و gRPC بارها به گوشتان خورده است. بعضی وقت‌ها حتی این سؤال پیش می‌آید که «خب، همه‌شون API هستن، دقیقاً فرقشون چیه؟» یا «آیا GraphQL قراره جای REST رو بگیره؟» یا «gRPC اصلاً به درد وب می‌خوره یا فقط برای سیستم‌های خاصه؟»در این مقاله سعی می‌کنم بدون اغراق، اما عمیق و فنی، این سه رویکرد را بررسی کنم؛ از فلسفه‌ی طراحی گرفته تا جزئیات فنی، مزایا، محدودیت‌ها و سناریوهای واقعی استفاده.Rest | graphQL | gRPCREST؛ استاندارد قدیمی، سادهREST یا Representational State Transfer در اصل یک سبک معماری است، نه یک تکنولوژی مشخص. یعنی REST به شما نمی‌گوید حتماً از چه کتابخانه یا پروتکلی استفاده کنید، بلکه یک سری اصول و قرارداد تعریف می‌کند برای این‌که کلاینت و سرور بتوانند به شکل ساده و قابل پیش‌بینی با هم حرف بزنند.در REST همه‌چیز حول مفهوم «Resource» می‌چرخد. شما یک منبع دارید، مثلاً user یا order، و این منبع از طریق URL قابل دسترسی است. عملیات‌ها هم معمولاً با HTTP Methodها مشخص می‌شوند؛ GET برای خواندن، POST برای ساختن، PUT/PATCH برای به‌روزرسانی و DELETE برای حذف. این سادگی باعث شده REST به انتخاب پیش‌فرض دنیای وب تبدیل شود.از نظر فنی، REST معمولاً روی HTTP/1.1 یا HTTP/2 پیاده‌سازی می‌شود و رایج‌ترین فرمت تبادل داده هم JSON است. همین انتخاب‌ها باعث شده REST بسیار قابل دیباگ باشد؛ شما به‌راحتی می‌توانید با curl یا Postman درخواست بزنید، پاسخ را ببینید و حتی در مرورگر تستش کنید.اما همین سادگی، محدودیت‌هایی هم ایجاد می‌کند. یکی از مشکلات معروف REST مسئله‌ی Over-fetching و Under-fetching است. یعنی یا دیتای بیشتری از نیازتان می‌گیرید یا مجبور می‌شوید برای گرفتن یک داده‌ی کامل، چندین درخواست مختلف بفرستید. مخصوصاً در اپلیکیشن‌های موبایل یا فرانت‌اندهای پیچیده، این موضوع می‌تواند آزاردهنده باشد.از نظر نسخه‌بندی هم REST معمولاً به ورژن‌بندی در URL یا Header متکی است (مثلاً /v1/users). این کار جواب می‌دهد، اما وقتی API بزرگ می‌شود، مدیریت نسخه‌ها می‌تواند پیچیده شود.به طور خلاصه REST هنوز هم انتخاب بسیار خوبی است وقتی:API ساده و قابل فهم میخواهیم درست کنیمبا وب و HTTP سر و کار داریمتیم بزرگ است و خوانایی و استاندارد مهم استPerformance خیلی بحرانی نیستمثال REST APIفرض کنیم یک API ساده با Node.js و Express داریم.EndpointGET /users/:id
پیاده‌سازی سمت سرورimport express from &quot;express&quot;;

const app = express();

app.get(&quot;/users/:id&quot;, (req, res) =&gt; {
  const userId = req.params.id;

  // این رو فرض کنیم دیتابیسه برگردونده برامون
  const user = {
    id: userId,
    name: &quot;Mojtaba&quot;,
    email: &quot;mojtaba@example.com&quot;,
    age: 35,
    posts: 15
  };

  res.json(user);
});

app.listen(3000, () =&gt; {
  console.log(&quot;REST API running on port 3000&quot;);
});
درخواست از سمت کلاینتGET /users/1 HTTP/1.1
Host: api.example.comپاسخ{
  &quot;id&quot;: &quot;1&quot;,
  &quot;name&quot;: &quot;Mojtaba&quot;,
  &quot;email&quot;: &quot;Mojtaba@example.com&quot;,
  &quot;age&quot;: 35,
  &quot;posts&quot;: 15
}در REST، شکل پاسخ توسط سرور تعیین میشه. حتی اگر کلاینت فقط name و email را بخواهد، کل آبجکت برمیگرده.GraphQL؛ کنترل کامل داده در دست کلاینتGraphQL توسط فیسبوک معرفی شد تا یکی از مشکلات اساسی REST را حل کند:این‌که کلاینت دقیقاً همان دیتایی را بگیرد که نیاز دارد، نه بیشتر و نه کمتر.در GraphQL برخلاف REST، ما چند endpoint مختلف نداریم؛ معمولاً فقط یک endpoint وجود دارد. کلاینت داخل request خودش مشخص می‌کند چه فیلدهایی را از چه آبجکت‌هایی می‌خواهد. سرور هم دقیقاً همان ساختار را برمی‌گرداند.این رویکرد از نظر تجربه‌ی توسعه، مخصوصاً در فرانت‌اند، فوق‌العاده هستش. فرانت‌اند دیگر وابسته به تصمیم‌های بک‌اند درباره‌ی شکل response نیست. اگر فردا نیاز به یک فیلد جدید داشته باشیم، کافی است query را تغییر دهیم، بدون این‌که API جدیدی ساخته شود. (این از نظر تجربی خیلی موضوع مهمی است)GraphQL یک Schema strongly typed دارد. یعنی از قبل مشخص است چه typeهایی داریم، چه فیلدهایی، چه ورودی‌هایی و چه خروجی‌هایی. همین موضوع باعث می‌شود tooling خیلی قوی‌ای شکل بگیرد؛ از auto-complete در IDE گرفته تا validation قبل از اجرا و هر آن چیزی که جریان ما رو تحت تاثیر بتونه قرار بده.اما این قدرت، هزینه هم داره. پیاده‌سازی GraphQL در بک‌اند معمولاً پیچیده‌تر از REST است. باید حواسمون به performance باشه، چون یک query بد میتونه منجر به تعداد زیادی call به دیتابیس بشه (مشکل معروف N+1). این مشکل رو در گروه فنی تلگرامی Dev Experience (@devexperiences) شرح دادم. همچنین caching در GraphQL برخلاف REST که روی HTTP Cache سوار می‌شود، نیاز به راهکارهای اختصاصی دارد. (در مقاله بعدی بیشتر بهش خواهم پرداخت موضوع کش رو)از نظر امنیت هم باید دقت بیشتری کرد؛ چون کلاینت می‌تواند queryهای پیچیده بنویسد، معمولاً محدودیت عمق (query depth) یا complexity تعریف می‌شود.GraphQL انتخاب مناسبی است وقتی:فرانت‌اند پیچیده و پویا داریمچند کلاینت مختلف (وب، موبایل و ...) داریمتغییرات مداوم در نیازهای داده وجود داردتیم توان مدیریت پیچیدگی بک‌اند را داردمثال GraphQLدر GraphQL همان سناریو قبلی که نوشیم واسه REST را داریم، اما کنترل داده دست کلاینت هستش.Schemaimport { buildSchema } from &quot;graphql&quot;;

export const schema = buildSchema(`
  type User {
    id: ID!
    name: String!
    email: String!
    age: Int
    posts: Int
  }

  type Query {
    user(id: ID!): User
  }
`);
Resolverexport const root = {
  user: ({ id }) =&gt; {
    return {
      id,
      name: &quot;Mojtaba&quot;,
      email: &quot;mojtaba@example.com&quot;,
      age: 35,
      posts: 15
    };
  }
};
Query از سمت کلاینتquery {
  user(id: &quot;1&quot;) {
    name
    email
  }
}
پاسخ{
  &quot;data&quot;: {
    &quot;user&quot;: {
      &quot;name&quot;: &quot;Mojtaba&quot;,
      &quot;email&quot;: &quot;mojtaba@example.com&quot;
    }
  }
}
اینجا دقیقاً همان چیزی که درخواست شده برگشته؛نه age داریم، نه posts.این همان جایی هست که GraphQL مشکل over-fetching را حل می‌کند.gRPC؛ ارتباط سریع، فشرده و مخصوص سیستم‌های توزیع‌شدهgRPC بیشتر از این‌که برای وب عمومی طراحی شده باشد، برای ارتباط بین سرویس‌ها (Microservices) ساخته شده است. این تکنولوژی توسط گوگل توسعه داده شده و تمرکز اصلی‌اش روی performance، type safety و ارتباطات پایدار است.gRPC به‌جای JSON از Protocol Buffers (Protobuf) استفاده می‌کند؛ یک فرمت باینری فشرده و بسیار سریع. قرارداد ارتباطی (Service و Messageها) در فایل‌های .proto تعریف می‌شود و بعد از روی آن‌ها کد کلاینت و سرور به زبان‌های مختلف به‌صورت خودکار تولید می‌شود.از نظر شبکه، gRPC روی HTTP/2 سوار است و قابلیت‌هایی مثل multiplexing، streaming (یک‌طرفه یا دوطرفه) و connection پایدار را به‌صورت built-in دارد. این ویژگی‌ها باعث می‌شود gRPC برای سیستم‌هایی با latency بالا یا حجم بالای درخواست فوق‌العاده باشد.اما gRPC برای استفاده‌ی مستقیم در مرورگر محدودیت دارد. مرورگرها به‌صورت native از gRPC پشتیبانی نمی‌کنند (مگر با gRPC-Web که خودش داستان جداگانه‌ای داره و باید در یک پست دیگه حتما راجبش بحث و گفتگو کنیم). همچنین دیباگ کردن gRPC نسبت به REST سخت‌تر است؛ چون داده‌ها باینری هستند و با چشم دیده نمی‌شوند.gRPC معمولاً انتخاب اول نیست برای public API، اما برای ارتباط داخلی بین سرویس‌ها بسیار محبوب است.gRPC بهترین گزینه است وقتی:Performance و latency حیاتی هست برامونمعماری Microservice داریم و یا حداقل شبه میکروسرویسارتباط server-to-server داریم در پروژهtype safety و قرارداد مشخص مهم است برامونمثال gRPCدر gRPC اول قرارداد ارتباطی را تعریف می‌کنیم و بعد از روی آن کد تولید می‌شود.فایل protosyntax = &quot;proto3&quot;;

package user;

service UserService {
  rpc GetUser (UserRequest) returns (UserResponse);
}

message UserRequest {
  string id = 1;
}

message UserResponse {
  string id = 1;
  string name = 2;
  string email = 3;
  int32 age = 4;
  int32 posts = 5;
}
پیاده‌سازی سرور (Node.js)import grpc from &quot;@grpc/grpc-js&quot;;
import protoLoader from &quot;@grpc/proto-loader&quot;;

const packageDefinition = protoLoader.loadSync(&quot;user.proto&quot;);
const userProto = grpc.loadPackageDefinition(packageDefinition).user;

function getUser(call, callback) {
  callback(null, {
    id: call.request.id,
    name: &quot;Mojtaba&quot;,
    email: &quot;mojtaba@example.com&quot;,
    age: 35,
    posts: 15
  });
}

const server = new grpc.Server();
server.addService(userProto.UserService.service, { getUser });

server.bindAsync(
  &quot;0.0.0.0:50051&quot;,
  grpc.ServerCredentials.createInsecure(),
  () =&gt; server.start()
);
کلاینت gRPCconst client = new userProto.UserService(
  &quot;localhost:50051&quot;,
  grpc.credentials.createInsecure()
);

client.getUser({ id: &quot;1&quot; }, (err, response) =&gt; {
  console.log(response);
});
پاسخ (باینری در شبکه، ولی آبجکت در کد){
  id: &#039;1&#039;,
  name: &#039;Mojtaba&#039;,
  email: &#039;mojtaba@example.com&#039;,
  age: 35,
  posts: 15
}
در gRPC:قرارداد کاملاً type-safe استدیتا باینری و بسیار سریع منتقل می‌شودمناسب ارتباط service-to-service است، نه مصرف مستقیم در مرورگرمن اگر بخوام جمع کنم موضوع رو ...واقعیت این است که هیچ‌کدام «بهتر مطلق» نیستند. REST، GraphQL و gRPC هر کدام برای مسئله‌ای خاص طراحی شده. خیلی از سیستم‌های حرفه‌ای امروزی حتی از ترکیب این‌ها استفاده می‌کنند؛ مثلاً GraphQL برای فرانت‌اند، gRPC برای ارتباط داخلی سرویس‌ها و REST برای APIهای عمومی.سؤال اصلی همیشه این است:مسئله‌ی من چیست و کدام ابزار بهترین پاسخ را می‌دهد؟اگر این سؤال را درست بپرسیم، انتخاب معماری API هم معمولاً خودش را نشان می‌دهد.REST: ساده، خوانا، مناسب APIهای عمومیGraphQL: انعطاف‌پذیر، کلاینت‌محور، مناسب فرانت‌اندهای پیچیدهgRPC: سریع، تایپ‌سیف، عالی برای میکروسرویس‌ها</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Tue, 03 Feb 2026 14:31:07 +0330</pubDate>
            </item>
                    <item>
                <title>کلاس‌های Rich و Anemic؛ وقتی کلاس فقط نگهدارنده دیتا نیست</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/%DA%A9%D9%84%D8%A7%D8%B3-%D9%87%D8%A7%DB%8C-rich-%D9%88-anemic-%D9%88%D9%82%D8%AA%DB%8C-%DA%A9%D9%84%D8%A7%D8%B3-%D9%81%D9%82%D8%B7-%D9%86%DA%AF%D9%87%D8%AF%D8%A7%D8%B1%D9%86%D8%AF%D9%87-%D8%AF%DB%8C%D8%AA%D8%A7-%D9%86%DB%8C%D8%B3%D8%AA-wy7ulaslwg5p</link>
                <description>سلام؛ اگر مدتی با برنامه‌نویسی شی‌گرا کار کرده باشیم، احتمالاً به کلاس‌هایی برخورد کردیم که فقط چند تا فیلد دارن و کلی getter و setter. این کلاس‌ها معمولاً هیچ تصمیمی نمی‌گیرن، هیچ رفتاری ندارن و صرفاً یه بسته برای جابه‌جایی دیتا هستن. اینجاست که ناخودآگاه این سؤال پیش میاد که آیا این چیزی که نوشتیم واقعاً یه «شیء» به حساب میاد یا نه؟ بحث کلاس‌های Anemic و Rich دقیقاً از همین نقطه شروع میشه.Rich vs Anemic | OOP &amp; DDD Conceptsدر این مقاله، به این موضوع خواهیم پرداخت و اساساً بررسی میکنیم که ویژگی های این دو جریان چه تاثیری در کل سیستم خواهند داشت. بریم سراغ اصل مطلب.ابتدا میخوام این موضوع رو باهم داشته باشیم که واژه های Rich و Anemic برای توصیف وضعیت یک کلاس به کاربرده میشه. اگر کلاس ما بسیار ساده و صرفا دارای property و getter setter باشه (همون موضوعی که در اول مقاله بیان شد) میگیم کلاسه Anemic هست ولی در مقابل اگر کلاس ما دارای منطق باشه و این منطق ها رو از طریق متد ها بیاییم و داخل کلاس بنویسیم، این کلاس میشه Rich. حالا بریم دقیق تر بررسی کنیم ببینیم یعنی چی این موضوع.Anemic Model؛ کلاس‌هایی که کم‌خون شدن 😄همون طور که گفتیم، Anemic Model به مدلی گفته میشه که توش کلاس‌ها تقریباً هیچ منطق خاصی ندارن. مسئولیتشون فقط نگهداری داده‌ست و تمام قوانین و تصمیم‌ها میرن توی سرویس‌ها. در ظاهر شاید تمیز به نظر بیاد، اما کم‌کم ساختار کد از شی‌گرایی فاصله می‌گیره.مثلاً یه کلاس حساب بانکی رو در نظر بگیریم که فقط موجودی رو نگه می‌داره و هیچ ایده‌ای نداره برداشت پول یعنی چی:class BankAccount {
     private int balance;

     public int getBalance() {
         return balance;
     }

     public void setBalance(int balance) {
         this.balance = balance; 
    } 
} میاییم و میگیم، منطق برداشت پول جایی بیرون از خود حساب خواهیم نوشت:class BankAccountService { 
     
     public void withdraw(BankAccount account, int amount) {
         if (amount &lt;= 0) {
             throw new IllegalArgumentException();
         }

         if (account.getBalance() &lt; amount) {
             throw new RuntimeException(&quot;Not enough balance&quot;);
         }

          account.setBalance(account.getBalance() - amount);
     }
 } در این حالت، کلاس BankAccount هیچ کنترلی روی وضعیت خودش نداره. هر جایی از سیستم می‌تونه موجودی رو مستقیم تغییر بده، بدون اینکه قوانین رعایت بشه. این مدل طراحی کم‌کم باعث میشه منطق دامنه پخش بشه و کد بیشتر شبیه برنامه‌نویسی رویه‌ای بشه تا شی‌گرا. (معمار هامون باید دقت داشته باشند در اینجا که اول کار، چه تصمیمی گرفته میشه و به برنامه نویس ها و توسعه دهنده ها ابلاغ میشه)Rich Model؛ وقتی شیء مسئول خودش استدر مقابل Anemic Model، رویکرد Rich Model قرار داره. توی این مدل، کلاس‌ها فقط نگهدارنده‌ی دیتا نیستن؛ بلکه رفتار و قوانین مربوط به خودشون رو هم می‌شناسن و اجرا می‌کنن. یعنی هر شیء مسئول اینه که اجازه نده به وضعیت نامعتبر بره.همون مثال حساب بانکی رو این بار به شکل Rich ببینیم:class BankAccount {
     private int balance;

     public BankAccount(int initialBalance) {
         if (initialBalance &lt; 0) {
             throw new IllegalArgumentException();
         }

         this.balance = initialBalance;
     }

     public void withdraw(int amount) {
         if (amount &lt;= 0) {
             throw new IllegalArgumentException();
         }

         if (balance &lt; amount) {
             throw new RuntimeException(&quot;Not enough balance&quot;);
         }

         balance -= amount;
     }

     public int getBalance() {
         return balance;
     }
 } اینجا حساب بانکی خودش می‌دونه برداشت پول یعنی چی و چه قوانینی داره. سرویس فقط هماهنگ‌کننده‌ست و کار خاصی جز صدا زدن متد انجام نمی‌ده. نتیجه کدی میشه که هم خواناتر و هم امن‌تره.چرا با وجود این همه ایراد، Anemic Model هنوز استفاده میشه؟واقعیت اینه که Anemic Model خیلی وقت‌ها انتخاب بدی نیست. فریم‌ورک‌هایی مثل Spring و JPA (من خودم جاوایی ام چون 😄) به‌طور ناخودآگاه ما رو به این سمت هل میدن. از طرفی برای پروژه‌های ساده‌ی CRUD، این مدل سریع‌تر جواب می‌ده و پیچیدگی ذهنی کمتری داره. اما مشکل معمولاً از جایی شروع میشه که پروژه رشد می‌کنه، قوانین بیشتر میشن و منطق دامنه پیچیده‌تر میشه. اون موقع سرویس‌ها چاق میشن، کلاس‌ها لاغر می‌مونن و تغییر دادن قوانین تبدیل به کار پرریسکی میشه. (همون طور که بالا هم گفتم، معمار ما باید اول کار، تصمیم درست رو بگیره و بر اساس استک و وضعیت کلی پروژه، بدونه که رویکرد ما در پروژه نسبت به کلاس هامون، چی خواهد بود)Rich Model و ارتباطش با DDDاگه با Domain-Driven Design آشنا باشیم، احتمالاً شنیدیم که میگن موجودیت‌ها باید «رفتار» داشته باشن. Rich Model دقیقاً با همین طرز فکر هم‌راستاست. توی این رویکرد، entityها مسئول حفظ invariantها هستن و اجازه نمیدن سیستم وارد وضعیت اشتباه بشه. اگر با کلید واژه های DDD آشنا نیستی، یه سر به اینجا بزن.یرای مثال، فعال‌سازی یک کاربر بهتره تصمیم خود کاربر باشه، نه یه سرویس بیرونی که هر کاری دلش خواست با وضعیتش بکنه. بریم این رو یه کد برنیم و ببینیم چی به چی هست ...این کلاس ما الان یک کلاس Anemic هست که منطق اون در یه سرویس بیرونی هست:class User {
    private String email;
    private boolean active;

    // getter &amp; setter

}class UserService {
    public void activate(User user) {

        if (user.isActive()) {

            throw new RuntimeException(&quot;Already active&quot;);
        }

        user.setActive(true);
    }
}تغییر رویکرد میدیم و اون رو به Rich تبدیل میکنیم:class User {
    private String email;
    private boolean active;

    public void activate() {

        if (active) {

            throw new RuntimeException(&quot;Already active&quot;);
        }

        active = true;
    }
}جمع‌بندیدر نهایت، Rich و Anemic بیشتر از اینکه خوب یا بد مطلق باشن، دو تا انتخاب طراحی هستن که باید با توجه به نیاز پروژه انتخاب بشن: Anemic Model برای پروژه‌های ساده و دیتامحور می‌تونه کافی و حتی منطقی باشه Rich Model وقتی ارزش خودش رو نشون می‌ده که منطق دامنه مهمه و قراره سیستم در طول زمان رشد کنه اگه قراره فقط دیتا جابه‌جا کنی، Anemic اذیتت نمی‌کنه. ولی اگه قراره منطق بسازی، قوانین رو نگه داری و سیستم قابل توسعه داشته باشی، Rich Model انتخاب عاقلانه‌تریه.در مقالات بعدی، بیشتر در مورد این مفاهیم صحبت خواهیم کرد ...</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Sun, 25 Jan 2026 07:31:17 +0330</pubDate>
            </item>
                    <item>
                <title>Idempotency در مهندسی نرم افزار</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/idempotency-%D8%AF%D8%B1-%D9%85%D9%87%D9%86%D8%AF%D8%B3%DB%8C-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-rmauv2tvjeio</link>
                <description>Idempotency؛ مفهومی ساده با اثرات حیاتی در طراحی سیستم‌هاسلام دوستان؛ مستقیم میرم سر اصل مطلب. اگر بخوام صادقانه بگم، idempotency جزو اون مفاهیمیه که خیلی‌ها فکر می‌کنن می‌دوننش، ولی وقتی پای پیاده‌سازی واقعی وسط میاد، تازه عمق ماجرا مشخص می‌شه. مخصوصاً وقتی سیستم بزرگ می‌شه، توزیع‌شده می‌شه، یا با پول و داده‌ی حساس سروکار داره. idempotency دقیقاً همون چیزیه که جلوی خیلی از فاجعه‌های نرم‌افزاری رو قبل از اتفاق گرفتن می‌گیره.به زبان ساده، idempotency یعنی اجرای چندباره‌ی یک عملیات، سیستم رو به وضعیت جدیدی نبره و نتیجه نهایی همون نتیجه‌ی اجرای یک‌باره باشه. نکته‌ی مهم اینجاست که این «نتیجه» فقط مقدار خروجی نیست؛ شامل side effectها هم می‌شه. یعنی نه داده‌ی تکراری ساخته بشه، نه پول دوبار کم بشه، نه state سیستم از کنترل خارج بشه.ریشه‌ی idempotency؛ از ریاضیات تا مهندسی نرم‌افزارمفهوم idempotency در اصل از ریاضیات میاد. تابعی idempotent محسوب می‌شه که اگر چند بار روی یک ورودی اعمال بشه، خروجی تغییر نکنه. علوم کامپیوتر این مفهوم رو گرفت و به یکی از پایه‌های تفکر در طراحی سیستم‌های قابل‌اعتماد تبدیلش کرد.در مهندسی نرم‌افزار، idempotency بیشتر از اینکه یک تعریف تئوریک باشه، یک استراتژی برای بقا در دنیای غیرقابل‌اعتماد شبکه‌هاست. چون در عمل، هیچ تضمینی وجود نداره که یک درخواست فقط یک بار ارسال یا فقط یک بار پردازش بشه.idempotency در HTTP و APIها؛ قرارداد نانوشته اما حیاتیدر دنیای وب، idempotency به‌صورت رسمی در استانداردهای HTTP تعریف شده. طبق RFC 7231، بعضی متدهای HTTP ذاتاً idempotent در نظر گرفته می‌شن. این یعنی کلاینت حق داره اون‌ها رو چند بار صدا بزنه، بدون اینکه نگران تغییر ناخواسته‌ی وضعیت سرور باشه.اما این فقط یک «انتظار» از سمت پروتکله، نه یک تضمین خودکار. اگر backend درست طراحی نشده باشه، حتی PUT هم می‌تونه غیر-idempotent بشه. اینجاست که مسئولیت کامل می‌افته روی دوش طراح سیستم.جدول idempotency در متدهای HTTPجدول idempotency در متد های HTTPمثال واقعی ۱: پرداخت آنلاین؛ جایی که idempotency نجات‌دهنده استفرض کن کاربر روی دکمه‌ی «پرداخت» کلیک می‌کنه. درخواست به سرور می‌رسه، پول کم می‌شه، ولی پاسخ به‌خاطر timeout به کاربر نمی‌رسه. کاربر دوباره روی دکمه کلیک می‌کنه. اگر سیستم idempotent نباشه، پول دوباره کم می‌شه. همین یک سناریو به‌تنهایی کافیه تا بفهمیم idempotency شوخی‌بردار نیست.راه‌حل رایج اینه که برای هر درخواست پرداخت، یک idempotency key یکتا تعریف بشه. سرور اگر دوباره همون key رو ببینه، نتیجه‌ی قبلی رو برمی‌گردونه، نه اینکه عملیات رو تکرار کنه.مثال واقعی ۲: ثبت سفارش در فروشگاه اینترنتیدر ثبت سفارش، معمولاً POST استفاده می‌شه. اما اگر درخواست دوبار ارسال بشه، دو سفارش ساخته می‌شه. اینجاست که یا باید:شناسه‌ی سفارش از سمت کلاینت بیادیا server-side تشخیص بده که این درخواست قبلاً پردازش شدهدر غیر این صورت، دیتابیس پر از سفارش‌های تکراری می‌شه و کسی هم نمی‌فهمه مشکل از کجاست.مثال واقعی ۳: مصرف پیام در سیستم‌های صف و event-drivenدر Kafka یا RabbitMQ، دریافت دوباره‌ی پیام یک اتفاق کاملاً طبیعیه. مصرف‌کننده ممکنه crash کنه، یا commit offset انجام نشه. اگر پردازش پیام idempotent نباشه، نتیجه می‌تونه ثبت چندباره‌ی یک عملیات باشه.به همین دلیله که در سیستم‌های event-driven، idempotent consumer یک اصل طراحی محسوب می‌شه، نه یک بهینه‌سازی. (معمار ها میدونن چی دارم میگم!)idempotency و دیتابیس؛ فراتر از transactionخیلی‌ها فکر می‌کنن transaction داشتن یعنی idempotency. ولی این اشتباهه. transaction فقط atomic بودن رو تضمین می‌کنه، نه idempotent بودن رو. یک INSERT داخل transaction اگر دوبار اجرا بشه، دوبار داده می‌سازه.برای رسیدن به idempotency در دیتابیس، معمولاً از این ابزارها استفاده می‌شه:unique constraintprimary key معنادارupsertcheck روی state قبلیاشتباهات رایج در درک و پیاده‌سازی idempotencyیکی از رایج‌ترین اشتباهات اینه که idempotency فقط در سطح API دیده می‌شه. در حالی که اگر منطق بیزینس یا دیتابیس idempotent نباشه، API هم عملاً idempotent نخواهد بود.اشتباه رایج دیگه اینه که idempotency با retry اشتباه گرفته می‌شه. retry یک مکانیزمه، idempotency یک خاصیته. retry بدون idempotency فقط احتمال خرابکاری رو بیشتر می‌کنه.و شاید خطرناک‌ترین اشتباه اینه که idempotency رو فقط برای «سیستم‌های بزرگ» ضروری بدونیم. واقعیت اینه که حتی یک سرویس کوچیک هم اگر قراره رشد کنه، باید از همون اول با این طرز فکر ساخته بشه.جمع‌بندی؛ چرا idempotency نشانه‌ی بلوغ معماری است؟idempotency یعنی پذیرش این واقعیت که دنیای واقعی پر از خطاست. یعنی طراحی سیستم به‌گونه‌ای که از تکرار نترسه. سیستمی که idempotent طراحی شده، نه‌تنها پایدارتره، بلکه قابل‌اعتمادتر، قابل‌دیباگ‌تر و در نهایت حرفه‌ای‌تره.اگر بخوای یک خط قرمز بین سیستم آماتور و سیستم بالغ بکشی، idempotency یکی از واضح‌ترین اون‌هاست.در مقاله بعدی بیشتر در مورد پیاده سازی این موضوع صحبت خواهیم کرد ...</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Sat, 24 Jan 2026 18:50:46 +0330</pubDate>
            </item>
                    <item>
                <title>Connascence در نرم افزار</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/connascence-%D8%AF%D8%B1-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-xdnhxesnbnst</link>
                <description>اگر چند سالی در دنیای توسعه نرم‌افزار کار کرده باشید، احتمالاً بارها با کدی مواجه شده‌اید که تغییر دادنش شبیه راه رفتن روی مین است؛ یک خط را عوض می‌کنید و ناگهان چند جای دیگر می‌ترکد. خیلی وقت‌ها ریشه این مشکل چیزی است به نام «وابستگی پنهان» یا به بیان دقیق‌تر، مفهومی به نام Connascence. این مفهوم شاید به اندازه SOLID یا Clean Architecture معروف نباشد، اما اگر درست فهمیده و استفاده شود، می‌تواند نگاه مهندسان و معماران نرم‌افزار را به طراحی کد عمیق‌تر و عملی‌تر کند. بیایید کمی در موردش صحبت کنیم.Connascence به زبان ساده یعنی «چند بخش از سیستم مجبورند با هم یک چیز مشترک را بدانند تا درست کار کنند». هر جا که تغییر در یک بخش، شما را مجبور می‌کند بخش‌های دیگر را هم تغییر دهید، احتمالاً با نوعی از connascence طرف هستید. این مفهوم اولین بار توسط Meilir Page-Jones مطرح شد و هدفش این بود که به ما کمک کند وابستگی‌ها را بهتر بشناسیم، اندازه‌گیری کنیم و تا حد امکان کنترلشان کنیم.صفحه رسمی از connascence.ioنکته مهم اینجاست که connascence ذاتاً چیز بدی نیست. هیچ سیستمی بدون وابستگی ساخته نمی‌شود. مسئله این است که نوع و شدت این وابستگی‌ها چقدر است و در کجا اتفاق می‌افتند. معماری خوب معماری‌ای نیست که وابستگی نداشته باشد، بلکه معماری‌ای است که وابستگی‌هایش آگاهانه، محدود و قابل مدیریت باشند. بهتر هست بدونیم که دو دسته اساسی Static Connascences و Dynamic Connascences ها رو داریم که دارای زیر مجموعه هستند. لیست رو نگاه کنیم و بریم برخی از اون ها رو بررسی کنیم.در بحث Static Connascencesموضوع Nameموضوع Typeموضوع Meaningموضوع Positionموضوع Algorithmدر بحث Dynamic Connascencesموضوع Executionموضوع Timingموضوع Valueموضوع Identityموضوع Algorithmبریم برخی از این ها رو باهم بررسی کنیم ...موضوع connascence of nameیکی از ساده‌ترین و البته خطرناک‌ترین انواع connascence، بحث connascence of name است. یعنی دو بخش از کد باید دقیقاً روی یک اسم به توافق برسند. مثلاً وقتی اسم یک متد، یک متغیر یا یک کلید JSON عوض می‌شود و کلی جای دیگر می‌شکند. این نوع وابستگی خیلی رایج است و معمولاً در نگاه اول بی‌ضرر به نظر می‌رسد، اما اگر در مرز ماژول‌ها یا بین سرویس‌ها باشد، هزینه تغییر را بالا می‌برد.موضوع connascence of typeنوع دیگر connascence of type است. اینجا دو بخش سیستم باید روی نوع داده با هم هماهنگ باشند. مثلاً وقتی یک متد انتظار دارد حتماً یک integer بگیرد و جای دیگر سیستم باید دقیقاً همان نوع را پاس بدهد. این نوع وابستگی نسبت به name کمی بهتر است، چون زبان‌های strongly typed می‌توانند کمک کنند، اما باز هم اگر در سطح معماری کنترل نشود، دردسرساز می‌شود.موضوع connascence of meaningconnascence of meaning یا semantic connascence یکی از خطرناک‌ترین‌هاست. اینجا کدها روی «معنا» با هم توافق دارند، نه روی اسم یا نوع. مثلاً عدد ۱ یعنی active و ۰ یعنی inactive، بدون اینکه این معنا جایی به‌صورت صریح بیان شده باشد. این نوع وابستگی معمولاً با عددهای جادویی و قراردادهای نانوشته به وجود می‌آید و فهم و نگهداری سیستم را سخت می‌کند.موضوع connascence of positionنوع دیگری که خیلی در کدهای قدیمی دیده می‌شود connascence of position است. یعنی ترتیب پارامترها یا داده‌ها اهمیت دارد. مثلاً متدی که سه پارامتر می‌گیرد و اگر جای دوتای آن‌ها عوض شود، سیستم به‌هم می‌ریزد. هرچقدر تعداد پارامترها بیشتر شود، احتمال خطا هم بالاتر می‌رود. استفاده از objectها یا named parameterها معمولاً راه فرار از این نوع وابستگی است.موضوع connascence of algorithmconnascence of algorithm زمانی اتفاق می‌افتد که چند بخش از سیستم باید دقیقاً از یک الگوریتم یا منطق یکسان استفاده کنند. اگر این منطق در چند جا کپی شده باشد، با کوچک‌ترین تغییر باید همه‌جا را اصلاح کنید. اینجا refactor کردن و متمرکز کردن منطق مشترک می‌تواند وابستگی را کم‌هزینه‌تر کند.موضوع connascence of nameدر سطح معماری، connascence of timing هم اهمیت پیدا می‌کند. یعنی دو بخش سیستم باید در زمان مشخصی نسبت به هم عمل کنند. مثلاً یک سرویس باید حتماً قبل از سرویس دیگر بالا بیاید یا یک متد باید قبل از متد دیگر صدا زده شود. این نوع وابستگی معمولاً در سیستم‌های توزیع‌شده خیلی دردناک است و اگر درست مدیریت نشود، سیستم شکننده می‌شود.یکی از نکات کلیدی در بحث connascence این است که فقط نوع آن مهم نیست، بلکه محل وقوعش هم اهمیت داره. connascence داخل یک متد یا یک کلاس معمولاً قابل قبول‌تر از connascence بین ماژول‌ها، لایه‌ها یا سرویس‌هاست. هرچقدر فاصله اجزای وابسته بیشتر باشد، هزینه تغییر هم بالاتر می‌رود. به همین دلیل است که می‌گویند وابستگی‌های قوی را تا جای ممکن به کوچک‌ترین محدوده ممکن هل بدهید.connascence همچنین به ما یک ابزار ذهنی برای refactor دادن می‌دهد. وقتی می‌خواهید کدی را بهبود دهید، به این فکر کنید که اگر این بخش تغییر کند، چه جاهای دیگری مجبور به تغییر می‌شوند و چرا. خیلی وقت‌ها با تبدیل یک connascence of meaning به connascence of name یا type، سیستم قابل فهم‌تر و امن‌تر می‌شود.در نهایت، connascence بیشتر از اینکه یک قانون خشک باشد، یک لنز برای نگاه کردن به طراحی نرم‌افزار است. اگر به‌عنوان مهندس یا معمار نرم‌افزار عادت کنیم وابستگی‌ها را با این دید بررسی کنیم، تصمیم‌هایمان آگاهانه‌تر می‌شود. کدهایی می‌نویسیم که تغییرپذیرترند، راحت‌تر تست می‌شوند و در بلندمدت کمتر اعصاب تیم را خرد می‌کنند. شاید اسم connascence خیلی دهان‌پرکن نباشد، اما اثرش در کیفیت نرم‌افزار کاملاً واقعی و ملموس است. اگر خیلی علاقه مند هستید که موضوع رو از دید مرجع اصلی و خیلی اساسی تر داشته باشید، میتونید به سر به این مرجع بزنید. این مقاله تلاش کرد تا سرنخ رو به شمای معمار و برنامه نویس بده.در مقاله بعدی میریم سراغ کد هایی که معمولا درسرساز میشند در شرکت ها ...</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Sat, 24 Jan 2026 16:24:50 +0330</pubDate>
            </item>
                    <item>
                <title>کلید واژه های DDD</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/%DA%A9%D9%84%DB%8C%D8%AF-%D9%88%D8%A7%DA%98%D9%87-%D9%87%D8%A7%DB%8C-ddd-juw2czctpwm6</link>
                <description>اگر در پروژه های کسب و کار محور کار کرده باشید، حتما در مورد Domain Driven Design یا به اختصار DDD اطلاعات کافی دارید و میدونید که یک تفکری هست که در حوالی سال 2003 توسط آقای Eric Evans نوشته شده است (ایشون الان حدود 65 سال سن دارند) و در حالت کلی داره میگه که در یک پروژه، چگونه میتوانیم جداسازی ها و تفکیک ها رو بر اساس دامنه های کاری بیزینس انجام بدیم و روش های درستش چی هست. اگر میخواهید اطلاعات کافی داشته باشید، کتاب مرجع Domain-Driven Design: Tackling Complexity in the Heart of Software رو بررسی کنید ولی اگر زمان کافی برای مطالعه رو ندارید، بریم سراغ بررسی کلید واژه ها و موضوعات اساسی که در این مقاله میخوام در موردشون صحبت کنم. این مقاله برای معماران و تحلیل گران نرم افزار، برنامه نویس ها و علاقه مندان به این حوزه مفید خواهد بود.روی صحبت مقاله با کسانی هست که میخواهند قبل از پیاده سازی DDD در پروژه های نرم افزاری که دارای لایه بندی و معماری Clean یا Onion یا Hexagonal یا هر معماری و پترن دیگریست، مفاهیم پایه DDD رو از نگاهی با جنس نرم افزار و کد تجربه کنند.در گام اول بیایید دو مبحث Strategic Design و Tactical Design رو بحث کنیم و نگاهشون رو بدونیم.نگاه Strategic DesignStrategic Design در DDD به لایه‌ای از طراحی اشاره داره که تمرکز اون بر درک کلان دامنه کسب‌وکار و تعیین مرزهای مفهومی سیستم است. در این سطح، هدف اصلی پاسخ به این پرسش‌هاست که «سیستم ما از چه بخش‌هایی تشکیل شده است؟»، «هر بخش چه مسئولیتی دارد؟» و «کدام مفاهیم نباید با یکدیگر درهم‌آمیخته شوند؟». Strategic Design به معماران نرم‌افزار و تحلیل‌گران کمک میکنه تا پیش از ورود به جزئیات فنی و پیاده‌سازی، ساختاری شفاف و هم‌راستا با واقعیت‌های بیزینسی ایجاد کنند. مفاهیمی مانند مرزبندی دامنه‌ها، جلوگیری از وابستگی‌های ناخواسته و هم‌راستاسازی تیم فنی و بیزینس، همگی در این سطح مورد توجه قرار میگیره و نقش حیاتی در کاهش پیچیدگی و افزایش پایداری سیستم در بلندمدت دارد.نگاه Tactical DesignTactical Design بخش اجرایی و پیاده‌سازی‌محور DDD هست که تمرکز آن بر مدل‌سازی دقیق مفاهیم دامنه در قالب کد می‌باشد. در این سطح، تلاش میشه ساختار کلاس‌ها، اشیاء و سرویس‌ها به‌گونه‌ای طراحی شوند که بازتابی مستقیم از منطق بیزینسی باشند. Tactical Design به برنامه‌نویس ها و قبل از اون معمار ها، کمک می‌کند تا منطق دامنه را از لایه‌های زیرساختی و فنی جدا کرده و کدی بنویسند که خوانا، قابل نگهداری و منطبق بر زبان مشترک تیم باشد. مفاهیمی مانند Entity، Value Object، Aggregate و Serviceها در این لایه تعریف می‌شوند و هسته اصلی مدل دامنه را شکل می‌دهند.حالا بریم سراغ کلید واژه هایی که داخل این مفاهیم وجود دارند:EntityEntity در DDD به مفهومی اطلاق می‌شود که دارای هویت یکتاست و این هویت، مستقل از وضعیت یا ویژگی‌های فعلی آن، عامل تمایز آن محسوب می‌شود. یک Entity ممکن است در طول چرخه عمر خود تغییرات متعددی را تجربه کند، اما تا زمانی که هویت آن ثابت است، همچنان همان موجودیت تلقی می‌شود. از دیدگاه بیزینسی، Entity معمولاً نماینده مفاهیمی است که پیگیری، ردیابی و تاریخچه آن‌ها اهمیت دارد، مانند کاربر، سفارش یا قرارداد. در طراحی دامنه، تمرکز اصلی Entity نه بر داده‌های آن، بلکه بر رفتارها و قوانین بیزینسی مرتبط با آن است.Value ObjectValue Object نمایانگر مفاهیمی در دامنه است که هویت مستقل ندارند و تنها بر اساس مقدارشان تعریف می‌شوند. این اشیاء معمولاً immutable هستند؛ به این معنا که پس از ایجاد، تغییر نمی‌کنند و هرگونه تغییر منجر به ایجاد یک نمونه جدید می‌شود. Value Objectها به ساده‌سازی مدل دامنه کمک می‌کنند و باعث می‌شوند منطق بیزینسی به‌صورت شفاف‌تر و دقیق‌تر بیان شود. مفاهیمی مانند آدرس، مبلغ پول یا بازه زمانی نمونه‌هایی رایج از Value Object هستند که در آن‌ها برابری مفهومی اهمیت دارد، نه هویت.AggregateAggregate مفهومی است که برای گروه‌بندی چند Entity و Value Object مرتبط با یکدیگر تحت یک مرز مشخص استفاده می‌شود. هدف اصلی Aggregate حفظ یکپارچگی و سازگاری قوانین بیزینسی درون این مرز است. در DDD، هر Aggregate به‌عنوان یک واحد تراکنشی در نظر گرفته می‌شود و تغییرات باید به‌گونه‌ای اعمال شوند که قوانین دامنه در هر لحظه نقض نشوند. تعریف صحیح Aggregate نقش مهمی در کنترل پیچیدگی سیستم و جلوگیری از وابستگی‌های بیش از حد بین بخش‌های مختلف مدل دامنه دارد.Aggregate RootAggregate Root موجودیتی است که به‌عنوان نقطه ورود و کنترل‌کننده یک Aggregate عمل می‌کند. تمام تعاملات خارجی با اجزای داخلی Aggregate باید از طریق Aggregate Root انجام شوند. این مفهوم تضمین می‌کند که قوانین بیزینسی و قیود دامنه به‌درستی اعمال شده و از دسترسی مستقیم و ناامن به اجزای داخلی جلوگیری شود. Aggregate Root مسئول حفظ انسجام و صحت داده‌ها در محدوده Aggregate است و نقش کلیدی در طراحی مدل دامنه ایفا می‌کند.DomainDomain در DDD به حوزه مسئله‌ای اشاره دارد که نرم‌افزار برای حل آن طراحی می‌شود. Domain در واقع بازتابی از واقعیت‌های کسب‌وکار، قوانین، محدودیت‌ها و فرآیندهایی است که سازمان با آن‌ها سروکار دارد. درک صحیح Domain پیش‌نیاز اصلی موفقیت در DDD است، زیرا تمام تصمیمات معماری و طراحی باید بر اساس آن اتخاذ شوند. Domain نه یک مفهوم فنی، بلکه یک مفهوم بیزینسی است و باید از طریق تعامل مستمر با متخصصان کسب‌وکار (Domain Experts) به‌درستی شناسایی و مدل‌سازی شود. حالا در ادامه این بحث، موضوعات Subdomain ، موضوع Core Subdomain ، موضوع Supporting Subdomain و چیز های دیگری وجود داره که من در این مقاله نمیخوام واردش بشم و اگر کسی خیلی نیاز داره عمیق بشه، کتابی که در اول مقاله گذاشتم رو بخونه.Ubiquitous LanguageUbiquitous Language زبان مشترکی است که میان تیم فنی و بیزینس برای توصیف Domain استفاده می‌شود. این زبان باید در مکالمات روزمره، مستندات و حتی در کد منبع به‌صورت یکسان به کار رود. هدف Ubiquitous Language حذف سوءتفاهم‌ها و ایجاد هم‌راستایی ذهنی بین همه ذی‌نفعان سیستم است. زمانی که کد نرم‌افزار با همان مفاهیمی نوشته می‌شود که بیزینس با آن صحبت می‌کند، فاصله بین تحلیل و پیاده‌سازی به حداقل می‌رسد.Bounded ContextBounded Context محدوده‌ای مشخص است که در آن یک مدل دامنه خاص و Ubiquitous Language مربوط به آن معتبر است. این مفهوم یکی از کلیدی‌ترین مفاهیم Strategic DDD محسوب می‌شود، زیرا از تداخل مفاهیم و مدل‌ها جلوگیری می‌کند. هر Bounded Context می‌تواند تعاریف متفاوتی از یک مفهوم مشابه داشته باشد و این تفاوت‌ها کاملاً قابل قبول هستند، به شرط آنکه مرزها شفاف باشند. Bounded Context مبنای طراحی معماری ماژولار و سیستم‌های توزیع‌شده است.Context MapContext Map ابزاری مفهومی برای نمایش ارتباط بین Bounded Contextها است. این نقشه نشان می‌دهد که Contextهای مختلف چگونه با یکدیگر تعامل دارند و چه نوع وابستگی‌هایی میان آن‌ها وجود دارد. Context Map به معماران کمک می‌کند تا روابط پیچیده بین تیم‌ها و سیستم‌ها را مدیریت کرده و از ایجاد coupling ناخواسته جلوگیری کنند. این مفهوم نقش مهمی در طراحی سیستم‌های بزرگ و سازمانی ایفا می‌کند.RepositoryRepository یک الگوی طراحی در Tactical DDD است که مسئول مدیریت چرخه حیات Aggregateها می‌باشد. Repository به‌عنوان یک واسط بین Domain Model و لایه ذخیره‌سازی عمل می‌کند و جزئیات فنی مربوط به پایگاه داده را از منطق دامنه پنهان می‌سازد. استفاده از Repository باعث می‌شود Domain Model مستقل از فناوری ذخیره‌سازی باقی بماند و تمرکز آن بر منطق بیزینسی حفظ شود.Domain EventDomain Event بیانگر رخدادی معنادار در دامنه کسب‌وکار است که وقوع آن برای سایر بخش‌های سیستم اهمیت دارد. این رویدادها معمولاً نشان‌دهنده تغییر وضعیت یا انجام یک عمل مهم بیزینسی هستند و به سیستم اجازه می‌دهند به‌صورت واکنش‌محور طراحی شود. استفاده از Domain Eventها به کاهش coupling بین اجزای مختلف سیستم کمک کرده و امکان توسعه‌پذیری و انعطاف‌پذیری بالاتری را فراهم می‌کند. این مفهوم پلی میان منطق دامنه و نیازهای ارتباطی سیستم ایجاد می‌کند.Domain ServiceDomain Service زمانی مورد استفاده قرار می‌گیرد که منطق بیزینسی مشخصی وجود دارد که به‌طور طبیعی متعلق به هیچ Entity یا Value Object خاصی نیست. این سرویس‌ها بخشی از لایه دامنه هستند و شامل منطق بیزینسی خالص می‌شوند، نه منطق فنی یا زیرساختی. Domain Serviceها به مدل دامنه کمک می‌کنند تا از شلوغ شدن Entityها جلوگیری شود و مسئولیت‌ها به‌صورت شفاف و منطقی توزیع شوند.Application ServiceApplication Service نقش هماهنگ‌کننده بین لایه‌های بیرونی سیستم و مدل دامنه را بر عهده دارد. این سرویس‌ها مسئول اجرای Use Caseها هستند و معمولاً شامل منطق بیزینسی پیچیده نمی‌شوند. وظایفی مانند مدیریت تراکنش‌ها، فراخوانی سرویس‌های دامنه، ارسال و دریافت داده از لایه ارائه و کنترل جریان اجرای عملیات، در این لایه انجام می‌شود. Application Service مرزی شفاف بین دنیای فنی و منطق دامنه ایجاد می‌کند و به حفظ تمیزی معماری کمک می‌نماید.خب، حالا که این ها رو بررسی کردیم، میخوام بگم در عالم فنی و کد نویسی، ما بیشتر با مفاهیمی که میگم سر و کار خواهیم داشت:Entity هاValue Object هاAggregate هاAggregate Root هاDomain Event هاDomain Service هاApplication Service هادر مقاله های بعدی، میریم به سراغ اینکه، در معماری های میکروسرویس و حتی معماری های غیر میکروسرویسی، چطور میتونیم مفاهیم DDD رو وارد پروژه کرده و ازش استفاده کنیم.</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Sat, 24 Jan 2026 13:36:33 +0330</pubDate>
            </item>
                    <item>
                <title>Page؛ جایی که دیتابیس واقعاً نفس می‌کشد</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/page-%D8%AC%D8%A7%DB%8C%DB%8C-%DA%A9%D9%87-%D8%AF%DB%8C%D8%AA%D8%A7%D8%A8%DB%8C%D8%B3-%D9%88%D8%A7%D9%82%D8%B9%D8%A7%D9%8B-%D9%86%D9%81%D8%B3-%D9%85%DB%8C-%DA%A9%D8%B4%D8%AF-sg9rkjcy50o9</link>
                <description>سلام؛ دوستان میخوام یک مقدمه خیلی جزئی از بحث Page ها در دیتابیس رو مورد بررسی قرار بدیم و ببینیم که پشت پرده Read و Write که روی دیسک میشه، چه خبره.بریم سراغ Pageوقتی از دیتابیس حرف می‌زنیم، معمولاً ذهن‌مان می‌رود سمت جدول‌ها، سطرها و کوئری‌ها. انگار دیتابیس یک فایل اکسل بزرگ است که فقط خیلی سریع‌ تر و جذاب تر کار می‌کند. اما واقعیت این است که دیتابیس‌ها خیلی پایین‌تر از این لایه فکر می‌کنند.آن‌ها نه «سطر» می‌شناسند و نه «جدول»؛ چیزی که برایشان معنا دارد، Page است. اگر بخواهیم صادق باشیم، Page همان جایی است که دیتابیس واقعاً زندگی می‌کند. دیتابیس‌ها از ابتدا با یک چالش بزرگ روبه‌رو بوده‌اند: دیسک. دسترسی به دیسک، حتی در بهترین SSDها، در مقایسه با حافظه اصلی بسیار کند است.اگر قرار بود دیتابیس برای خواندن هر رکورد یا حتی هر جدول، مستقیم به دیسک مراجعه کند، عملاً هیچ سیستم جدی‌ای نمی‌توانست روی آن حساب کند.همین‌جا بود که مفهوم Page به‌وجود آمد؛ یک واحد میانی، نه خیلی بزرگ و نه خیلی کوچک، که دیتابیس بتواند داده‌ها را در قالب آن جابه‌جا کند. Page در ساده‌ترین تعریف، کوچک‌ترین واحدی است که دیتابیس حاضر است آن را از دیسک بخواند یا روی دیسک بنویسد.این نکته خیلی مهم است، چون یعنی دیتابیس هیچ‌وقت نمی‌گوید «این یک Row را بده». همیشه می‌گوید «Page مربوط به این Row را بده». حتی اگر فقط یک ستون از یک سطر را بخواهید، دیتابیس مجبور است کل Page را وارد حافظه کند.اجزا منطقی Data Pageاین تصمیم شاید در نگاه اول عجیب به نظر برسد، اما دقیقاً همان چیزی است که دیتابیس را سریع و قابل‌ اعتماد کرده است. وقتی یک Query اجرا می‌شود، اولین کاری که دیتابیس انجام می‌دهد این نیست که دنبال داده بگردد؛ اول دنبال Page می‌گردد. بررسی می‌کند که آیا Page موردنظر قبلاً در حافظه هست یا نه. اگر باشد، عملاً به دیسک کاری ندارد و پاسخ خیلی سریع برمی‌گردد. اگر نباشد، باید Page را از دیسک بخواند و وارد حافظه کند.بریم سراغ Buffer Pool یا Shared Buffersاینجاست که اهمیت چیزی به نام Buffer Pool یا Shared Buffers مشخص می‌شود؛ جایی که دیتابیس Pageها را نگه می‌دارد تا مجبور نباشد هر بار به دیسک مراجعه کند. نکته‌ای که معمولاً در آموزش‌های سطحی دیتابیس گفته نمی‌شود ولی میخوام در این بخش اشاره کنم این است که Page فقط شامل داده‌های جدول نیست. خود دیتابیس هم اطلاعات کنترلی، متادیتا، ایندکس‌ها و حتی وضعیت فضای خالی را در قالب Page ذخیره می‌کند. یعنی از نگاه دیتابیس، همه‌چیز Page است. جدول، ایندکس و حتی لاگ‌ها، همگی روی همین مفهوم سوار شده‌اند.به همین خاطر است که وقتی می‌گوییم «دیتابیس کند شده»، در واقع داریم درباره رفتار Pageها صحبت می‌کنیم، نه صرفاً کوئری‌ها. ارتباط بین Row و Page هم یکی از آن جاهایی است که اگر درکش نکنیم، خیلی از رفتارهای عجیب دیتابیس برایمان بی‌منطق به نظر می‌رسد. Rowها داخل Pageها جا می‌گیرند و هر Page می‌تواند چندین Row را در خودش نگه دارد.حالا اگر Rowهای ما بزرگ باشند، مثلاً ستون‌های زیاد یا فیلدهای متنی حجیم داشته باشند، طبیعتاً تعداد کمتری Row در هر Page جا می‌شود. نتیجه‌اش این است که برای خواندن همان تعداد داده، دیتابیس مجبور می‌شود Pageهای بیشتری را از دیسک یا حافظه عبور دهد. این یعنی هزینه بیشتر، هم از نظر I/O و هم از نظر مصرف حافظه. ایندکس‌ها هم دقیقاً از همین منطق پیروی می‌کنند. وقتی از B-Tree یا ساختارهای مشابه استفاده می‌کنیم، هر گره درخت در واقع یک Page است. حرکت کردن در ایندکس، یعنی پریدن از یک Page به Page دیگر. به همین دلیل است که عمق ایندکس، میزان Fragmentation و حتی ترتیب درج داده‌ها می‌تواند تأثیر مستقیم روی سرعت کوئری داشته باشد. دیتابیس در حال دنبال کردن اشاره‌گرهای Page است، نه سطرهای انتزاعی که ما در سطح SQL می‌بینیم.وقتی زمان میگذره و دیتاها زیاد میشه ...با گذشت زمان و با عملیات مداوم Insert، Update و Delete، پیج ها کم‌کم نظم اولیه‌شان را از دست می‌دهند. بعضی Pageها نصفه‌پر می‌شوند، بعضی جاها داده‌ها پراکنده می‌شوند و دیتابیس برای رسیدن به یک نتیجه ساده مجبور می‌شود Pageهای بیشتری را بررسی کند. این همان چیزی است که به آن Fragmentation می‌گوییم؛ مشکلی که روی کاغذ ساده به نظر می‌رسد، اما در عمل می‌تواند یک سیستم سالم را به یک سیستم کند و پرهزینه تبدیل کند. شاید در نهایت مهم‌ترین نکته این باشد که Page به ما یاد می‌دهد دیتابیس چطور فکر می‌کند. دیتابیس‌ها موجودات منطقی هستند که با محدودیت‌های سخت‌افزار کنار آمده‌اند. این عزیزان به جای اینکه دنبال ایده‌آل‌ترین حالت مفهومی باشند، دنبال کم‌هزینه‌ترین جابه‌جایی داده هستند. وقتی این را بفهمیم، خیلی از تصمیم‌هایمان در طراحی دیتابیس تغییر می‌کند؛ از انتخاب نوع ستون‌ها گرفته تا نحوه ایندکس‌گذاری و حتی شکل نوشتن Queryها. اگر بخواهیم جمع‌بندی کنیم، Page همان لایه‌ای است که دنیای انتزاعی SQL را به دنیای واقعی سخت‌افزار وصل می‌کند. جایی بین حافظه و دیسک، جایی که تصمیم‌های کوچک می‌توانند تأثیرهای بزرگ بگذارند. تا وقتی Page را نفهمیم، دیتابیس را فقط از روی ظاهرش قضاوت کرده‌ایم؛ اما وقتی وارد دنیای Pageها می‌شویم، تازه می‌فهمیم چرا دیتابیس‌ها این‌قدر پیچیده، و در عین حال این‌قدر هوشمند طراحی شده‌اند.اگر فرصت کنم و خدا بخواد، در مقاله بعدی اول در مورد ساختار فنی Page ها و قسمت های تشکیل دهنده اون رو مورد بررسی قرار میدیم. بعد اگر فرصت شد میریم سراغ Page های BCM و DCM و سپس در مورد Concurrency و Page locking میخوام باهم صحبت کنیم و بررسی کنیم و درخواست ها به صورت هم زمان میریزه سر دیتابیس، چه اتفاقاتی رخ میده. حتما میخوام در مورد Page های GAM و SGAM هم یه صحبتی داشته باشیم و اینکه PFS چه نقشی در این وسط داره بازی میکنه. معماری زیرین رو در کل بررسی خواهیم کرد انشاا...</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Mon, 05 Jan 2026 00:01:00 +0330</pubDate>
            </item>
                    <item>
                <title>آقای Edgar F.Codd | پدر پایگاه‌های داده رابطه‌ای</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/%D8%A2%D9%82%D8%A7%DB%8C-edgar-fcodd-%D9%BE%D8%AF%D8%B1-%D9%BE%D8%A7%DB%8C%DA%AF%D8%A7%D9%87-%D9%87%D8%A7%DB%8C-%D8%AF%D8%A7%D8%AF%D9%87-%D8%B1%D8%A7%D8%A8%D8%B7%D9%87-%D8%A7%DB%8C-bwqgxyb0kiev</link>
                <description>وقتی امروز در مورد «دیتابیس» حرف می‌زنیم،‌ شاید کمتر به ذهن‌مان برسد که پشت این فناوری به‌ ظاهر ساده، یک ذهن ریاضیاتی خارق‌العاده قرار دارد که حدود نیم‌قرن پیش مسیر علم داده و توسعه نرم‌افزار را برای همیشه تغییر داد. Edgar Frank Codd، مردی که اکثر متخصصان کامپیوتر او را «پدر پایگاه‌های داده رابطه‌ای» می‌نامند، نه‌ فقط یک محقق، بلکه پلی بود بین نظریه‌های ریاضی و نیازهای واقعی دنیای فناوری. یکی از استوره های علمی که من همیشه دوست داشتم از منظر تفکری دنباله روی مسیر ایشون در حوزه دیتابیس و پایگاه داده باشم.امروز میخوام کمی درباره ایشون صحبت کنم و کارهایی که در زمینه دیتابیس و پایگاه داده انجام داده رو مورد بررسی قرار بدیم. این مقاله احتمالاً برای همه مهندسان جذاب نباشه و قشر خاصی از معماران پایگاه داده، مدیران پایگاه داده و برنامه نویسانی که به نحوی درگیر هستند با دیتابیس و موضوعات اون رو دربر بگیره. اطلاعات ایشون در IBM موجود هست و برای شخص خودم که همیشه معماری های پشت پرده دیتابیس ها و ساختار هایی که وجود داره از اهمیت ویژه ای برخورد بوده، شناخت افرادی که زندگی خودشون رو در این مسیر گذاشتند، یک الزام بوده برام و هر کلمه ای که از این افراد یاد گرفتم، برام ارزشمند بوده. آقای Codd در سال 2003 در حدود 79 سالگی از دنیا رفتند و ما یکی از پایه گذاران معماری و در اصل پدر پایگاه های داده رابطه ای رو از دست دادیم.آقای Edgar F. Coddاز انگلیس تا دنیای کامپیوترEdgar Frank Codd در ۱۹ آگوست ۱۹۲۳ در جزیره‌ «Portland» در سواحل جنوبی انگلستان به دنیا آمد. او تحصیل در ریاضیات و شیمی را در دانشگاه آکسفورد آغاز کرد، اما با شروع جنگ جهانی دوم راهی خدمت در نیروی هوایی سلطنتی بریتانیا (RAF) شد. پس از جنگ، در سال ۱۹۴۸ به ایالات متحده مهاجرت کرد و خیلی زود راهش به گروه‌های تحقیقاتی IBM باز شد.تحصیلات تکمیلی و ورود به دنیای نظریه داده‌هادر دهه‌ی ۱۹۶۰، Codd به دانشگاه میشیگان رفت و دکترای علوم کامپیوتر گرفت؛ رشته‌ای که آن زمان هنوز در بسیاری از دانشگاه‌ها وجود نداشت. پایان‌نامه‌اش درباره اتوما‌تای سلولی (cellular automata) بود. شاخه‌ای از علوم نظری که بازتاب‌دهنده ذهن ریاضیاتی عمیق او است. دو سال بعد از دریافت دکترا، او به آزمایشگاه تحقیقات IBM در سان‌خوزه، کالیفرنیا منتقل شد و دقیقاً همانجا بود که یکی از مهم‌ترین ایده‌های تاریخ کامپیوتر شکل گرفت.انقلاب رابطه‌ها - تولد مدل Relational Databaseتا اواخر دهه‌ی ۱۹۶۰، پایگاه‌های داده به شکل‌های پیچیده و سخت‌فهم ذخیره و بازیابی می‌شدند. معماری‌های سلسله‌مراتبی و شبکه‌ای، برای دستیابی به داده‌ها نیاز به برنامه‌نویسان خبره داشتند و عملاً استفاده برای کاربران عادی دشوار بود. در سال ۱۹۷۰، Codd مقاله‌ای منتشر کرد با عنوان «A Relational Model of Data for Large Shared Data Banks» که در آن پیشنهاد داد داده‌ها باید به‌صورت جدول‌هایی مرتب ذخیره شوند. جایی که روابط بین داده‌ها با مشخصه‌های مشترک (کلیدها) تعریف می‌شود، نه با آدرس‌های فیزیکی روی حافظه. من این مقاله رو خوندم و مطمن هستم برای هر معمار پایگاه داده و مدیر دیتابیسی که پایه های موضوعات فنی دیتابیس براش مهم هست، ارزشمند و جذاب خواهد بود. دانشگاه پنسیلوانیا این رو داره و میتونید از این لینک بخشی از مقاله رو بخونید. این ایده انقلابی بود. به‌جای اینکه کاربر بداند دیتای شما کجا و چگونه ذخیره شده، فقط بتواند مفهوم داده‌ها را بفهمد و با یک زبان پرس و جو (query) ساده پاسخ بگیرد. دوستان خواهش میکنم اینجا دقت کافی رو داشته باشید. ایشون ایده کلی رو مطرح کردند و هنوز در این بازه، چیزی وجود ندارد.تصویری ساده از نحوه کار یک سیستم پایگاه دادهسیستم‌ها و زبان SQLهرچند مفهوم رابطه‌ای ابداع Codd بود، اما اولین دیتابیس‌های تجاری مبتنی بر آن چند سال طول کشید تا پدید بیایند. تیمی در IBM روی پروژه‌ای به نام System R کار کردند و در همین مسیر بود که SQL یعنی محبوب‌ترین زبان پرس و جوی داده در تاریخ شکل گرفت. بچه ها همیشه از من میپرسند آقا این SQL از کجا اومده و چه کسانی روش کار کردند. سوال خیلی از عزیزان هست. ریشه SQL همین جاست. رقبا نیز از این ایده استقبال کردند. شرکت‌هایی مثل Oracle (که بعداً یکی از ارکان صنعت دیتابیس شد) توانستند این ایده را زودتر به بازار بیاورند و از آن سود ببرند.تصویری از Edgar F. Coddمفاهیم کلیدی - نرمال‌سازی و اصول رابطه‌ایCodd فراتر از ایده جدول‌ها رفت. ایشون قوانین ریاضی پشت پایگاه‌های داده را هم تعریف کرد، مثل فرم‌های نرمال‌سازی داده‌ها که به طراحی صحیح دیتابیس کمک می‌کنند. یکی از معروف‌ترین آن‌ها، Boyce–Codd Normal Form (BCNF) است که نام او را نیز یدک می‌کشد. همیشه به بچه های دیتابیسی میگم که مفاهیمی که امروزه داریم روش کار میکنیم و در کار های روز مره ازشون استفاده میکنیم مثل موضوعات نرمال سازی و سطوح آن که به 1NF، 2NF، 3NF، 4NF، 5NF و موضوعات که ذکر شد مثل BCNF و حتی PJ/NF ریشه شون اینجاست. ایشون همچنین مجموعه‌ای از ۱۲ قاعده تعریف کرد که مشخص می‌کند یک سیستم مدیریت دیتابیس واقعاً رابطه‌ای است یا فقط اسمش را یدک می‌کشد. اگر اطلاعاتی راجع به این موضوع ندارید میتونید با یک سرچ ساده یا مراجعه به این لینک، اطلاعات لازم رو کسب کنید.اگر میخواهید اطلاعات رو از زبان خود IBM بخونید، بهتره به این لینک هم سری بزنید.</description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Fri, 26 Dec 2025 18:40:11 +0330</pubDate>
            </item>
                    <item>
                <title>تئوری CAP؛ سه‌گانه‌ی دنیای دیتابیس‌ها</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/%D8%AA%D8%A6%D9%88%D8%B1%DB%8C-cap-%D8%B3%D9%87-%DA%AF%D8%A7%D9%86%D9%87-%DB%8C-%D8%AF%D9%86%DB%8C%D8%A7%DB%8C-%D8%AF%DB%8C%D8%AA%D8%A7%D8%A8%DB%8C%D8%B3-%D9%87%D8%A7-k1yvke81yzas</link>
                <description>اگر با دیتابیس‌های توزیع‌شده، میکروسرویس‌ها یا حتی سیستم‌های Cloud-based سروکار داشته باشید، احتمالاً اسم CAP Theorem رو شنیده باشید. معمولاً هم در حد یه جمله که:  «نمی‌تونی Consistency، Availability و Partition Tolerance رو با هم داشته باشی»  ولی واقعیت این هست که CAP خیلی عمیق‌تر از این جمله‌ کلیشه‌ای است. تو این مقاله می‌خوایم از صفر تا صد CAP رو باز کنیم و موضوع رو دقیق بررسی کنیم ببینیم که اصل قضیه چی هست و مدیر پایگاه داده ما چه اصولی رو باید بلد باشه. مثلث CAPقبل از ورود به موضوعات فنی، باید بررسی کنیم که CAP اصلاً چرا به وجود اومد؟قبل از CAP، بیشتر دیتابیس‌ ها متمرکز (Centralized) بودند. به این معنا که ما یک سرور داشتیم و روی اون سرور یک زیرساخت پایگاه داده که دیتابیس روی اون بود. وقتی سیستم‌ ها بزرگ شدن، چند تا اتفاق بسیار مهم رخ داد:○ دیتابیس‌ ها روی چند سرور پخش شدن ○ دیتاسنتر ها جغرافیایی شدن○ latency و network failure واقعی شدنخب، الان که فهمیدیم CAP چرا به وجود اومده، میریم به سراغ تعریف و  اینکه هر حرف از چه کلمه ای میاد.CAP می‌گه در یک سیستم دیتابیس توزیع‌ شده، فقط می‌تونی دو تا از این سه ویژگی رو هم‌ زمان تضمین کنی: ○ Consistency (سازگاری)○ Availability (دسترس‌پذیری)○ Partition Tolerance (تحمل پارتیشن شبکه)Consistency یعنی همه یک چیز ببینن! کمی بریم جلو توضیح بدم یعنی چی. Consistency (سازگاری) یعنی:  هر درخواست Read، آخرین Write معتبر رو برگردونه و این یعنی چی؟بیایید فرض کنیم که کاربر A موجودی حساب رو از ۱۰۰ به ۲۰۰ تغییر می‌ده کاربر B بلافاصله بعدش Read می‌زنه  Consistency یعنی کاربر B حتماً ۲۰۰ رو ببینه، نه ۱۰۰  نکته‌ی مهمی که وجود داره عبارت است از اینکه، این Consistency با ACID Consistency فرق داره و نباید اشتباه بگیریم. اینجا حرف از Single Source of Truth توی کل نودهاست. بزارید بهتر بگم، تا وقتی کلیه نود ها از Write ی که انجام شده، مطمئن نشده اند و عملیات همگام سازی کلیه نود ها انجام نشده باشه، نباید (منظورم از کلمه نباید این هست که باید حداکثر سعی خودمون رو بکنیم) نتیجه Write در عملیات Read برگرده. من هر چقدر از اهمیت درک این قسمت بگم، کم گفتم. نباید طوری باشه که وقتی چند نفر هم زمان درخواست میدند، اطلاعات متفاوتی رو دریافت کنند.Availability یعنی سیستم همیشه جواب بدهAvailability (دسترس پذیری) یعنی:  هر درخواست، یه پاسخ بگیره (حتی اگه پاسخ کامل یا به‌روز نباشه)  یعنی:  سیستم Down نشه و  Timeout نده  حتی اگه دیتای قدیمی برمی‌گردونه، حداقل جواب بده.این موضوع برای سیستم‌ هایی مثل شبکه‌ های اجتماعی یا سیستم‌ های Recommendation ها یا سرویس هایی که باید دسترس پذیری بالایی داشته باشند، خیلی مهم هست.بهتره این طور بگم که Availability خیلی مهم‌تر از Consistency لحظه‌ایه.Partition Tolerance یعنی وقتی شبکه خراب می‌شه چی؟Partition یعنی ارتباط بین نود ها قطع یا ناپایدار بشه و Partition Tolerance یعنی سیستم حتی در صورت قطع ارتباط بین نودها، همچنان کار کنه.حقیقت این هست که تو دنیای واقعی Network Failure اجتناب‌ ناپذیر هست.  پس عملاً  هر سیستم توزیع‌ شده‌ی واقعی باید P رو داشته باشه.بسیار خب. الان که تعریف و مفهوم هر سه موضوع رو فهمیدیم، بریم سراغ اینکه چرا نمیتونیم هر سه گزینه رو در یک حالت داشته باشیم.بیایید فرض کنیم شبکه بین دو نود قطع شده (Partition اتفاق افتاده).  حالا دو تا انتخاب داریم:حالت اول این هست که Consistency رو نگه داریم. یعنی چی؟ یعنی اینکه اگر نودها مطمئن نباشن دیتا یکیه اونموقع درخواست رو Reject می‌کنن و نتیجه این میشه که Availability رو از دست میدیم.حالت دوم این هست که Availability رو نگه داریم و هر نود هر چی داره جواب میده و نتیجه این میشه که Consistency رو از دست میدیم.اینجاست که CAP خودش رو نشون می‌ده و ما باید در زمان Partition، باید بین C و A یکی رو انتخاب کنیم.خب حالا بریم سراغ چند تا واژه مهم که دیتابیس کارهامون باید این ها رو بدونند:اینکه ما دیتابیس ها رو از منظر CAP به چند مدل طبقه بندی میکنیم:دیتابیس CP Databases (Consistency + Partition Tolerance)دیتابیس AP Databases (Availability + Partition Tolerance)دیتابیس CA Databases (Consistency + Availability)این حالت سوم در اصل وجود نداره. چرا؟ چون CA یعنی  Consistency + Availability  بدون Partition Tolerance و این فقط تو سیستم‌ های غیرتوزیع‌شده یا Single Node ممکنه.خب، حالا موضوع CAP رو در دنیای واقعی برای DBA هامون هم بررسی کنیمبرای یه DBA، مفهوم CAP یعنی اینکه بدونی دیتابیسی که انتخاب می‌کنی چه رفتاری تو بحران داره  بدونی Failover چطوری کار می‌کنه و بدونی Read و Write تحت Partition چی می‌شن.سؤال درست این نیست که کدوم دیتابیس بهتره؟سؤال درست این هستش که سیستم من تو شرایط خرابی باید چطوری رفتار کنه؟طراح دیتابیس ما باید بدونه Eventually Consistency یعنی چی واقعاً؟ اینکه سیستم قول می‌ده که اگه Write جدیدی نیاد، بالاخره همه نودها همگام می‌شن. ببینید این ها موضوعات مهمی هستند که نیاز هست بدونیم.امیدوارم کمک کننده بوده باشه ... </description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Thu, 18 Dec 2025 05:45:05 +0330</pubDate>
            </item>
                    <item>
                <title>دیتا ساینتیست ها چه کاری میکنند؟</title>
                <link>https://virgool.io/@s.mojtaba-hashemi/%D8%AF%DB%8C%D8%AA%D8%A7-%D8%B3%D8%A7%DB%8C%D9%86%D8%AA%DB%8C%D8%B3%D8%AA-%D9%87%D8%A7-%DA%86%D9%87-%DA%A9%D8%A7%D8%B1%DB%8C-%D9%85%DB%8C%DA%A9%D9%86%D9%86%D8%AF-eiwa2psfkc9e</link>
                <description>Data Scientist یا به اصطلاح کسی که داره روی داده ها کار میکنه چه کسی هست و چه عملیاتی رو باید در شرکت ها انجام بده؟ اساساً این پوزیشن چه مسئولیت هایی داره و با چه استک و ابزارهایی باید کار کنه؟ این مقاله رو با همکاری دیتاساینتیست شرکت خودمون نوشتم که چندین سال هست داریم باهم کار میکنیم و قرار هست که در اینجا تجربیات به اشتراک گذاشته بشه. دیتاساینتیست ها در حال آنالیزیک جریان کلی از کار هایی که دیتاسانتیست در شرکت ما انجام میده رو باهم بررسی کنیم بعد بریم سراغ موضوعات دیگه. ما در شرکت خودمون حدود ده سالی هست که چند تا استارت اپ با اسکیل کوچیک یا متوسط و چند تا پروژه ثابت اصلی داریم که این ها داده های خیلی زیادی رو تولید میکنند. ما این داده ها رو از زیرساخت های دیتابیس خودمون استخراج میکنیم و در یکسری شیت های اکسلی مرتبشون کرده و به عنوان داده های خام میدیم به دیتاساینتیستمون. ایشون در اولین مرحله که این داده ها رو از تیم دیتابیس ما گرفت، میاد و این ها رو بر اساس یکسری اصول اولیه با متد هایی که خودش داره مرتب سازی میکنه و وضعیت سطر و ستون ها و داده های اصلی و داده های computed رو درست میکنه و خروجی میشه مثلا یک فایل اکسل تر تمیز. در مرحله بعدی میاد و این داده ها رو به عنوان ورودی میده به داشبورد های کاریشون (یکسری داشبورد هایی دارند اینها که بعداً صحبت میکنیم راجبش) و این داشبورد بر اساس یکسری شاخص ها و تنظیمات و فرمول ها، میاد و اون داده های خام رو visualize (بصری سازی) میکنه و بر اساس چارت های گرافیکی دینامیک نشون میده. در مرحله بعدی، این دیتاساینتیست ما میاد و این داده های به وجود اومده رو بررسی میکنه و یکسری الگوهایی که احتمالاً بدون دقت دیده نمیشوند رو پیدا میکنند و اون رو به گزارش تبدیل میکنند. خودشون بهش الگو های پنهان میگن و از نظر این عزیزان، الگو های پنهان خیلی میتونه برای بیزینس اون سیستم اهمیت داشته باشه. در نهایت، این گزارشات میاد میرسه به دست ما و با مطالعه این گزارشات میفهمیم که باید چه تصمیمات و چه کارهایی رو انجام بدیم.حالا بریم سراغ موضوعات تکنیکال و ابزار هایی که این دوستان باید داشته باشند و کار کردن باهاش رو بلد باشند. آشنایی با دیتابیس ها و ساختار های دیتابیس آشنایی با مفاهیم مدل سازی داده (هم داده منطقی و هم داده فیزیکی)آشنایی با مباحث داده کاوی (Data Mining)آشنایی با ابزار ها و داشبورد های visualization همانند Tableau یا Power BI یا Grafanaآشنایی کامل با SQLآشنایی با پایتون و کتابخونه هایی که خیلی لازمشون میشه مثل پای چارتآشنایی با الگوریتم های دیتاسانتیستی مثل درخت های تصمیم، SVM و رگرسیون ها و ... آشنایی با تکنیک های Segmentation آشنایی با Machine Learning و پیاده سازی مدل های مبتنی بر اون ها این ها کارهایی بود که در خود شرکت ما اتفاق میوفته و جالب هست که فقط دو نفر (خودم به عنوان برنامه نویس و دیتاآنالیستمون هم به عنوان متخصص این حوزه) تقریبا موضوعات رو جمع میکنیم و به نتیجه میرسونیم. این جریان برای پروژه هایی که دیتای زیاد روزانه تولید میکنند اتفاق میوفته و برای تصمیم هایی که باید گرفته بشه، دارای اهمیت ویژه ای هست. طبیعتاً شرکت های خیلی بزرگ و مهمی وجود دارند که مسئولیت این عزیزان در اون شرکت ها خیلی سنگین تر و مهم تر و حتی پیچیده تر هست اما بیس کار همین موضوعاتی که عرض کردم این ها هستند. موفق و پیروز باشید. </description>
                <category>میر مجتبی هاشمی جنتی</category>
                <author>میر مجتبی هاشمی جنتی</author>
                <pubDate>Wed, 10 Dec 2025 11:53:27 +0330</pubDate>
            </item>
            </channel>
</rss>