<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های Mohsen Farokhi - محسن فرخی</title>
        <link>https://virgool.io/feed/@mohsen-farokhi</link>
        <description>Senior .NET Developer</description>
        <language>fa</language>
        <pubDate>2026-04-14 18:05:38</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/75105/avatar/ctSi2T.jpg?height=120&amp;width=120</url>
            <title>Mohsen Farokhi - محسن فرخی</title>
            <link>https://virgool.io/@mohsen-farokhi</link>
        </image>

                    <item>
                <title>طراحی معماری مقاوم در برابر خطاها: مفاهیم و الگوها</title>
                <link>https://virgool.io/@mohsen-farokhi/%D9%88%D8%A7%DA%A9%D9%86%D8%B4-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85-%D8%A8%D9%87-%D8%AE%D8%B7%D8%A7-%D9%88-%D9%86%D9%82%D8%B4-%D9%86%D9%88%D8%B9-%D8%A7%D8%B1%D8%AA%D8%A8%D8%A7%D8%B7-%D9%85%DB%8C%D8%A7%D9%86-%D8%A7%D8%AC%D8%B2%D8%A7-eftqhqgcybu5</link>
                <description>خطا و از کار افتادن بخشی از سیستم، یک اتفاق استثنایی نیست، بلکه بخشی از رفتار طبیعی سیستم‌های نرم‌افزاری واقعی است. بنابراین طراحی باید از ابتدا به این سؤال پاسخ دهد که سیستم در زمان بروز خطا چگونه واکنش نشان می‌دهد.خطا به‌عنوان بخشی از واقعیت سیستم‌های نرم‌افزاریدر طراحی نرم‌افزار، انتظار اینکه همهٔ اجزا همیشه درست کار کنند غیرواقعی است. خطاها، وقفه‌ها و ناهنجاری‌ها در زمان اجرا اجتناب‌ناپذیرند و باید پیشاپیش برای آن‌ها آمادگی طراحی داشت. در واقع، قابلیت پاسخ دادن مناسب به خطاها بخشی از کیفیت کلی سیستم است Medium.ایزوله‌سازی و جلوگیری از انتشار خطایکی از پایه‌ای‌ترین روش‌ها برای محدود کردن اثر خطا این است که اجزا را به‌گونه‌ای طراحی کنیم که یک خطا در یک بخش، به بخش‌های دیگر تسری نیابد. در معماری‌های توزیع‌شده (مثلاً میکروسرویس) این جداسازی به‌طور طبیعی بیشتر اتفاق می‌افتد، چرا که هر سرویس با حداقل وابستگی به سایر سرویس‌ها عمل می‌کند و خطا در یک سرویس، معمولاً عملکرد سایرین را متوقف نمی‌کند GeeksforGeeks.نقش نوع ارتباط در واکنش به خطانوع ارتباط میان اجزا تعیین می‌کند که سیستم چگونه به خطا پاسخ دهد:وابستگی لحظه‌ای (synchronous) می‌تواند باعث شود اگر یکی از اجزا پاسخ ندهد، زنجیره‌ای از خطاها در دیگر سرویس‌ها شکل بگیرد.ارتباط غیرلحظه‌ای (asynchronous) یا استفاده از صف‌ها و پیام‌رسانی باعث می‌شود قطعی در یک سرویس تأثیر مستقیم و فوری روی دیگری نگذارد و امکان حفظ عملکرد کلی سیستم بیشتر شود GeeksforGeeks.بنابراین اگر بخش‌های سیستم مستقل باشند، احتمال گسترش خطا کمتر می‌شود.الگوهای طراحی برای تحمل خطادر مهندسی نرم‌افزار، چند الگوی رایج و شناخته‌شده برای افزایش پایداری و تحمل خطا وجود دارد:🔹 الگوی «قطع‌کننده‌ی مدار» (Circuit Breaker)این الگو به‌منظور جلوگیری از خطاهای زنجیره‌ای در سیستم‌های توزیع‌شده به کار می‌رود: اگر یک سرویس پاسخ نمی‌دهد یا کند شده، Circuit Breaker جلوی فراخوانی‌های مکرر به آن سرویس را می‌گیرد و اجازه نمی‌دهد خطاها روی بخش‌های دیگر اثر بگذارند Wikipedia.🔹 «Isolation Patterns»استفاده از جداسازی محیط اجرایی (مثل کانتینرها و ماشین‌های مجازی) یا جداسازی منطقی منابع، باعث می‌شود که خطا در یک بخش به صورت محصور باقی بماند و روی بقیهٔ بخش‌ها اثر نگذارد GeeksforGeeks.🔹 افزونگی (Redundancy)مثال کلاسیک افزونگی، روش Triple Modular Redundancy (TMR) است که با اجرای چند نسخه و رأی‌گیری نتایج، سیستم را در برابر خطاهای احتمالی مقاوم می‌سازد — این روش در سخت‌افزار و نرم‌افزار کاربرد دارد Wikipedia.🔹 N-Version Programmingدر این روش چند نسخهٔ مستقل از یک مؤلفه پیاده‌سازی می‌شود تا اگر یکی خطا داد، دیگری بتواند کار را ادامه دهد یا خطا تشخیص داده شود Wikipedia.تحمل خطا (Fault Tolerance) و پایداری (Resilience) در معماری‌های توزیع‌شدهدر معماری‌هایی مانند میکروسرویس، طراحی به‌گونه‌ای است که سرویس‌ها مستقل از هم هستند و ارتباط آن‌ها از طریق واسطه‌ها انجام می‌شود. این استقلال باعث می‌شود که:خطا در سرویس A لزوماً سیستم را مختل نکندسیستم کلی بتواند به فعالیتش ادامه دهد حتی اگر کیفیت پاسخ‌دهی کاهش یابد GeeksforGeeks.رابطهٔ معماری مقاوم با ارزش‌های بیزنسیدر طراحی معماری، نباید فقط به خواص فنی نگاه کرد. معماری مقاوم باید با اهداف بیزنس هم هم‌راستا باشد:کدام بخش‌ها باید همیشه در دسترس باشند؟کدام بخش‌ها می‌توانند در شرایط خطا با کیفیت کمتر ارائه شوند؟هزینهٔ جلوگیری از خطا تا کجا قابل‌قبول است؟این نوع سؤال‌ها باعث می‌شود طراحی خطاپذیر نه فقط فنی، بلکه معنادار و قابل استفاده برای اهداف واقعی بیزنس باشد.جمع‌بندیطراحی سیستم‌های مقاوم در برابر خطا نیازمند درک این است که:خطاها همیشه رخ می‌دهندنوع ارتباط میان اجزا تعیین‌کنندهٔ نحوهٔ انتشار یا محدود شدن خطاستالگوهای معماری مانند Circuit Breaker، Isolation و افزونگی می‌توانند به بهبود پایداری کمک کننداین تصمیم‌ها باید با درک نیازهای بیزنیسی و عملیاتی گرفته شوند</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Tue, 30 Dec 2025 09:51:51 +0330</pubDate>
            </item>
                    <item>
                <title>تصمیم‌گیری در طراحی نرم‌افزار و مواجهه با عدم قطعیت</title>
                <link>https://virgool.io/@mohsen-farokhi/%D8%AA%D8%B5%D9%85%DB%8C%D9%85-%DA%AF%DB%8C%D8%B1%DB%8C-%D8%AF%D8%B1-%D8%B7%D8%B1%D8%A7%D8%AD%DB%8C-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-%D9%88-%D9%85%D9%88%D8%A7%D8%AC%D9%87%D9%87-%D8%A8%D8%A7-%D8%B9%D8%AF%D9%85-%D9%82%D8%B7%D8%B9%DB%8C%D8%AA-zccqag65lrif</link>
                <description>طراحی نرم‌افزار همواره در شرایطی انجام می‌شود که اطلاعات کامل در دسترس نیست.بسیاری از تصمیم‌ها باید زمانی گرفته شوند که همه‌چیز روشن نیست و آینده سیستم به‌طور دقیق قابل پیش‌بینی نیست.1.      طراحی در شرایط عدم قطعیتطراح نرم‌افزار معمولاً با اطلاعات ناقص کار می‌کندهمهٔ سناریوها از ابتدا مشخص نیستندنیازها و شرایط می‌توانند تغییر کنندبنابراین طراحی، به‌معنای رسیدن به پاسخ قطعی نیست، بلکه به‌معنای انتخاب بهترین تصمیم با دانسته‌های فعلی است.2.      نقش فرض‌ها در طراحیبسیاری از تصمیم‌های طراحی بر پایهٔ فرض‌ها شکل می‌گیرند. این فرض‌ها ممکن است درست باشند یا در آینده نقض شوند. به همین دلیل لازم است فرض‌ها:شناسایی شوندمستند شوندو در صورت تغییر شرایط، بازبینی شوند3.       تصمیم‌گیری تدریجیتصمیم‌ها نباید همه در ابتدای پروژه قفل شوند. برخی تصمیم‌ها می‌توانند:به زمان مناسب‌تری موکول شوندبا اطلاعات بیشتر گرفته شونداین رویکرد کمک می‌کند ریسک تصمیم‌های اشتباه کاهش پیدا کند.4.      هزینهٔ تصمیم‌های زودهنگامتصمیم‌هایی که خیلی زود و بدون نیاز گرفته می‌شوند ممکن است انعطاف سیستم را کم کنند و تغییرات بعدی را پرهزینه کنند.بنابراین باید تشخیص داد:کدام تصمیم‌ها حیاتی‌اندو کدام تصمیم‌ها را می‌توان به تعویق انداخت5.      بازبینی و اصلاح تصمیم‌هاتصمیم‌های طراحی:دائمی نیستندباید در طول زمان بررسی شوندو در صورت تغییر شرایط، اصلاح شوندطراحی یک مسیر خطی نیست، بلکه فرآیندی بازگشتی است.6.      ارتباط تصمیم‌ها با business تصمیم‌های طراحی جدا از business معنا ندارند. هر تصمیم باید با توجه به:هدف سیستمارزش ایجاد شدهو محدودیت‌های واقعیگرفته شود، نه صرفاً بر اساس ترجیح فنی.7.      نقش تجربه و یادگیریتجربه نقش مهمی در تصمیم‌گیری دارد اما تجربه هم جای تحلیل را نمی‌گیرد.یادگیری از تصمیم‌های قبلی و بازخوردها بخشی از فرآیند طراحی است.جمع‌بندیعدم قطعیت بخش جدایی‌ناپذیر طراحی نرم‌افزار استتصمیم‌گیری یعنی انتخاب آگاهانه با اطلاعات محدودطراحی خوب، امکان اصلاح و بازبینی را از بین نمی‌برد</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Tue, 16 Dec 2025 10:11:45 +0330</pubDate>
            </item>
                    <item>
                <title>رفتار سیستم در شرایط فشار، خطا و محدودیت</title>
                <link>https://virgool.io/@mohsen-farokhi/%D8%B1%D9%81%D8%AA%D8%A7%D8%B1-%D8%B3%DB%8C%D8%B3%D8%AA%D9%85-%D8%AF%D8%B1-%D8%B4%D8%B1%D8%A7%DB%8C%D8%B7-%D9%81%D8%B4%D8%A7%D8%B1-%D8%AE%D8%B7%D8%A7-%D9%88-%D9%85%D8%AD%D8%AF%D9%88%D8%AF%DB%8C%D8%AA-eljjoy85edqc</link>
                <description>سیستم‌های نرم‌افزاری در دنیای واقعی همیشه در شرایط عادی و ایده‌آل کار نمی‌کنند.قطع سرویس‌ها، فشار زیاد، خطاهای پیش‌بینی‌نشده و از کار افتادن بخش‌هایی از سیستم، اتفاقاتی طبیعی هستند و باید از ابتدا در طراحی دیده شوند.1.      رفتار سیستم در شرایط غیرعادیوقتی سیستم با مشکل مواجه می‌شود، دو حالت کلی وجود دارد:یا سیستم به‌طور کامل از کار می‌افتدیا سیستم با قابلیت و کیفیت کمتر، اما همچنان قابل استفاده باقی می‌ماندتمرکز روی حالت دوم است؛ حالتی که سیستم «سقوط نمی‌کند»، بلکه رفتارش تغییر می‌کند.2.      کاهش سطح سرویس به‌صورت آگاهانهکاهش کیفیت یا حذف برخی قابلیت‌ها نباید اتفاقی باشد.از قبل باید مشخص شده باشد که کدام قابلیت‌ها در شرایط بحرانی حذف می‌شوند. این یعنی رفتار سیستم در شرایط فشار، طراحی‌شده و از پیش فکر شده است.3.       تفاوت از کار افتادن تصادفی و رفتار طراحی‌شدهحالتی که بخشی از سیستم بدون برنامه از کار می‌افتدحالتی که سیستم طبق تصمیم قبلی، بخشی از توان خود را کنار می‌گذارد تا کل سیستم فعال بماندحالت دوم نتیجهٔ تصمیم آگاهانه در طراحی است، نه یک خرابی ناخواسته.4.       ارتباط با در دسترس بودن سیستم«در دسترس بودن» همیشه به معنای کار کردن کامل همهٔ بخش‌ها نیست.در بسیاری از سناریوها، کار کردن ناقص سیستم بهتر از کار نکردن کامل آن است. پس در دسترس بودن می‌تواند در سطوح مختلف معنا داشته باشد.5.      تصمیم‌گیری و بده‌بستاناین نوع طراحی همیشه با بده‌بستان همراه است. یعنی برای حفظ کلیت سیستم، باید بخشی از کیفیت یا بخشی از امکانات فدا شود. این تصمیم بدون شناخت business قابل‌گرفتن نیست.6.       نقش business در این تصمیم‌هاتصمیم‌ها نباید فقط فنی باشندbusiness . باید مشخص کند:چه چیزی حیاتی استچه چیزی قابل حذف یا کاهش استپس این نوع طراحی، نتیجهٔ همکاری فنی و بیزنسی است.7.      فرض‌ها و شرایط بحرانیبسیاری از طراحی‌ها بر اساس فرض‌هایی انجام می‌شوند که ممکن است روزی نقض شوند.به همین دلیل باید:شرایط بحرانی شناسایی شوندحالت‌های خاص در نظر گرفته شوندو رفتار سیستم در این شرایط از قبل مشخص باشد8.      طراحی به‌عنوان یک فرآیند مداوماین تصمیم‌ها یک‌بار برای همیشه گرفته نمی‌شوند.بلکه:طراحی می‌شوندآزمایش می‌شوندبازبینی می‌شوندو در طول زمان اصلاح می‌شوند جمع‌بندیشرایط غیرعادی بخشی از واقعیت سیستم هستندطراحی خوب، این شرایط را انکار نمی‌کندبلکه رفتار سیستم را برای این موقعیت‌ها از قبل مشخص می‌کند</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Sun, 14 Dec 2025 11:38:51 +0330</pubDate>
            </item>
                    <item>
                <title>پیچیدگی، تغییر و تکامل در معماری نرم‌افزار</title>
                <link>https://virgool.io/@mohsen-farokhi/%D9%BE%DB%8C%DA%86%DB%8C%D8%AF%DA%AF%DB%8C-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1-%D9%88-%D8%AA%DA%A9%D8%A7%D9%85%D9%84-%D8%AF%D8%B1-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%DB%8C-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-uvfho84uztl6</link>
                <description>معماری نرم‌افزار یک حوزه مهندسی است که حل مسئله در آن با ساختارهای ریاضی یا فرمولی قابل ارائه نیست و ماهیتاً به «طراحی» و «تصمیم‌گیری» وابسته است.پیچیدگی (Complexity)تغییر مداوم (Constant Change)فرگشت / تکامل (Evolution) پیچیدگی در معماری نرم‌افزار● پیچیدگی همیشه از تعداد زیاد المان‌ها ایجاد نمی‌شودتعداد زیاد کامپوننت‌ها لزوماً پیچیدگی نیست.قابل‌دسترس بودن (Available) بودن سیستم نیز لزوماً مسئله‌ای پیچیده نیست.آنچه باعث پیچیدگی می‌شود:ترکیب شدن چند مسئله با همتأثیر متقابل لایه‌های مختلفتشدید اثر محدودیت‌ها و نیازها بر یکدیگربنابراین پیچیدگی در معماری بیشتر از تعامل مؤلفه‌ها و تصمیمات نشأت می‌گیرد، نه صرفاً تعداد. تغییر مداوم (Constant Change)● تغییر، ویژگی دائمی نرم‌افزار استنرم‌افزار همیشه در معرض تغییر است و این تغییر پایان ندارد.سه منبع اصلی تغییر:تغییرات businessاهداف تغییر می‌کنندمدل کسب‌وکار ممکن است عوض شودمثال: یک startup ناگهان ۲۰ میلیون دلار سرمایه دریافت می‌کند و تمام نیازهایش تغییر می‌کندتغییرات تکنولوژیکابزارها و تکنولوژی‌ها دائماً عوض می‌شوندمثال: تغییر فریمورک، نسخه جدید .NET، ورود IoT یا Cloudتغییرات انسانیآدم‌ها عوض می‌شوندکاربران جدید با نیازهای جدید می‌آیندحتی ممکن است سازمان دیگری کل مجموعه را خریداری کندبنابراین معماری نرم‌افزار باید در ذات خود برای تغییر مداوم آماده باشد. تفاوت «تغییر» و «فرگشت/تکامل»● تغییر (Change)چیزی سطحی‌تر است، مثل:تغییر ابزارتغییر نسخهٔ فریمورکتغییر یک ویژگی یا رفتار● فرگشت/تکامل (Evolution)یک تغییر ماهیتی و عمیق است.برای مثال یک پلتفرم که تا امروز نسخه‌فروشی بوده، تصمیم می‌گیرد به‌صورت سرویس (SaaS) ارائه شوداین دیگر «تغییر» نیست؛ این پوست‌اندازی (Metamorphosis) استمثال «کرمی که تبدیل به پروانه می‌شود»:این تغییر ماهیت است، نه ارتقاء جزئی.بنابراین:Change = اصلاحEvolution = دگردیسیمعماری نرم‌افزار = ساختار + ویژگی‌ها + تصمیمات کلیدیمعماری نرم‌افزار شامل سه جزء است:۱) ساختار (Structure)المان‌های سیستمنحوه ارتباط آن‌هاویژگی‌های ارتباطات۲) ویژگی‌های معماری (Architectural Characteristics)مانند:AvailabilityModifiabilityReliabilityPerformanceو …این ویژگی‌ها وظیفه دارند از تصمیمات طراحی پشتیبانی کنند.۳) تصمیمات کلیدی طراحی (Key Design Decisions)تصمیماتی که تغییر آن‌ها سخت، پرهزینه و اثرگذار است.ابعاد مختلف Design در مهندسی نرم‌افزارطراحی فقط یک بُعد ندارد و Design مجموعه‌ای از لایه‌های تصمیم‌گیری استArchitectural designComponent designDetail designData model design...Viewها در معماری نرم‌افزار(برای مثال بدن یک انسان را در نظر بگیرید)برای درک یک سیستم، می‌توان به آن از زاویه‌های مختلف نگاه کرد؛ مثلا:ساختار (Structure View) – مثل اسکلتعملیات (Behavior View) – مثل سیستم عصبیداده (Data View) – مثل جریان خونهر View بخشی از معماری را نشان می‌دهد.نقش معمار نرم‌افزار و تغییر پارادایممعمار نرم‌افزار یک شخص دستوردهنده از بالا نیست.معماری یک ماهیت تیمی است.دیگر این‌طور نیست که معمار طراحی کند و برنامه‌نویسان فقط کد بزنند.بلکه:تصمیمات باید برای همه قابل فهم باشدهمه باید بتوانند فیدبک بدهندمعماری یک فرآیند مشارکتی استاین مطلب بر سه محور اصلی تأکید داشت:پیچیدگی نتیجهٔ تجمع و تعامل تصمیمات است، نه تعداد مؤلفه‌ها.تغییر دائمی است و معماری باید بر مبنای پذیرش آن شکل بگیرد.فرگشت یک تغییر ریشه‌ای و ماهیتی است، نه ارتقاء ساده.</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Wed, 10 Dec 2025 12:47:30 +0330</pubDate>
            </item>
                    <item>
                <title>مبانی تصمیم‌گیری در طراحی نرم‌افزار</title>
                <link>https://virgool.io/@mohsen-farokhi/%D9%85%D8%A8%D8%A7%D9%86%DB%8C-%D8%AA%D8%B5%D9%85%DB%8C%D9%85-%DA%AF%DB%8C%D8%B1%DB%8C-%D8%AF%D8%B1-%D8%B7%D8%B1%D8%A7%D8%AD%DB%8C-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-aif1zedplt9p</link>
                <description>در فرایند طراحی نرم‌افزار، تمرکز تنها روی کدنویسی نیست؛ بلکه بر این است که بتوانیم مشکل را درست بفهمیم، فضای دامنه را بشناسیم، context را تحلیل کنیم و تصمیم‌های طراحی معنادار بگیریم.طراحی خوب از «درک دقیق مسئله» شروع می‌شود، نه از تکنولوژی یا راه‌حل آماده.１.  شناخت مسئله، business و هدفطراح نرم‌افزار باید بداند:مشکل اصلی چیستچرا این سیستم ساخته می‌شودهدف business چیستکاربران چه نیازهایی دارندچه محدودیت‌ها، ریسک‌ها و ارزش‌هایی وجود داردوقتی هدف‌ها و مسئله روشن نباشند:طراحی بی‌معنا می‌شودتوسعه بی‌هدف انجام می‌شودمحصول ممکن است با نیاز اصلی متفاوت باشد2.  اهمیت Contextیکی از محورهای مهم، نقش «context» در تصمیم‌گیری است.context شامل:شرایط محیطمحدودیت‌های سازمانینیازهای businessتکنولوژی موجودتیم، مهارت‌ها، بودجه، زمانوضعیت فعلی سیستمطراحی همیشه وابسته به Context است و هیچ راه‌حل مطلقاً درست یا غلطی وجود ندارد. بنابراین، یک تصمیم در یک context عالی است و در context دیگر اشتباه.3.  معماری و طراحی به عنوان تصمیممعماری مجموعه‌ای از Architecture Decision است. هر تصمیم معماری معمولاً بر اساس:هدف‌هاcontextمحدودیت‌هاریسک‌هاگزینه‌های موجودtrade-offهاگرفته می‌شود. این تصمیم‌ها هستند که شکل نهایی سیستم را مشخص می‌کنند.4.  Trade-offsدر طراحی همیشه بین گزینه‌های مختلف، توازن برقرار می‌شود:سرعت vs هزینهسادگی vs انعطافقابلیت توسعه vs زمان تحویلکیفیت vs محدودیت‌های تیمهیچ راه‌حل کامل وجود ندارد. طراحِ خوب کسی است که بتواند ارزش‌ها و هزینه‌های هر تصمیم را بسنجد.5.  نقش‌ها و مسئولیت‌هامعمار نرم‌افزارمدیر محصولتوسعه‌دهندهمدیر پروژههرکدام نقش خاصی در تصمیم‌گیری دارند. معمار نباید بدون شناخت business تصمیم بگیرد و مدیر محصول هم نباید معماری را نادیده بگیرد. هدف این است که همه‌ی نقش‌ها با هم یک مسئله مشترک را حل کنند.6.  بدهی فنی (Technical Debt)«فشار زمانی»، «تحویل سریع» و «تصمیم‌های کوتاه‌مدت». این‌ها معمولاً باعث ایجاد بدهی فنی می‌شوند.بدهی فنی زمانی ایجاد می‌شود که:عجله کنیمطراحی را نادیده بگیریمتغییرات موقت انجام دهیمقوانین معماری را رعایت نکنیمو در آینده هزینه زیادی برای رفع آن پرداخت می‌کنیم.7.  تکامل معماری (Evolutionary Architecture)معماری باید بتواند:تغییر کندرشد کندخودش را با شرایط جدید تطبیق دهداین همان مفهوم معماری تکاملی است. معماری باید به جای این‌که یک‌بار برای همیشه طراحی و قفل شود، در طول زمان با نیازها و محدودیت‌ها هماهنگ شود.8.  اولویت‌ها و ارزش‌هابعضی تیم‌ها فقط دنبال زیبایی و کمال (Perfectionism) هستندبعضی تیم‌ها فقط دنبال سرعتبرخی فقط دنبال تکنولوژی جدیداما طراحی خوب یعنی:شناخت ارزش واقعی business و هماهنگ کردن تصمیم‌های فنی با آن. 9.  تفکر طراحی (Design Thinking)طراحی فقط چیدن کلاس و ماژول نیستطراحی یک نوع تفکر استباید مسئله را عمیقاً تحلیل کردباید سوال درست پرسیدباید سناریوها را بررسی کردباید به نیازهای واقعی business توجه کردو این مهارتی است که با تمرین و تجربه به دست می‌آید.</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Tue, 09 Dec 2025 14:57:38 +0330</pubDate>
            </item>
                    <item>
                <title>مبانی معماری نرم افزار - بخش هفتم</title>
                <link>https://virgool.io/@mohsen-farokhi/%D9%85%D8%A8%D8%A7%D9%86%DB%8C-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%DB%8C-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-%D8%A8%D8%AE%D8%B4-%D9%87%D9%81%D8%AA%D9%85-gaq3vo8xpyg3</link>
                <description>در بخش ششم، به موضوع Learnability پرداختیم. در این بخش دو مفهوم Evolution و Refactoring را مورد بحث قرار می دهیم.مسئله Change و Evolve شدن، دو موضوع نزدیک به هم اما متفاوت هستند. مسئله Evolvability، در حدی با اهمیت و تاثیر گذار است که صرفا به عنوان quality attribute، به آن اشاره نمی شود و بیشتر یک Meta-Characteristic است که ابعاد مختلفی دارد.مسئله evolution دارای چند قسمت است. در software و در مسئله Dynamic، با تغییراتی روبرو می شویم که قبل از اتفاق افتادن، قابل پیش بینی نیستند. و در بعضی مواقع از وجود چیزهایی که قرار است تغییر کنند نیز بی خبر هستیم. می تواند شامل تکنولوژی، سازمان ها، آدم ها و غیره شود. از طرفی مسئله Balance را داریم که در تغییرات سعی می کنیم تعادل را برقرار کنیم و زاویه طراحی را به سمت آن تغییرات می بریم.مسئله Evolutionary Architecture که در سال های اخیر در حوزه معماری پر رنگ شده است، به این موضوع می پردازد که ما چگونه می توانیم معماری را طوری طراحی کنیم که تغییر پذیر باشد. مساله evolution در یک مسیر اتفاق می افتد و به حرکت خود ادامه می دهد. evolution در طول زمان ممکن است یک معماری را مدام تغییر دهد. قوانینی پیرامون program evolution تحت عنوان Laws of Program Evolution وجود دارد.سیستم های E-type که قسمتی از دنیای واقعی را مدل کرده اند، در طول زمان باید تطابق داده شوند. در غیر این صورت کاربرد خود را از دست خواهند داد.سیستم های E-type که evolve می شوند، evolve شدن در آن ها باعث افزایش پیچیدگی می شود مگر اینکه ما برای آن ها کاری انجام دهیم. تمام پروژه هایی که در حال حاضر وضعیت خوبی ندارند، بخاطر عدم تغییر بوده است. اکثر این پروژه ها در شروع کار و از نگاه طراح، بهترین design را داشته اند.به معماری هایی Evolutionary Architecture گفته می شود که از تغییرات کوچک در راستای ابعاد مختلف پشتیبانی می کنند.فرض کنید در حوزه availability، نرم افزاری داریم که 98% available است و به دنبال اعمال تغییراتی برای بهتر کردن آن هستیم. اولین موضوع این است که از کجا بفهمیم که این تغییرات ما را به چیزی که می خواهیم نزدیک می کند یا خیر. احتمالا در این مورد به این فکر می کنیم که با هر اتفاقی، این عدد پایین تر نخواهد آمد. بنابراین از طریق یک function به نام Fitness Function، تمام موضوعات characteristic را ارزیابی می کنیم. در اینجا ممکن است عدد uptime نرم افزار را به عنوان ورودی function در نظر بگیریم.مفهوم Fitness Function، مکانیزمی است که مشخص می کند آیا ما داریم در مسیر درست حرکت می کنیم یا خیر. از testها می توانیم برای ساده ترین نوع پیاده سازی استفاده کنیم. برای مثال تست عدم وجود dependency به کلاسی خاص. نمونه ابزار این مدل تست ها ArchUnitNET وجود دارد که می توان با آن تست های در سطح structure نوشت. نوع دیگری از fitness function، خانواده metricها هستند. برای ساده ترین نوع آن میتوانیم Cyclometric Complexity را نام ببریم. در نظر بگیرید که fitness functionها الزاما automate نیستند و می توانند Manual باشند. به این معنی که شخصی چیزی را از طریق مشاهده چک کند.Refactoringاز نگاه design، تغییرات structural یا ساختاری در راستای درایور های Easier to change و More readable که با تغییرات نکردن ماهیت سیستم همراه است.بزرگترین مشکل refactoring، تبدیل به فاز شدن آن است. به این معنی که تجمیع بدهی های فنی، refactoring را با پیامد های زیادی همراه می کند و ممکن به سادگی قابل انجام نباشد. بنابراین همگام شدن با تغییرات کوچک تر، نکته اساسی refactoring است.مفهوم Detour در refactoring مطرح می شود. زمانی که مشکلات پروژه به حدی می رسد که در وضعیت فعلی نمی توان از آن استفاده کرد. بنابراین با ایجاد detorها، اجازه می دهیم سیستم release شود و همزمان تغییرات نیز در حال انجام باشند. در این زمینه، می توانید تکنیک BranchByAbstraction آقای Martin Fowler را مطالعه فرمایید.اتفاقی که در این تصویر مشاهده می کنید، abstraction مناسب تری ایجاد شده است و یکی از clientها به این abstraction جدید اشاره می کند و بقیه clientها به همان با همان کلاس قدیمی کار می کنند. اما client که از abstraction جدید استفاده می کند هم در نهایت از همان پیاده سازی قدیمی استفاده می کند. هدف برداشتن گام کوچک است. گام اول این بوده که یک نفر این abstraction را بشناسد. در جریان این کار، application می تواند release شود و کار کند.در گام بعدی، یک پیاده سازی بهتر برای آن در نظر گرفته می شود. این کار ادامه پیدا می کند تا dependency از پیاده سازی قدیمی کم شود و در نهایت از پروژه حذف شود. این کارها با استفاده از fitness functionهایی که مساله را ارزیابی می کنند، انجام می شود.</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Tue, 21 Nov 2023 15:26:22 +0330</pubDate>
            </item>
                    <item>
                <title>مبانی معماری نرم افزار - بخش ششم</title>
                <link>https://virgool.io/@mohsen-farokhi/%D9%85%D8%A8%D8%A7%D9%86%DB%8C-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%DB%8C-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-%D8%A8%D8%AE%D8%B4-%D8%B4%D8%B4%D9%85-syuvvehzz3ta</link>
                <description>در بخش پنجم، heuristicها را مورد بحث قرار دادیم.در این بخش درباره دو مورد از responseهای Resiliency به نام های Protect و Mitigate صحبت می کنیم. و همچنین موضوع Learnability را نیز بررسی می کنیم.Responsive vs Preventativeراه حل هایی تحت عنوان Responsive و Preventative وجود دارند که به تفاوت های آن می پردازیم.راه حل های preventative در طراحی نرم افزار، به راه حل های جلوگیرانه گفته می شوند. یعنی با اتخاذ مکانیزم های دفاعی، جلوی اتفاق افتادن چیزی را بگیریم. برای مثال در دیتابیس فیلد ایمیل را بصورت unique تعریف کنیم. و راه حل های responsive، به دنبال نشان داده یک response مناسب در زمان اتفاق افتادن مشکل هستند. برای مثال وجود یک سرویس مغایرت گیر در سیستم های مالی، یک راه حل responsive است.از لحاظ Availability، راه حل های responsive دارای availability بالاتری هستند. از این جهت که شما دنبال این نیستید که جلوی یک اتفاق را بگیرید. زمانی که در computation قصد دارید جلوی یک اتفاق را بگیرید، باید در آن مساله جایی برای کنترل متمرکز داشته باشید. وقتی در مورد یک مقدار تکراری صحبت می کنیم، راه حل preventative این است آن را unique index قرار دهیم. و راه حل responsive این است که آن مقدار تکراری ثبت شود و بعد سرویسی مقادیر تکراری را جمع آوری کند و response مناسبی را برگرداند. در مورد unique index، مکانیزم کنترل در درون عملیات وجود دارد و مساله در خود دچار یک گلوگاه ذاتی شده است.از لحاظ Scalability، راه حل های responsive مقیاس پذیرتر هستند. در برخی از مسائل وقتی از مقیاس خاصی عبور می کنیم، نمی توانیم از راه حل های preventative استفاده کنیم.از لحاظ Simplicity در سطح technical، راه حل های preventative ساده تر هستند. وقتی که ما جلوی یک اتفاق را می گیریم، از حجم تحلیل هایی که نیاز داریم کاسته می شود. یعنی نیازی به فکر کردن در مورد این موضوع نیست که اگر این اتفاق افتاد، تاثیر آن چگونه خواهد بود و ما چه کاری باید انجام دهیم. اما گاهی اوقات prevent کردن نیز سخت است و یا prevent کردن با performance درست و حفظ availability و scalability سیستم در این شرایط ممکن است پیچیدگی ایجاد کند.در حوزه Protect کردن، مجموعه ای از راه حل ها را داریم. یکی از responseهایی که در حوزه resiliency می توانیم داشته باشیم، این است که اجازه ندهیم یک failure قابل پیش بینی برای سیستم رخ دهد. یا ریسک اتفاق افتادن را کم کنیم و یا اگر failure اتفاق افتاد، از بخش های دیگر محافظت کنیم.یکسری از tacticهایی وجود دارد که در مورد رفتار سیستم صحبت می کنند. در Service-Level تغییر رفتاری ممکن است رخ دهد که جلوی یکسری از اتفاقات گرفته شود. برای مثال، ورودی درخواست های سیستم را محدود کنیم که می تواند به اشکال مختلفی اتفاق بیافتد.تغییر رفتاری که در Service-Level اتفاق می افتد، در راستای حفظ Service-Level Agreement انجام می شود. برای مثال حفظ ترافیک سیستم. این تغییر رفتار، بعضی وقت ها شامل کل scope در application می شود و بعضی وقت ها بر اساس رفتار یک کاربر، IP یا application و غیره اتفاق می افتد. ساده ترین نوع آن، تغییر رفتار برای کل application است. راه حل دیگری که وجود دارد، رفتار را بر اساس system state انجام می دهد. زمانی که system در وضعیتی قرار می گیرد که اصطلاحا Overload می شود، این مکانیزم فعال می شود.دسته دیگری از راه حل ها وجود دارند که در خانواده protect قرار می گیرند. هدف جلوگیری از بزرگ شدن یک مشکل است. مثل Circuit Breaker یا Timeout که از اتفاق افتادن یک failure بعد از این failure را می گیرند.در راستای Mitigate کردن، راه حل هایی که وجود دارند در حوزه resiliency تعریف می شوند اما کاملا محدود به resiliency نیستند. برای مثال مجموعه راه حل هایی که در راستای Data Consistency بعد از اتفاق افتادن یک failure انجام می شوند.موضوع resiliency این است که، زمانی که یک failure اتفاق می افتد یا سیستم به یک failure نزدیک می شود، باید در سیستم ساز و کاری داشته باشیم که سیستم کار خود را ادامه دهد یا حداقل در سطح پایین تری کار خود را ادامه دهد.بعد از اتفاق افتادن failure و برای recover شدن نرم افزار، راه حل های general وجود دارد.موضوع Compensation یا Compensation Transaction در حوزه Data Consistency، هر نوع رفتاری است که در راستای برگرداندن وضعیت خراب به وضعیت درست انجام می شود. برای مثال عملیات rollback کردن در کارت به کارت هایی که با موفقیت انجام نمی شود.راه حل هایی که در حوزه Data Availability انجام می شود. برای مثال داشتن یک replication از داده ها که ممکن است در حوزه دسترسی به گزارش گیری مورد استفاده قرار گیرد.موضوع resiliency، جزو موضوعاتی است که وضعیت آن باید همیشه بررسی و بهبود داده شود. به این دلیل شاید خیلی از آن ها را از قبل ندانیم. بنابراین مساله Maintaining مطرح می شود. یعنی اینکه کارکرد مساله باید در عمل بررسی شود، مشکلات آن رفع شود و یا بازبینی در آن اتفاق بیافتد. ما از خیلی از failureها اطلاعی نداریم  و در داخل عملیات شناسایی می شوند. برای مثال رفتاری خارج از contract که یک third-party ممکن است انجام دهد.نقش Observability، در گام اول برای این مهم است که خیلی از رفتار های resilient را داشته باشیم. و در گام بعدی برای موضوع ارزیابی، اندازه گیری، تشخیص، برای مثال اینکه چه زمانی حجم درخواست ها بالاست یا چه زمانی تعداد fault و failure بیشتر می شود و یا third-party چه زمانی بیشتر down می شود و غیره که در تحلیل ها مورد استفاده قرار می گیرند.Learnabilityموضوع مهمی دیگری که کمتر به آن توجه می شود، موضوع Learnability است که در quality attributeها در حوزه Cross-Cutting قرار می گیرد.بطور کلی فرآیند یادگیری به این صورت است که در شروع سرعت آرامی داریم و رفته رفته با یک سرعت قابل قبول در یک فضای بلوغ از یادگیری می رسیم و مجدد سرعت کم می شود. برای کُند بودن افراد در شروع یادگیری در یک مساله، راه حلی نمی توان ارائه کرد. به این دلیل که مدت زمانی لازم است تا کلیات مساله مورد مطالعه قرار بگیرد. موضوعی که در learnability بیشتر دارای مساله است، بازه ای است که سرعت یادگیری بالا رفته و شامل بخش بزرگی است.مساله قابلیت یادگیری، عمدتا در زمان و هزینه است و الزاما امکان پذیری آن نیست. زمانی که در مورد دانش صحبت می کنیم، Explicit knowledge را به عنوان یک دانش خیلی سریع و واضح و مشخص می دانیم. موضوع How در کد خیلی وقت ها explicit است. با نگاه کردن به کد متوجه عملکرد آن می شویم. چیزی که در سطح کد explicit نیست، Why است که همیشه Implicit است. بنابراین درک &quot;چرایی&quot; مهم تر &quot;چگونگی&quot; است.نکته مهم در learnability، تلاش برای تبدیل Implicit knowledge به Explicit knowledge است.مشخصه learnability بر روی maintainability تاثیر گذار است.مورد Test یا Specifications، سه quality attribute اصلی Readability, Maintainability Trustworthiness را داریم که اگر وجود نداشته باشند تست با مشکلات جدی روبرو خواهد شد.در راستای learnability در سطح Code، در جهت خوانایی کد مشخصه Readability را داریم. مساله Comment در جایی که به تشریح کد می پردازد، نشان از عدم خوانایی کد دارد. اما اگر commentها به مساله چرایی مساله بپردازند می توانند مفید باشند. در جایی که امکان readable کردن کد به هر دلیلی وجود ندارد، می توانیم از comment استفاده کنیم و آن را تشریح کنیم.در راستای learnability در مورد Simple Design یا طراحی ساده، موضوعی سادگی با موضوع آسان بودن نباید اشتباه گرفته شود. برای یک سوال پیچیده نمی توانیم یک جواب آسان دهیم. منظور از سادگی، الزما تعداد کم اِلمان ها نیست. منظور این است که چیز اضافه ای وجود نداشته باشد و آن چیزهایی باید وجود داشته باشند که مساله به آنها نیاز دارد.موضوع Integrity جزء موضوعات مهم در learnability است که به یکپارچگی یا وحدت رویه می پردازد. منظور استفاده همه از یک معماری یا روش خاصی نیست. منظور زمانی است که وقتی با مسائل مشابه ای روبرو هستیم، برای آنها از روش های مختلفی استفاده نکنیم و جایی که در مورد مفاهیم یکسان صحبت می کنیم، زبان و فضا تغییر نکند. برای مثال، به مساله naming در کد می توانیم اشاره کنیم.در راستای learnability موضوع Source Control فارغ از نگاه ابزاری جهت نگهداری کد و تغییرات آن، یک منبع خیلی غنی از دانش و اطلاعات می باشد. تحلیل هایی روی آن می توان انجام داد که اطلاعات ارزشمندی را به ما می دهد و در حوزه learnability اهمیت بالایی دارد. نقطه شروع مطالعه برای اعمال تغییرات و آنالیز مساله می تواند مطالعه source control باشد.</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Tue, 07 Nov 2023 08:56:44 +0330</pubDate>
            </item>
                    <item>
                <title>مبانی معماری نرم افزار - بخش پنجم</title>
                <link>https://virgool.io/@mohsen-farokhi/%D9%85%D8%A8%D8%A7%D9%86%DB%8C-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%DB%8C-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-%D8%A8%D8%AE%D8%B4-%D9%BE%D9%86%D8%AC%D9%85-rpkkb8j3ljcq</link>
                <description>در بخش چهارم، مساله resiliency و سیستم های resilient را مورد بحث قرار دادیم.در این بخش در مورد Heuristicها صحبت می کنیم که یکی از مباحث اساسی در Architectural Design است.در تعریف Engineering Method، استراتژی اعمال بهترین تغییر، در فضایی است که خوب شناخته نشده یا به علت داشتن منابع محدود، امکان شناخت کامل آن وجود ندارد. برای مثال بازی شطرنج را در نظر بگیرید. در بازی شطرنج به این فکر می کنیم که مهره خود را چگونه حرکت دهیم. یک حالت این است که تمام مسیرهای قابل اجرا را محاسبه کنیم و بعد به نتیجه برسیم. با وجود داشتن منابع محدود، این کار غیر منطقی به نظر می رسد. بنابراین به موضوع استفاده از Heuristicها برای اعمال بهترین تغییر می رسیم. تقریبا در تمام مسائل مهندسی، مساله ها مبتنی بر heuristic هستند. برای مثال در شطرنج یک heuristic می تواند فراهم کردن شرایط حرکت رُخ باشد.یک heuristic، هر آن چیزی است که یک مسیر یا کمک احتمالا خوبی در مساله است. اما از لحاظ تحلیلی، ممکن است کمک کننده نباشند و یا آسیب زننده هم باشند. بنابراین، یک مجموعه از heuristicها شامل راهنمایی ها، تذکرات و نکاتی در حول محور حل مساله است که در یکسری از contextها موثر هستند اما قابل اثبات هم نیستند.نکته ای که در مورد heuristicها وجود دارد، این است که وابسته context هستند. نکته بعدی این است که ممکن است heuristicها با هم در تناقض باشند. آن چیزی که در heuristic وجود دارد، مجموعه ای از فرضیات در ذهن کسی است که آن را مطرح می کند. Properties over Principlesتا زمانی که از لحاظ ذهنی از &quot;اصل&quot; عبور نکنیم، خیلی از موضوعات معنی پیدا نمی کنند. خیلی وقت ها در زمان ارائه راه حل، این مساله را نمی دانیم که principleها به آن معنا اصل نیستند و بیشتر heuristicهایی هستند که قرار است به حل مساله کمک کنند. وقتی نگاه ما کاملا اصول محور باشد، وارد این فضا می شویم که اصل را باید رعایت کنیم نه اینکه مساله را حل کنیم. برای مثال ممکن است به یک طراح بگوییم که این تصمیم به این دلیل اشتباه است که اصل SRP رعایت نشده است. در حوزه technical، یکی از مشکلات برداشت اشتباه از heuristicها است. لازم است که ادبیات خود را از اصول گرایی به معنای اصل هایی که باید رعایت شوند، به سمت ویژگی هایی ببریم که می خواهیم داشته باشیم. برای مثال Simplicity. در مورد ویژگی هایی صحبت کنیم که باعث ساده سازی می شوند. در حوزه Technical، به مطالعه heuristicهایی می پردازیم که ممکن است آن ها را به عنوان اصل بشناسید. برای مثال (DRY) Don&#x27;t repeat yourself. و در حوزه Strategic نیز به مطالعه heuristicهایی می پردازیم که بیشتر ماهیت راهبردی دارند. برای مثال &quot;تصمیمات را تا جای ممکن به تاخیر بیانداز&quot;. با هدف بدست آوردن اطلاعات بیشتر از مساله.به طور کلی در Design چندین موضوع مطرح می شود. موضوع اول این است که ابهام را به عنوان یک ریسک قلمداد کنیم. از بزرگترین آسیب هایی که در طراحی نرم افزار وجود دارد، مساله ابهام است. زمانی که در مساله ابهام داشته باشیم، قابلیت trade-off را از دست می دهیم. اینکه در مورد مساله شناخت کافی داشته باشیم ولی بسته به شرایطی، بهترین راه حل را انتخاب نکنیم، می تواند کاملا قابل پذیرش باشد. اما زمانی که در مورد مساله ابهام داریم و وارد design می شویم، همیشه با مساله های ناشناخته ای روبرو خواهیم شد. سوالی که به عنوان یک معمار در طراحی باید زیاد مطرح کنیم، این سوال می باشد که مساله Language را مطرح می کند.Are we talking about the same thing?به عنوان یک طراح، نباید در مورد زبان آدم هایی که مساله را عنوان می کنند ابهام داشته باشیم. برای درست فهمیدن مساله، باید یک زبان مشترک و دقیق حول محور مساله تشکیل دهیم. Weak / Weakest linkموضوع راهبردی دیگری که وجود دارد، weak یا weakest link است. به این موضوع می پردازد که، بازدهی یک سیستم، به اندازه bottleneck آن است. برای مثال جریان ترافیک یک جاده را bottleneck آن جاده تعیین می کند.وقتی در مورد حل مساله صحبت می کنیم، به دنبال بالابردن بهره وری و پایین آوردن هزینه ها هستیم. و در نهایت با اعمال کمترین هزینه، بیشترین سود را داشته باشیم. زمانی که نام microservice مطرح می شود، ممکن است با این فضا روبرو شوید که یک دیتابیس وجود دارد و در راستای optimize کردن، سرویس ها از همدیگر جدا شده اند. در صورتی که در اینجا، دیتابیس به عنوان یک bottleneck، تاثیر جدی روی سیستم می گذارد.به توضیحات بیشتری در مورد DRY به عنوان یک heuristic در حوزه technical بپردازیم. چه دغدغه هایی در پشت DRY وجود دارد؟ زمانی که از زاویه code duplication به مساله نگاه کنیم، مساله به این سمت می رود که هدف ما جلوگیری از تکرار کد باشد. اما DRY در مورد تکرار کد صحبت نمی کند و بحث آن عدم تکرار دانش است. در مورد DRY دو موضوع maintainability و learnability وجود دارد. اگر بخواهیم آن را بهتر تشریح کنیم، می توانیم بگوییم knowledgeها در یک ساختار باید در یکجا وجود داشته باشند.</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Mon, 16 Oct 2023 16:10:39 +0330</pubDate>
            </item>
                    <item>
                <title>مبانی معماری نرم افزار - بخش چهارم</title>
                <link>https://virgool.io/@mohsen-farokhi/%D9%85%D8%A8%D8%A7%D9%86%DB%8C-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%DB%8C-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-%D8%A8%D8%AE%D8%B4-%DA%86%D9%87%D8%A7%D8%B1%D9%85-wwpwuqfmnkaw</link>
                <description>بررسی اصول طراحی سیستم‌های Resilient و تأثیر آن بر ویژگی‌های کیفی نرم‌افزار.در بخش‌های پیشین، به بررسی faultها و تأثیر آن‌ها بر ویژگی‌های کیفی نرم‌افزار پرداخته شد. یکی از ویژگی‌های کیفی مهم در نرم‌افزارهای مقیاس بزرگ، resiliency است؛ مفهومی که در سال‌های اخیر به دلیل تغییرات ساختاری در معماری نرم‌افزارها و ظهور تکنولوژی‌های جدید مانند cloud computing، اهمیت بیشتری یافته است.تحولات معماری و نقش Resiliencyدر معماری‌های سنتی نرم‌افزارها، ساختارهای monolithic غالب بودند که به شکل یک یا چند instance واحد پیاده‌سازی می‌شدند. اما با گسترش استفاده از محاسبات ابری، پروژه‌ها مقیاس بزرگتری پیدا کردند و معماری‌های توزیع‌شده (distributed) جایگزین معماری‌های متمرکز شدند. این تغییرات باعث شدند که مفاهیمی چون high-availability و resiliency به یکی از بخش‌های مهم طراحی و توسعه نرم‌افزارهای cloud-ready تبدیل شوند.تفاوت High-Availability در سیستم‌های Monolithic و Cloud-Readyدر سیستم‌های سنتی، مباحث high-availability عمدتاً در سطح زیرساخت و با روش‌هایی مثل replication دیتابیس یا سرورهای اپلیکیشن صورت می‌گرفت. اما در معماری‌های cloud-ready، high-availability به بخش‌های داخلی معماری و طراحی نرم‌افزار نفوذ کرد. هدف این است که اگر یکی از سرویس‌ها دچار اختلال شد، نرم‌افزار به نحوی عمل کند که این اختلال اثر منفی کمتری داشته باشد و حتی بتواند سرویس مختل شده را به مدار بازگرداند.اهمیت درک Business Context در طراحی سیستم‌های Resilientیکی از نکات مهم در طراحی سیستم‌های resilient، درک Business Context است. چرا که failureها به عنوان بخشی از واقعیت سیستم‌های بزرگ شناخته می‌شوند و در صورتی که رخ دهند، باید در چارچوب اهداف و نیازهای کسب‌وکار مدیریت شوند. دو معیار مهم در این راستا، Mean Time Between Failures (MTBF) و Mean Time to Recovery (MTTR) هستند. هدف اصلی، کاهش زمان لازم برای بازیابی سیستم از failure یا کاهش اثرات جانبی آن است.فرآیند شناسایی Failure و نقش Circuit Breakerبرای مقابله با failure، ابتدا باید بتوانیم آن را شناسایی کنیم. در این راستا، دو مفهوم Detection و Recognition مطرح می‌شوند که معانی متفاوتی دارند. با رخ دادن failure، ابتدا باید آن را recognize کنیم، یعنی ماهیت و محل وقوع آن را مشخص کنیم. سپس فرآیند detection برای شناسایی و واکنش به failure انجام می‌شود. به عنوان مثال، در مکانیزم Circuit Breaker، failure پیش‌بینی شده و مکانیزمی برای پاسخ مناسب به آن پیاده‌سازی می‌شود.مدیریت failure از طریق Isolate کردن و تفاوت بین ارتباطات Sync و Asyncدر طراحی سیستم‌های resilient، یکی از اصول کلیدی، ایزوله‌سازی failure است؛ یعنی محدود کردن تأثیر failureها به بخش‌های خاص سیستم. در این رابطه، تفاوت بین رفتارهای Sync و Async اهمیت پیدا می‌کند. در حالت sync، کامپوننت‌ها به طور مستقیم به هم وابسته‌اند، در نتیجه failure در یکی از کامپوننت‌ها باعث اختلال در دیگری می‌شود. اما در ارتباطات async، این وابستگی زمانی وجود ندارد و failure یک کامپوننت ممکن است تأثیر کمتری بر کامپوننت‌های دیگر داشته باشد.در حالت async نیز ممکن است داده‌ها به روز نباشند، در حالی که در حالت sync داده‌ها به‌روز می‌مانند و محاسبات خطا کاهش می‌یابد. در اینجا context و نیازهای خاص سیستم تعیین می‌کند که بروز بودن داده‌ها تا چه اندازه اهمیت دارد.استفاده از Cache و رفتارهای پیش‌فرض در کاهش اثرات failureراه‌حل‌هایی مانند استفاده از Cache و تعریف رفتارهای پیش‌فرض می‌توانند به کاهش اثرات failure کمک کنند و سیستم را از نظر کارکردی ایزوله نگه دارند.روش‌های Protect مانند Timeout و Circuit Breakerدر برخی موارد، هدف اصلی از پیاده‌سازی راه‌حل‌های protect، رفع failure نیست بلکه جلوگیری از تشدید آن است. به عنوان مثال، در مکانیزم Timeout، یک کامپوننت تنها تا زمانی معین منتظر پاسخ کامپوننت دیگر می‌ماند و در صورت عدم دریافت پاسخ، از انتظار خارج می‌شود. در مکانیزم Circuit Breaker نیز، با کنترل جریان ارتباط بین دو کامپوننت و قطع ارتباط در صورت لزوم، از تشدید failure جلوگیری می‌شود. این مکانیزم بر اساس یک state machine کار می‌کند که شامل حالات Closed، Open و Half-Open است.نتیجه‌گیریطراحی سیستم‌های resilient به عنوان یک رویکرد نوین در توسعه نرم‌افزارهای مقیاس بزرگ، به تیم‌های فنی امکان می‌دهد که با استفاده از تکنیک‌های ایزوله‌سازی، محافظت و مدیریت خطا، پایداری و دسترس‌پذیری سیستم‌ها را بهبود دهند. این رویکردها نه تنها به کاهش اثرات failure کمک می‌کنند، بلکه با کاهش زمان بازیابی، پاسخ‌گویی بهتر به نیازهای کسب‌وکار را نیز ممکن می‌سازند.</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Sun, 08 Oct 2023 15:26:50 +0330</pubDate>
            </item>
                    <item>
                <title>مبانی معماری نرم افزار - بخش سوم</title>
                <link>https://virgool.io/@mohsen-farokhi/%D9%85%D8%A8%D8%A7%D9%86%DB%8C-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%DB%8C-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-%D8%A8%D8%AE%D8%B4-%D8%B3%D9%88%D9%85-qqwzwgkcjix9</link>
                <description>در بخش قبلی به مفهوم availability و ابعاد مختلف آن پرداختیم. در این بخش، به جزئیات بیشتری از مفهوم fault و تاثیرات آن بر سیستم‌ها پرداخته می‌شود.همانطور که اشاره شد، fault به دلیل بروز اختلالاتی پدید می‌آید؛ به‌عنوان‌مثال، باگ در طراحی یا قطعی برق سرور عملیاتی می‌تواند به عنوان یک fault تلقی شود. Faultها می‌توانند ناشی از نفوذ یک فرد به سیستم یا ناآگاهی توسعه‌دهنده باشند و به این دلیل نباید آن‌ها را صرفاً به معنای «حادثه» (Accident) در نظر گرفت.ویژگی‌های کیفی و تاثیر Fault بر آن‌هاعمده Quality Attributeها در حوزه Operational، مشخصه‌هایی هستند که در زمان اجرا و عملیات سیستم نمود پیدا می‌کنند. برخی از این مشخصه‌ها عبارتند از:Availability: میزان در دسترس بودن سیستمReliability: تداوم سرویس‌دهی سیستمResiliency: توانایی سیستم در بازیابی از failureها یا کاهش تأثیر آن‌هاPerformance: کارایی سیستم تحت تأثیر Faultبه طور کلی، faultها می‌توانند این ویژگی‌ها را مستقیم یا غیرمستقیم تحت تأثیر قرار دهند.ابعاد مختلف Faultبرای بررسی بهتر faultها، می‌توان آن‌ها را از ابعاد مختلف دسته‌بندی کرد:Nature: شامل دو نوع Accident (غیرعمدی) و Intentional (عمدی) است. در حالت Intentional، ممکن است نفوذ به سیستم انجام شود یا یک خطا توسط توسعه‌دهنده به دلیل عدم آگاهی ایجاد گردد.Cause: شامل دو نوع Physical و Human است. Physical faultها مانند قطعی شبکه، خرابی دیسک، یا قطع برق می‌باشند.Boundary: به مرزهای failure اشاره دارد و به دو دسته Internal و External تقسیم می‌شود. حملات خارجی، به عنوان External و مشکلات داخلی سیستم مانند down شدن پایگاه داده به عنوان Internal در نظر گرفته می‌شوند.Phase of Creation: به مرحله‌ای که fault رخ داده است، اشاره می‌کند و به دو دسته Design و Operational تقسیم می‌شود. خطاهای طراحی (Design faults) معمولاً دائمی هستند و منشا داخلی و انسانی دارند.Persistence: به ماندگاری fault اشاره دارد و به دو نوع Permanent و Temporary تقسیم می‌شود. برای مثال، physical faultها مانند خرابی قطعات نیازمند مداخله برای رفع هستند و معمولاً Permanent تلقی می‌شوند.بررسی ویژگی‌های کیفی مرتبط با Failureسیستم‌ها عموماً دو حالت Valid و Invalid دارند. انتقال از حالت Valid به Invalid به عنوان failure شناخته می‌شود و بازگشت به حالت Valid را restoration می‌نامیم.مشخصه Availability، نشان‌دهنده میزان دسترسی‌پذیری سیستم است و مشخصه Reliability، به تداوم سرویس‌دهی سیستم اشاره دارد. این دو ویژگی به شدت به یکدیگر وابسته هستند، زیرا استمرار سرویس‌دهی زمانی معنا پیدا می‌کند که سیستم آماده و در دسترس باشد.مشخصه Safety، موضوع اعتماد به سیستم را در بر می‌گیرد. عدم وجود این ویژگی باعث کاهش اعتماد به نرم‌افزار می‌شود.مشخصه Resiliency، ویژگی بازگشت به حالت عادی پس از failure و کاهش تأثیر آن است. با گسترش cloud computing و فاصله گرفتن از ساختار monolithic، اهمیت این ویژگی افزایش یافته است.مدیریت Fault در سیستم‌های Resilientیک سیستم resilient می‌تواند failure را بپذیرد و بیشتر به بعد از وقوع آن توجه داشته باشد تا اقدامات پیشگیرانه. طراحی سیستم‌های توزیع‌شده باید بر مبنای پذیرش وقوع خطا و تمرکز بر بازیابی و کاهش اثرات آن باشد.ویژگی resiliency در واکنش به faultها به پنج طبقه تقسیم می‌شود:Recognize: شناسایی failureها.Isolate: کاهش تأثیر failureها؛ برای مثال، ارتباطات ناهمزمان (Async Communication) به‌عنوان یک tactic برای ایزوله‌کردن استفاده می‌شود.Protect: جلوگیری از تأثیر failure؛ به عنوان مثال Circuit Breaker.Mitigate: مقابله با failure.Resolve: اطمینان از اجرای صحیح اقدامات مقابله با failure.نتیجه‌گیریبا توجه به اهمیت و پیچیدگی مدیریت Fault در سیستم‌های عملیاتی، بهبود ویژگی‌های کیفی مانند Availability، Reliability، Safety، و Resiliency از اصول اصلی در طراحی سیستم‌های مدرن به‌ویژه سیستم‌های توزیع‌شده و مبتنی بر cloud است.</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Mon, 11 Sep 2023 14:46:50 +0330</pubDate>
            </item>
                    <item>
                <title>مبانی معماری نرم افزار - بخش دوم</title>
                <link>https://virgool.io/@mohsen-farokhi/%D9%85%D8%A8%D8%A7%D9%86%DB%8C-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%DB%8C-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-%D8%A8%D8%AE%D8%B4-%D8%AF%D9%88%D9%85-q3yfibelnfwo</link>
                <description>در معماری نرم‌افزار، کیفیت و کارایی سیستم‌ها تا حد زیادی به ویژگی‌های کیفی آن‌ها وابسته است. ویژگی‌های کیفی مانند عملکرد، امنیت، پایداری و قابلیت نگهداری نقش مهمی در موفقیت یک نرم‌افزار ایفا می‌کنند. در اینجا به بررسی و طبقه‌بندی این ویژگی‌ها، تاثیر متقابل آن‌ها و نحوه بهینه‌سازی آن‌ها در قالب استراتژی‌های trade-off می‌پردازیم.معماری نرم‌افزار فرایندی پیچیده است که هدف آن ایجاد ساختاری است که علاوه بر پوشش نیازهای عملکردی، ویژگی‌های کیفی مورد نظر را نیز برآورده کند.طبقه‌بندی ویژگی‌های کیفیویژگی‌های کیفی یا Quality Attributes بر اساس عملکرد و اهداف مورد نظر به سه دسته اصلی تقسیم می‌شوند:Operationalویژگی‌های عملکردی که عمدتاً در زمان اجرا نمود پیدا می‌کنند.Structuralویژگی‌های ساختاری که در طراحی نرم‌افزار نقش دارند.Cross-Cuttingویژگی‌هایی که بخش‌های مختلف سیستم را تحت تاثیر قرار می‌دهند.ویژگی‌های عملیاتی (Operational)این دسته شامل ویژگی‌هایی است که مستقیماً بر رفتار سیستم در زمان اجرا تاثیر می‌گذارند، مانند عملکرد (performance)، در دسترس بودن (availability)، پایداری (resiliency) و قابلیت اطمینان (reliability). این ویژگی‌ها به طور گسترده‌ای توسط اندازه‌گیری‌های دقیق ارزیابی می‌شوند و برای بهبود عملکرد در محیط‌های عملیاتی مورد استفاده قرار می‌گیرند.ویژگی‌های ساختاری (Structural)ویژگی‌های ساختاری به مواردی مانند قابلیت نگهداری (maintainability)، قابلیت تکامل (evolvability) و قابلیت تست (testability) اشاره دارند که بر طراحی و توسعه بلندمدت سیستم اثر می‌گذارند. بهینه‌سازی این ویژگی‌ها، توسعه و بهبود نرم‌افزار در طول زمان را ساده‌تر می‌کند.ویژگی‌های Cross-Cuttingاین ویژگی‌ها به عنوان ویژگی‌های کیفی سیستم شناخته می‌شوند که بخش‌های مختلف را تحت تاثیر قرار می‌دهند. برای مثال امنیت (security) و یادگیری‌پذیری (learnability). این ویژگی‌ها اغلب در سراسر سیستم منتشر شده و چالش‌های خاصی برای تیم‌های معماری و توسعه به همراه دارند.ویژگی‌های کیفی صریح و ضمنی (Explicit and Implicit)ویژگی‌های کیفی در زمینه‌های مختلف ممکن است صریح (explicit) یا ضمنی (implicit) باشند. برای مثال، امنیت در یک وب‌سایت معمولی ممکن است به عنوان یک ویژگی ضمنی در نظر گرفته شود، اما در سیستم‌های حساس و حیاتی مانند سیستم‌های بانکی، امنیت به طور صریح مورد توجه قرار گرفته و به عنوان یک ضرورت طراحی تلقی می‌شود.استراتژی‌های trade-off در ویژگی‌های کیفیکیفیت‌های مختلف می‌توانند اثرات مثبت یا منفی بر یکدیگر داشته باشند و نیاز به بهینه‌سازی trade-off میان آن‌ها را ایجاد کنند. به عنوان مثال، افزایش امنیت ممکن است موجب کاهش عملکرد شود و بالعکس. هدف معماری نرم‌افزار، دستیابی به کم‌هزینه‌ترین ترکیب trade-off برای به حداکثر رساندن کیفیت‌ها است.بررسی ویژگی دسترس‌پذیری (Availability)دسترس‌پذیری یکی از ویژگی‌های عملیاتی کلیدی است که بر مبنای وقوع خطاها (faults) و شکست‌ها (failures) تعیین می‌شود. در این بخش به بررسی روش‌های جلوگیری از بروز خطا، شناسایی خطا و بهبود دسترس‌پذیری می‌پردازیم:Preventجلوگیری از وقوع خطاهاTolerateمدیریت خطا و تحمل آن با استفاده از روش‌هایی مانند IdempotencyRemoveحذف عامل خطا از مدار. برای مثال خارج کردن server دارای اختلالForecastپیش‌بینی خطاها. برای مثال پیش‌بینی زیر بار رفتن سیستمدر مورد fault، اولین و مهم‌ترین مسئله، شناسایی آن‌ها است. منظور از شناسایی این است که بدانیم چه نوع faultهایی ممکن است در سیستم رخ دهند و چگونه می‌توانیم برای آن‌ها آماده باشیم. به‌عنوان مثال، خرابی دیتابیس یا قطع شدن شبکه از جمله faultهای محتمل هستند.در ادامه، ارزیابی ریسک و تواتر رخداد این خطاها مطرح می‌شود که برای درک عمق و احتمال تاثیرات آن‌ها ضروری است.به طور کلی faultها به سه دسته کلی تقسیم می‌شوند:Minor: خطاهای جزئی که ممکن است کاربران را دچار ناراحتی کنند، اما باعث از کار افتادن سیستم نمی‌شوند.Major: خطاهای جدی که می‌توانند در عملکرد سیستم اختلال ایجاد کنند.Critical:خطاهایی که در صورت تبدیل به failure، ممکن است سیستم را به طور کامل از کار بیندازند.این طبقه‌بندی به معماران سیستم کمک می‌کند تا با توجه به سطح اهمیت، برای هر نوع fault راهکارهای مناسبی جهت شناسایی و مدیریت آن‌ها تدوین کنند.سناریوهای مختلف در دسترس‌پذیری (Availability General Scenarios)برای سنجش دسترس‌پذیری، لازم است عوامل موثر بر سیستم و شرایط مختلف را مورد بررسی قرار دهیم:Normal Operation: شرایط عادی سیستمOverloaded: شرایطی که سیستم تحت بار زیاد قرار داردSpecial Occasions: شرایط خاص مانند راه‌اندازی، خاموشی و بازیابی سیستمDegraded Operation: حالتی که عملکرد سیستم کاهش یافته و فقط بخشی از وظایف اجرا می‌شودتکنیک‌های شناسایی و بازیابی از خطاسیستم‌ها باید قادر به شناسایی خطاهای پیش آمده و بازیابی از آن‌ها باشند. تکنیک‌هایی مانند Heartbeat، Monitoring و Ping برای شناسایی خطا و تکنیک‌های Resiliency برای بازیابی سیستم از شکست استفاده می‌شوند.نتیجه‌گیریدر معماری نرم‌افزار، ویژگی‌های کیفی نقش بسیار مهمی در موفقیت سیستم‌ها ایفا می‌کنند. طراحی و پیاده‌سازی این ویژگی‌ها نیازمند شناخت و درک کامل trade-offهای بین آن‌ها است.</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Sat, 02 Sep 2023 17:37:13 +0330</pubDate>
            </item>
                    <item>
                <title>مبانی معماری نرم افزار - بخش اول</title>
                <link>https://virgool.io/@mohsen-farokhi/%D9%85%D8%A8%D8%A7%D9%86%DB%8C-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%DB%8C-%D9%86%D8%B1%D9%85-%D8%A7%D9%81%D8%B2%D8%A7%D8%B1-%D8%A8%D8%AE%D8%B4-%D8%A7%D9%88%D9%84-um3sj9ps6ggz</link>
                <description>معماری نرم‌افزار: رویکرد طراحی کلان برای مدل‌سازی کیفیت و عملکرد سیستم‌های نرم‌افزاریرویکرد طراحی کلان در این زمینه به معنای آن است که معماران نرم‌افزار به‌جای توجه به جزئیات پیاده‌سازی، بیشتر روی ساختار کلی و نحوه تعامل بخش‌های مختلف سیستم تمرکز دارند تا بتوانند سیستمی مقیاس‌پذیر، قابل اعتماد، و با عملکرد مناسب ارائه دهند.در معماری نرم‌افزار، مفاهیم مختلفی برای به تصویر کشیدن و دستیابی به کیفیت و عملکرد مورد انتظار سیستم مطرح می‌شوند. از جمله مهم‌ترین این مفاهیم می‌توان به مشخصه‌های کیفی، سبک‌های معماری، الگوهای معماری، و تاکتیک‌های معماری اشاره کرد.آشنایی با مفاهیم اساسی در معماری نرم‌افزارمشخصه‌های کیفی (Quality Attributes / Characteristics)معماری نرم‌افزار بر پایه مشخصه‌های کیفی بنا شده است که موفقیت سیستم را در کنار عملکرد آن تعریف می‌کنند. برخی از مشخصه‌های کیفی اصلی شامل عملکرد (Performance)، دسترسی‌پذیری (Availability)، مقیاس‌پذیری (Scalability)، قابلیت نگهداری (Maintainability)، و تست‌پذیری (Testability) هستند. این مشخصه‌ها با هدف ارتقای کیفیت سیستم در سطح معماری مطرح می‌شوند و معماری نرم‌افزار تأثیر مستقیمی بر آن‌ها دارد.در بررسی و توصیف یک سیستم و معماری آن، اغلب از اصطلاح &quot;مشخصه&quot; یا &quot;Characteristics&quot; استفاده می‌شود. هنگامی که در مورد طراحی صحبت می‌کنیم، اصطلاح &quot;مشخصه کیفی&quot; یا &quot;Quality Attribute&quot; به کار می‌رود. به عنوان مثال، مقیاس‌پذیری (Scalability) یکی از مشخصه‌های کیفی است که در ذهن طراح نقش می‌بندد و هنگام توصیف سیستم، به مقیاس‌پذیری بالای آن اشاره می‌شود.سبک‌های معماری (Architectural Styles)سبک‌های معماری به عنوان مجموعه‌ای از اجزا (Componentها) و رابط‌ها (Connectorها) تعریف می‌شوند که ساختار کلی سیستم را شکل می‌دهند. در این سبک‌ها، رابط‌ها نحوه ارتباط بین اجزا را توصیف می‌کنند. یک سبک معماری به طور کلی واژگانی از انواع اجزا و رابط‌ها و مجموعه‌ای از محدودیت‌ها برای ترکیب آن‌ها فراهم می‌کند. برای مثال، معماری میکروسرویس‌ها (Microservices Architecture) از مجموعه‌ای از اجزا و روابطی تشکیل شده که مشخصه‌های خاصی را پوشش می‌دهند.به طور کلی، سبک‌های معماری، راه‌حل‌های عمومی و تکرارپذیر در سطح ساختار معماری سیستم هستند. به عنوان نمونه، سبک (Pipes &amp; Filters) مجموعه‌ای از اجزا و روابط آن‌ها را در قالب یک راه‌حل برای حل مسائل مختلف ارائه می‌دهد و شامل مشخصه‌های کیفی مرتبط می‌باشد.الگوهای معماری (Architectural Patterns)الگوها در تمام سطوح طراحی به کار می‌روند و برای حل مشکلات تکرارپذیر، هسته اصلی یک راه‌حل را ارائه می‌دهند. در طراحی معماری، الگوها نیز به عنوان راه‌حل‌های استاندارد شناخته می‌شوند. برای مثال، الگوی معماری MVC، به دلیل اینکه ساختار کلی سیستم را تعیین نمی‌کند و بیشتر به لایه‌ی ارائه (Presentation Layer) ساختار می‌بخشد، یک الگوی معماری محسوب می‌شود. الگوهای دیگری نظیر CQRS و API Gateway نیز نمونه‌هایی از الگوهای معماری هستند. هدف اصلی از الگوهای معماری، ایجاد یک زبان مشترک و استاندارد برای طراحی سیستم‌ها است.تاکتیک‌های معماری (Architectural Tactics)در بررسی مشخصه‌های کیفی، مبحث سناریوهای مشخصه‌های کیفی (Quality Attribute Scenarios) برای ارزیابی این مشخصه‌ها مطرح می‌شود. این سناریوها شامل پارامترهایی نظیر محرک (Stimulus)، منبع محرک (Stimulus Source)، و پاسخ (Response) سیستم می‌باشند. به عنوان مثال، در سناریوی دسترسی‌پذیری (Availability)، &quot;خرابی&quot; (Fault) می‌تواند یک محرک باشد که منجر به واکنش سیستم در قالب شناسایی یا رفع خرابی می‌شود.یک تاکتیک در حقیقت یک تصمیم طراحی است که بر دستیابی به پاسخ مشخصه کیفی تاثیر می‌گذارد. برای مثال، در دستیابی به دسترسی‌پذیری بالا، راه‌حل‌هایی مانند ایجاد کلاستر، سیستم جایگزین، شناسایی خطا، چک سلامتی (Health Check)، و مانیتورینگ به عنوان تاکتیک‌های مناسب استفاده می‌شوند. یا برای رسیدن به پایداری (Resiliency)، از تکنیک Circuit Breaker می‌توان به عنوان یک تاکتیک استفاده کرد.جمع‌بندیمعماری نرم‌افزار شامل تصمیمات طراحی کلیدی است که چارچوبی برای دستیابی به مشخصه‌های کیفی ایجاد می‌کند. این چارچوب‌ها به کمک سبک‌ها، الگوها، و تاکتیک‌ها شکل می‌گیرند و راهنمایی جامع برای طراحی سیستم‌های پایدار، کارآمد، و باکیفیت ارائه می‌دهند.</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Tue, 29 Aug 2023 16:25:50 +0330</pubDate>
            </item>
                    <item>
                <title>Domain Driven Design - بخش هشتم</title>
                <link>https://virgool.io/@mohsen-farokhi/domain-driven-design-%D8%A8%D8%AE%D8%B4-%D9%87%D8%B4%D8%AA%D9%85-lgi5knki51cn</link>
                <description>در این بخش، Domain Event که یکی از اجزای کلیدی DDD می باشد را بررسی می کنیم.Domain Driven Designبه طور کلی، domain event یک رویداد است که در یک domain اتفاق می افتد و این اتفاق از نگاه business مهم و تاثیر گذار می باشد. بنابراین از domain eventها، به عنوان یک ابزار کارآمد برای modeling استفاده می کنیم.در مفهوم Event Storming، از eventها به عنوان نقطه شروع اتفاقاتی که در یک domain می افتد، استفاده می شود. بنابراین domain eventها می توانند یکی از راه های شناخت سیستم باشند.یک event از نتیجه اتفاق افتادن یک command بوجود می آید. برای مثال از نتیجه PlaceOrder، یک event به نام OrderPlaced بوجود می آید. این به معنای رابطه یک به یک بین commandها و eventها نیست و ممکن است یک command باعث بوجود آمدن چند event شود.یک event دارای دو بخش raising و handling می باشد و در DDD برای آن ها class می سازیم و آن ها را model می کنیم.public class DeliveryGuaranteeFailed
{
    public DeliveryGuaranteeFailed(OrderForDelivery order)
    {
        Order = order;
    }
    public OrderForDelivery Order { get; private set; }
}موضوع دیگری که در مورد eventها وجود دارد، مفهوم event sourcing است. ایده آن این است که بجای نگهداری aggregateها به شکل last state در دیتابیس، یک سری از eventها را ذخیره می کنیم. می توانید در اینجا بیشتر در مورد آن مطالعه فرمایید.در مورد eventها می تونیم دو رویکرد Sync و Async داشته باشیم. در eventهای sync، در نظر بگیرید که یک command وارد سیستم می شود، یک event اتفاق می افتد و در همان درخواست، event دریافت می شود و یک reaction در مورد آن شکل می گیرد.در eventهای async نیز در نظر بگیرید که از طریق یک command، یک event اتفاق می افتد و پاسخ بر می گردد. در نهایت از طریق مکانیزم های دیگر، یک reaction نسبت به آن event شکل می گیرد. در پیاده سازی رویکرد Sync، از روش هایی مثل استفاده از eventهای موجود در dotnet یا الگویی که به اسم Event Aggregator معروف است می توانیم استفاده کنیم. در الگوی Event Aggregator دو واحد تشکیل شده از publisherها و listenerها موجود است که از طریق event aggregator به هم مرتبط هستند. این کار در memory و در یک transaction انجام می شود. public class OrderForDelivery
{
    ...
    private IBus Bus { get; set; }
    ...
    public void ConfirmReceipt(DateTime timeThatPizzaWasDelivered)
    {
        if (Status != FoodDeliveryOrderSteps.Delivered)
        {
            TimeThatPizzaWasDelivered = timeThatPizzaWasDelivered;
            Status = FoodDeliveryOrderSteps.Delivered;
            if (DeliveryGuaranteeOffer.IsNotSatisfiedBy(TimeOfOrderBeingPlaced, TimeThatPizzaWasDelivered))
            {
                Bus.InMemory.Raise(new DeliveryGuaranteeFailed(this));
            }
       }
    }
 ...
}در این مثال، در کلاسی تحت عنوان OrderForDelivery و در متد Confirm، یک raise ،event شده است.در بخشی دیگری، یک نفر به این event گوش می دهد و زمانی که event اتفاق می افتد، یک reaction به آن نشان می دهد.public void Confirm&#40;DateTime timeThatPizzaWasDelivered, Guid orderId&#41;
{
    using(DomainEvents.Register&lt;DeliveryGuaranteeFailed&gt;(onDeliveryFailure))
    {
        var order = orderRepository.FindBy(orderId);
        order.ConfirmReceipt(timeThatPizzaWasDelivered);
    }
}
private void onDeliveryFailure(DeliveryGuaranteeFailed evnt)
{
    // handle internal event and publish external event
    bus.Send(new RefundDueToLateDelivery() { OrderId = evnt.Order.Id });
}در این مثال یک event داخلی دریافت شده و تبدیل به یک event خارجی شده است. دو موضوع کلی در مورد domain eventها وجود دارد. یکی Technical Detailهایی که بر روی domain eventها ممکن است قرار بگیرد. برای مثال، یک aggregate یا value object بر روی domain event قرار بگیرد. و موضوع بعدی domain eventهایی هستند که نمی خواهیم به همان شکل در اختیار دیگران قرار دهیم. برای مثال ممکن است نخواهیم به کاربر اطلاع دهیم که پسوردش عوض شده است و یا سه domain event را در قالب یک مجموعه اطلاع دهیم. اگر objectهای domain را روی domain eventها قرار می دهیم، محدوده این domain eventها نباید از این BC خارج شود. در مقابل external eventها وجود دارند که کاربری آن ها برای BCهای دیگر می باشد.</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Sun, 13 Nov 2022 16:42:58 +0330</pubDate>
            </item>
                    <item>
                <title>الگوی Observer</title>
                <link>https://virgool.io/@mohsen-farokhi/%D8%A7%D9%84%DA%AF%D9%88%DB%8C-observer-zkvhh538wkrp</link>
                <description>الگوی طراحی Observer، وابستگی یک به چند را بین objectها تعریف می کند به طوری که وقتی یک object تغییر حالت می دهد، تمام وابسته های آن به طور خودکار مطلع می شوند.الگوی Observerیک sensor را در نظر بگیرید که در نقش subject، دمای یک قطعه سخت افزاری را بر می گرداند و قصد داریم خروجی آن را در observerهای مختلف به اشکال متفاوت نمایش دهیم.بنابراین یک کلاس sensor برای این جریان ایجاد می کنیم.public class Sensor
{
    public int CurrentTemperature { get; private set; }
    public void StartDetecting()
    {
        Task.Factory.StartNew(() =&gt;
        {
            var random = new Random();
            while (true)
            {
                CurrentTemperature = random.Next(-10, 50);
            }
        });
    }
}قصد داریم بدون اینکه sensor به observerها وابستگی داشته باشد، در مورد تغییرات دما اطلاع داشته باشیم.برای این کار نیاز به تعریف چند abstraction داریم که در دات نت به نام های IObservable و IObserver شناخته می شوند. observer کسی است که notify می شود و برای اطلاع دادن تغییرات، به یک observable ارسال می شود. public interface IObserver&lt;T&gt;
{
    void Notify(T value);
}

public interface IObservable&lt;T&gt;
{
    void Subscribe(IObserver&lt;T&gt; observer);
}دو کلاس observer ایجاد می کنیم که در لحظه تغییر دما، مقدار را به طریقی نمایش می دهند.public class NumericDisplay : IObserver&lt;int&gt;
{
    public void Notify(int value)
    {
        Console.WriteLine(value);
    }
}

public class Gauge : IObserver&lt;int&gt;
{
    public void Notify(int value)
    {
        Debug.WriteLine($&amp;quot------{value}------&amp;quot);
    }
}در کلاس sensor که یک observable است، observerها می تونند از طریق متد Subscribe به آن وصل شوند و زمانی که تغییر اتفاق می افتد، observable آن ها را notify می کند.public class Sensor : IObservable&lt;int&gt;
{
    private IList&lt;IObserver&lt;int&gt;&gt; _observers = 
        new List&lt;IObserver&lt;int&gt;&gt;();

    public void Subscribe(IObserver&lt;int&gt; observer)
    {
        _observers.Add(observer);
    }

    public int CurrentTemperature { get; private set; }
    public void StartDetecting()
    {
        Task.Factory.StartNew(() =&gt;
        {
            var random = new Random();
            while (true)
            {
                CurrentTemperature = random.Next(-10, 50);

                foreach (var observer in _observers)
                {
                    observer.Notify(CurrentTemperature);
                }
            }
        });
    }
}اتفاقی که می افتد، decoupling بین observable و observerها می باشد. subject اتفاق را به بقیه خبر می دهد ولی در مورد observerها اطلاعی ندارد. var sensor = new Sensor();
sensor.Subscribe(new NumericDisplay());
sensor.Subscribe(new Gauge());
sensor.StartDetecting();
Console.ReadLine();دو مفهوم Push Model و  Pull Model در پیاده سازی observer وجود دارد که در مورد پروتکل بین observable و observer صحبت می کند. با فرض داشتن دو کامپوننت A و B، دو رویکرد کلی می تونیم در مورد ارتباط بین آن ها داشته باشیم. اگر A را به عنوان subject در نظر بگیریم، یک رویکرد این است که تغییرات از آن push شود و رویکرد دیگر این است که B به عنوان observer، از طریق pull کردن به داده ها برسد.در الگوی observer نیز دو نوع پیاده سازی push model و pull model وجود دارد. رویکردی که در مثال بالا ملاحظه کردید از نوع push model بود. به این صورت که subject عنوان می کند که این تغییر اتفاق افتاده است. در push model، بخش subject داده بیشتری در مورد تغییرات دارد. به این صورت که observerهایی هستند که مشخصا به این مقدار نیاز دارند. بنابراین در pull model، وابستگی subject به dependencyها کمتر است. موضوعی که وجود دارد، در push model مفهوم reusability را از دست می دهیم. در مثال بالا فقط یک عدد را اطلاع رسانی می کنیم و ابعاد دیگری از آن نمی توانیم داشته باشیم. در pull model، فهمیدن اینکه چه چیزی تغییر کرده است، گاهی می تواند مساله ایجاد کند.در استفاده از push model با توجه به مساله، وجود dependency به observerها می تواند مشکل ساز شود. در مساله sensor این موضوع وجود ندارد. به این دلیل که ماهیت sensor یک عدد است.در مورد pull model، می تونیم کل object را به observerها notify کنیم و observerها تصمیم می گیرند که چه دیتایی را از آن بخوانند. به طور خلاصه، observerها در اینجا variability هستند که نسبت به تغییرات reaction نشان می دهند. و change notification و subject که به عنوان پروتوکل است نیز commonality ما می باشد.</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Sun, 23 Oct 2022 10:24:02 +0330</pubDate>
            </item>
                    <item>
                <title>الگوی Chain of Responsibility</title>
                <link>https://virgool.io/@mohsen-farokhi/%D8%A7%D9%84%DA%AF%D9%88%DB%8C-chain-of-responsibility-vpflmhucdizj</link>
                <description>الگوی chain of responsibility، به تفکیک ارسال کننده و دریافت کننده یک پیام می پردازد. به این شکل که به objectهای مختلف امکان پردازش آن پیام را می دهد.در واقع، با تشکیل یک زنجیره از آبجکت هایی که قابلیت handle کردن یک درخواست را دارند و ارسال درخواست به آبجکت اول و سپس دست به دست شدن آن و احتمالا برگشت یک پاسخ به client، می تونیم به هدفی که داریم برسیم.Chain of Responsibilityمفهومی به نام Implicit Receiver وجود دارد که در برخی از الگوها از جمله chain of responsibility دیده می شود. زمانی که یک client، درخواست انجام کاری را به یک object می دهد، اگر client این دانش را داشته باشد که چه کلاسی قرار است جواب را به آن برگرداند، به آن explicit receiver گفته می شود. در implicit receiver به این صورت است که client اطلاعاتی در مورد receiver ندارد و متوجه این نمی شود که چه کسی آن کار را انجام می دهد.  سه المان اصلی Client, Handler, Concrete Handler در این الگو وجود دارد. وجود successor به این معنی است که هر handler می تواند به handler بعدی برای ارسال پیام، point داشته باشد.جزء Handler، یک abstraction می باشد که برای مثال class زیر را برای آن در نظر بگیرید.public abstract class Handler&lt;TRequest, TResponse&gt;
{
    protected Handler&lt;TRequest, TResponse&gt; Successor;
    public void SetSuccessor(Handler&lt;TRequest, TResponse&gt; successor)
    {
        Successor = successor;
    }
    public abstract TResponse HandleRequest(TRequest request);
}به عنوان concrete handlers، برای مثال پیاده سازی های زیر را در نظر بگیرید. public class TrimHandler : Handler&lt;string, string&gt;
{
    public override string HandleRequest(string request)
    {
        request = request.Trim();
        if (Successor != null)
            return Successor.HandleRequest(request);
        else
            return request;
    }
}public class UppercaseHandler : Handler&lt;string, string&gt;
{
    public override string HandleRequest(string request)
    {
        request = request.ToUpper();
        if (Successor != null)
            return Successor.HandleRequest(request);
        else
            return request;
    }
}به این ترتیب این قابلیت در client وجود دارد که به طریقی یک abstract از handlerها دریافت شود و ازین طریق کاری را انجام می دهد.var str = &amp;quot test &amp;quot
var handler = GetHandler();
var result = handler.HandleRequest(str);

static Handler&lt;string,string&gt; GetHandler()
{
    var trimHandler = new TrimHandler();
    var upperHandler = new UppercaseHandler();
    trimHandler.SetSuccessor(upperHandler);
    return trimHandler;
}بنابراین، زمانی که ممکن است بیش از یک object، درخواستی را دریافت کنند و پردازشی انجام دهند و همچنین مشخص نیست که در مورد کدام یک از handlerها صحبت می کنیم، می توانیم از این الگو استفاده کنیم.در این الگو به طور مشخصی بین ارسال کننده و دریافت کننده، یک decoupling وجود دارد. همچنین وارد کردن یک ساختار dynamic که می تواند در runtime قابل تغییر باشد، به ما اجازه می دهد که یک زنجیره از handlerها ایجاد کنیم و درخواست را برای پردازش، داخل این زنجیره قرار دهیم. موضوع successor نهایی، می تواند در این الگو مساله ساز باشد. کسانی که درگیر این موضوع هستند، به نحوی در مورد نبود successor نگرانی دارند.یک راه حل می تواند استفاده از null object باشد که یک رفتار خنثی انجام می دهد.برای مثال در این مساله می توینم به این شکل یک null object ایجاد کنیم و در handlerها، درگیر چک کردن null بودن successor نباشیم.public class NullHandler : Handler&lt;string, string&gt;
{
    public override string HandleRequest(string request)
    {
        return request;
    }
}ساختن یک chain، معمولا با پیچیدگی هایی همراه می باشد. برای این موضوع می تونیم از الگوی builder استفاده کنیم.public class ChainBuilder&lt;TRequest, TResponse&gt;
{
    private readonly IList&lt;Handler&lt;TRequest, TResponse&gt;&gt; _handlers =
        new List&lt;Handler&lt;TRequest, TResponse&gt;&gt;();

    public ChainBuilder&lt;TRequest, TResponse&gt; With&lt;THandler&gt;()
        where THandler : Handler&lt;TRequest, TResponse&gt;, new()
    {
        var handler = new THandler();
        _handlers.Add(handler);
        return this;
    }
    public Handler&lt;TRequest, TResponse&gt; Build()
    {
        _handlers.Aggregate((a, b) =&gt;
        {
            a.SetSuccessor(b);
            return b;
        });
        return _handlers.First();
    }
}static Handler&lt;string, string&gt; GetHandler()
{
    var handler = new ChainBuilder&lt;string, string&gt;()
        .With&lt;TrimHandler&gt;()
        .With&lt;UppercaseHandler&gt;()
        .With&lt;NullHandler&gt;()
        .Build();

    return handler;
}در مقایسه با کدهای قبلی، فارغ از ساده کردن مساله ساخت، می تواند readability را نیز به همراه داشته باشد. به طور خلاصه، موضوع receiver و handler به عنوان کسی که یک کار را انجام می دهد و همچنین ترتیب آن ها را به عنوان variability در نظر می گیریم و شناختن concept آن handler توسط client را به عنوان commonality در نظر می گیریم.</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Thu, 13 Oct 2022 15:28:15 +0330</pubDate>
            </item>
                    <item>
                <title>Domain Driven Design - بخش هفتم</title>
                <link>https://virgool.io/@mohsen-farokhi/domain-driven-design-%D8%A8%D8%AE%D8%B4-%D9%87%D9%81%D8%AA%D9%85-aydzobxt5tjv</link>
                <description>در این بخش و در ادامه الگوهای کاربردی، به الگوی Decorator و ترکیب آن با Command pattern می پردازیم و موضوع Framework را بررسی می کنیم.Domain Driven Designجمله &quot;Favor Composition Over Inheritance&quot; را در قوانین طراحی بارها شنیده ایم که نشان از برتری composition نسبت به inheritance دارد. اما چه محدودیتی در inheritance می تواند وجود داشته باشد؟مفهوم inheritance، به صورت static و compile time اتفاق می افتد که می تواند بزرگترین محدودیت آن باشد. محدودیت های دیگری مانند عمق ارث بری وجود دارد که ممکن است باعث ایجاد ساختارهای شکننده شود. الگوی decorator، به اضافه کردن یک رفتار به یک object به صورت dynamic می پردازد و می تواند یک جایگزین مناسب برای مفهوم subclassing باشد.decoratorدر این دیاگرام، یک component را ملاحظه می کنید که یک abstraction از کلاسی است که قرار است decorate شود و رفتاری به آن اضافه شود. جزء دیگری به نام decorator را ملاحظه می کنید که از component ارث بری کرده است و یک instance نیز از آن دارد.برای مثال یک abstraction با نام IWriter و یک پیاده سازی به عنوان concrete component برای آن در نظر می گیریم.public interface IWriter
{
    void Write();
}

public class Writer : IWriter
{
    public void Write()
    {
        Console.WriteLine(&amp;quotI&#039;m writing&amp;quot);
    }
}از طرفی یک کلاس به عنوان decorator داریم که از component ارث بری می کند و یک instance از آن را نیز در اختیار دارد.public class DashSeparatorDecotator : IWriter
{
    private readonly IWriter _writer;
    public DashSeparatorDecotator(IWriter writer)
    {
        _writer = writer;
    }
    public void Write()
    {
        Console.WriteLine(&amp;quot--------&amp;quot);
        _writer.Write();
        Console.WriteLine(&amp;quot--------&amp;quot);
    }
}در سطح client، با فرض این که Writer به یک طریقی به دست client می رسد، می تونیم آن را به این شکل در نظر بگیریم.IWriter writer = CreateWriter();
writer.Write();

public IWriter CreateWriter()
{
    return new DashSeparatorDecotator(new Writer());
}از آنجایی که client فقط interface را می شناسد، قابلیت این را داریم که آن را به بینهایت شکل درست کنیم و به دست client برسانیم. با فرض اینکه یک decorator دیگر می تونیم داشته باشیم.public class StarDecorator : IWriter
{
    private readonly IWriter _writer;
    public StarDecorator(IWriter writer)
    {
        _writer = writer;
    }
    public void Write()
    {
        Console.WriteLine(&amp;quot********&amp;quot);
        _writer.Write();
        Console.WriteLine(&amp;quot********&amp;quot);
    }
}از آنجایی که با یک رابطه ارث بری طرف نیستیم، می تونیم به صورت dynamic، رفتار را کم یا زیاد کنیم.public IWriter CreateWriter()
{
    IWriter writer = new Writer();

    Console.WriteLine(&amp;quotInsert dashes?&amp;quot);
    var dashResponse = Console.ReadLine();

    if (dashResponse == &amp;quoty&amp;quot)
        writer = new DashSeparatorDecotator(writer);

    Console.WriteLine(&amp;quotInsert star?&amp;quot);
    var starResponse = Console.ReadLine();

    if (starResponse == &amp;quoty&amp;quot)
        writer = new StarDecorator(writer);

    return writer;
}در ترکیب decorator و command pattern، از آنجایی که command hanlderها دارای interface یکسانی هستند، براحتی می تونیم decoratorهای متفاوتی را بنویسیم.public class LoggingCommandHandlerDecorator&lt;T&gt; : ICommandHandler&lt;T&gt; 
    where T : ICommand
{
    private readonly ICommandHandler&lt;T&gt; _handler;
    public LoggingCommandHandlerDecorator(ICommandHandler&lt;T&gt; handler)
    {
        _handler = handler;
    }

    public void Handle(T command)
    {
        Console.WriteLine($&amp;quotCommand stated! {typeof(T).Name}&amp;quot);
        _handler.Handle(command);
        Console.WriteLine($&amp;quotCommand {typeof(T).Name} executed successfully&amp;quot);
    }
}از این طریق می تونیم با در نظر گرفتن Open-Closed Principle در اصول SOLID، یک رفتار را به تمام command handlerها اضافه کنیم. این جریان را می تونیم از طریق IOC Containerها اضافه کنیم. برای مثال در Autofac از طریق متد RegisterGenericDecorator، این امکان وجود دارد.builder.RegisterGenericDecorator        
    (typeof(LoggingCommandHandlerDecorator&lt;&gt;), typeof(ICommandHandler&lt;&gt;));در مفهوم AOP یا Aspect-oriented programming، نیازمندی هایی در نرم افزار داریم که لایه های مختلف را درگیر می کنند. برای مثال در لاگ کردن commandها، در exception handling و این گونه موارد. AOP مجموعه ای از تکنیک ها و ابزارها است که به ما کمک می کند بجای اینکه در تک تک جاها این موارد را بنویسیم، یک جا بنویسیم و در کل پروژه apply کنیم.روش هایی مثل Code Weaving, Interceptor, Decorator, Proxy و غیره، می توانند برای AOP استفاده شوند. با وارد کردن decorator به این جریان، می تونیم کارهای AOP بر روی command handler را مدیریت کنیم. Frameworkزمانی که پروژه ای را design می کنیم، یکسری طراحی انجام می دهیم که ممکن است در BCهای مختلف استفاده شوند. برای مثال قابلیت هایی که برای استفاده از command pattern اضافه کردیم، قابلیت هایی هستند که اگر قرار باشد BCهای domain centric داشته باشیم، باید در تمام آن ها این قابلیت ها را داشته باشیم. برای این که effort برنامه نویسان از نوشتن موضوعات این چنینی برداشته شود و بر روی model کردن business گذاشته شود، این نیازمندی ها را بر روی پکیج هایی می گذاریم و به شکل قابل نصب در اختیار تیم های مختلف قرار می گیرد.نکته ای که در رابطه با framework وجود دارد این است که frameworkها را برای handle کردن business طراحی نمی کنیم. framworkها برای این موضوع طراحی می شوند که یکسری طراحی و نیازمندی های کلی را در آن ها ببینیم و تمرکز برنامه نویس را به سمت business و domain model ببریم.همچنین framework باید flexibility لازم را داشته باشد و برنامه نویس را مجبور به استفاده از یک چارچوب یا ابزار خاص نکند. از frameworkهای آماده ای که برای دات نت وجود دارند، می تونیم به ASP.NET Boilerplate به عنوان نمونه اشاره کنیم. </description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Wed, 05 Oct 2022 18:16:09 +0330</pubDate>
            </item>
                    <item>
                <title>Domain Driven Design - بخش ششم</title>
                <link>https://virgool.io/@mohsen-farokhi/domain-driven-design-%D8%A8%D8%AE%D8%B4-%D8%B4%D8%B4%D9%85-nxchisptq36f</link>
                <description>در این بخش، از سری الگوهایی که کمک می کنند code base بهتری داشته باشیم، به  الگوی Command و Query و قابلیت هایی که در سطح application layer اضافه می کند، می پردازیم.Domain Driven Designاولین موضوعی که به آن می پردازیم، الگوی Command و Query است که در سطح application layer می توانیم از قابلیت های آن کمک بگیریم.متدهای لایه application را می توانیم به دو دسته تقسیم کنیم. دسته اول را Write یا Commands نامگذاری می کنیم. هدف آن، نوشتن داده ها و اعمال تغییرات است. دسته دوم را با عنوان Read یا Query می شناسیم که خواندن داده ها برای clientها را مطرح می کند.قوانینی که در بخش های قبل برای aggregateها تعیین شد، باعث می شدند که گرفتن query از سیستم دچار پیچیدگی شود. برای مثال زمانی که قانون reference کردن aggregateها با Id را مطرح می کنیم، کار در query گرفتن سخت می شود. در model driven design، روش های متفاوتی برای query گرفتن وجود دارد. یکی از روش هایی که در DDD مطرح شده است، وجود View Model یا Read Model برای خواندن داده ها است که در این تصویر یک مدل از این جریان را مشاهده می کنید.موضوعی که در command داریم، Invariant Control است. تمام مباحث مانند aggregate, entity و غیره که در بخش های قبل مطرح شدند، برای کنترل invariantهای سیستم بودند. در حالی که در query موضوعی برای کنترل invariantها نداریم. بنابراین یکی از روش هایی که وجود دارد، این است که مکانیزم command و query از هم متفاوت باشد.مفهوم CQRS یا Command Query Responsibility Segregation در سال 2010 مطرح شد و به تفکیک command و query می پردازد. برای مثال در سطح Data Modeling و در یک دیتابیس relational، داده ها را Normalize می کنیم. این کار در حوزه command پیشنهاد می شود. اما برای query، این موضوع مساله ایجاد می کند. بنابراین یکی از حوزه هایی که در آن CQRS مطرح می شود، دیتابیس است. ایده آن این است که داده ها را برای نوشتن در یک دیتابیس ذخیره کنیم و برای خواندن یک data model مناسب query داشته باشیم. سطوح دیگری از CQRS در سطح ساده تری مطرح می شوند. این موضوع که فقط در سطح مدل این کار را انجام دهیم و بصورت فیزیکی دیتابیس ها را از هم جدا نکنیم. به این معنی که برای نوشتن از یک مدل پیروی کنیم و برای خواندن از مدل دیگری که ساده تر است استفاده کنیم. الگویی که در این حوزه به نام Command pattern مطرح می شود، می تواند مزایای زیادی را به ما بدهد. زمانی که در سیستم یک command اتفاق می افتد، آن command از طریق یک interface مشترک، به دست یک command handler می رسد و از این طریق پردازش می شود.public interface ICommandHandler&lt;T&gt;
{
    void Handle(T command);
}در نظر بگیرید که به عنوان یک مشتری وارد یک رستوران می شوید. قاعدتا شما برای سفارش غذا مستقیما سراغ آشپز نمی روید و فقط پیش خدمت را می شناسید. پیش خدمت بسته به سفارش شما، سفارش را به دست یکی از آشپزها می رساند و نتیجه را برمی گرداند.در این الگو، اگر clientها را Rest در نظر بگیریم، فقط Command Bus را به عنوان یک گذرگاه می شناسند و از این طریق، commandها به command handler می رسد. اولین موضوعی که به عنوان یک مزیت مطرح می شود، این است که وابستگی ما به command hadlerها صفر می شود و این می تواند اعمال تغییرات را راحت تر کند. بنابراین side effect تغییرات در command handlerها روی clientها تاثیری نمی گذارد.موضوع دوم این است که تمام commandها از یک گذرگاه واحد عبور می کنند. مزیتی که به ما می دهد، برای مثال لاگ تمام commandها را می توانیم داشته باشیم. همچنین کنترل این موضوع که چه کسی می تواند این command را پردازش کند را داشته باشیم.موضوع سوم این است که، interface در تمام اجزایی که command را handle می کنند، یکی می شود. داشتن interface یکسان، یعنی می توانیم به یک چشم با command handlerها برخورد کنیم و همه را در قالب یک abstraction نگاه کنیم. در حوزه decoupling، فرض کنید که یک Web Tier و یک Application Tier داریم که به صورت فیزیکی از هم جدا شدند. web tier از طریق rest درخواست ها را می گیرد و بر روی یک command bus قرار می دهد. bus می تواند commandها را در یک صف قرار دهد و با این تفکیک سازی، می توانیم تعداد application tierها را بالا ببریم تا به صورت موازی به commandها گوش دهند. از command bus به عنوان گذرگاه commandها صحبت کردیم که از طریق یک متد، command را دریافت می کند. clientها فقط command و command bus را می شناسند. در این طراحی، می توانیم از یک اینترفیس به عنوان marker برای commandها استفاده کنیم. از این طریق می توانیم تایپ جنریک ها را محدود به command کنیم.public interface ICommand
{
}

public interface ICommandBus 
{     
    void Dispatch&lt;T&gt;(T command) where T : ICommand;
 }

public interface ICommandHandler&lt;T&gt;  where T : ICommand
{     
    void Handle(T command);
 }فرض کنید در application ما، یک open auction command اتفاق می افتد.public class OpenAuctionCommand : ICommand
{
    public string Product { get; set; }
    public int StartingPrice { get; set; }
    public DateTime EndDateTime { get; set; }
}  

public class OpenAuctionHandler : ICommandHandler&lt;OpenAuctionCommand&gt; 
{     
    public void Handle(OpenAuctionCommand command)     
    {
        Console.WriteLine(&amp;quotHandling Open Auction&amp;quot);
    } 
}کلاس command handler، به مشابه یکی از توابع در application service است و کار پردازش این command را انجام می دهد. در سطح client، فقط commandها و command bus را می شناسیم.var container = BuildContainer();
var bus = container.Resolve&lt;ICommandBus&gt;();

var openAuction = new OpenAuctionCommand
{
    Product = &amp;quotx&amp;quot,
    StartingPrice = 1000,
    EndDateTime = DateTime.Now.AddDays(10)
};

bus.Dispatch(openAuction);در نهایت command bus کلاس command handler را پیدا می کند و command را به آن ارسال می کند. بهترین حالتی که می توانیم برای پیاده سازی یک In-memory command bus در نظر بگیریم، استفاده از یک ابزار به عنوان IOC Container برای مثال Autofac است که بتواند برای ما Dependency Injection انجام دهد.به این صورت که به Autofac یک assembly معرفی می کنیم و از آن می خواهیم هر کلاسی که ICommandHandler را پیاده سازی کرده است را پیدا کند و register کند.private static IContainer BuildContainer()
{
    var builder = new ContainerBuilder();

    builder.RegisterAssemblyTypes(typeof(Program).Assembly)
        .As(type =&gt; type.GetInterfaces()
            .Where(interfaceType =&gt; interfaceType.IsClosedTypeOf(typeof(ICommandHandler&lt;&gt;))))
        .InstancePerLifetimeScope();

    builder.RegisterType&lt;AutofacCommandBus&gt;()
        .As&lt;ICommandBus&gt;().SingleInstance();

    return builder.Build();
}در پیاده سازی AutofacCommandBus، به Autofac LifetimeScope یک reference داریم. ICommandHanlder مربوطه را از آن دریافت می کند و متد Handle را صدا می زند.public class AutofacCommandBus : ICommandBus
{
    private readonly ILifetimeScope _scope;
    public AutofacCommandBus(ILifetimeScope scope)
    {
        _scope = scope;
    }

    public void Dispatch&lt;T&gt;(T command) where T : ICommand
    {
        var handler = _scope.Resolve&lt;ICommandHandler&lt;T&gt;&gt;();
        handler.Handle(command);
    }
}بنابراین، در حالت In-memory بهترین حالت استفاده از ابزارهای IOC Container می تواند باشد. از این جهت که در command handlerها احتمالا وابستگی وجود دارد و ساخت آن ها موضوعیت پیدا می کند. </description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Wed, 05 Oct 2022 11:41:18 +0330</pubDate>
            </item>
                    <item>
                <title>Domain Driven Design - بخش پنجم</title>
                <link>https://virgool.io/@mohsen-farokhi/domain-driven-design-%D8%A8%D8%AE%D8%B4-%D9%BE%D9%86%D8%AC%D9%85-kr48k5cnqso7</link>
                <description>در بخش پنجم از سری مطالب طراحی مبتنی بر دامنه (Domain Driven Design)، به بررسی موضوعات مرتبط با Tactical Design شامل مدلسازی (Modeling) و معماری (Architecture) می‌پردازیم. تمرکز ما در این بخش بر روی معماری‌های Domain-Centric و نحوه طراحی سیستم‌هایی است که دارای پیچیدگی‌های قابل توجه در منطق دامنه هستند.Domain Driven Designمعماری در طراحی استراتژیکدر طراحی استراتژیک، این امکان وجود دارد که در Bounded Contextهای مختلف، با توجه به نیازمندی‌ها، از معماری‌های متفاوتی استفاده کنیم. به‌عنوان مثال:اگر یک Bounded Context ساده و صرفاً شامل عملیات‌های CRUD باشد، می‌توان از طراحی ساده‌تری استفاده کرد.اما در مقابل، زمانی که با یک Context پیچیده با قوانین وابسته و متنوع، Invariant‌های متعدد و منطق‌های پیچیده مواجه هستیم، نیاز به رویکردی ساختارمندتر داریم.در این مطلب، تمرکز ما روی دسته دوم است: سیستم‌هایی با پیچیدگی بالا که نیازمند طراحی دقیق و معماری مبتنی بر دامنه هستند.معماری Domain-Centricمعماری سنتی: سه لایه‌ایدر گذشته، معماری رایج به صورت سه لایه‌ای (Presentation, Business, Data) طراحی می‌شد. در این ساختار، اغلب از الگویی به نام Transaction Script استفاده می‌شد که در آن، منطق برنامه به صورت رویه‌ای (Procedural) در قالب Serviceها پیاده‌سازی می‌شد.در این رویکرد، مدل‌های دامنه، تنها شامل داده‌ها بودند و فاقد رفتار منطقی بودند؛ به این نوع مدل‌ها Anemic Domain Model گفته می‌شود.Rich Domain Modelدر مدل دامنه‌ی غنی (Rich Domain Model)، اشیاء دامنه، هم شامل داده هستند و هم رفتارهایی که مربوط به آن داده‌هاست. ویژگی‌های این مدل:منطق کسب‌وکار درون اشیاء دامنه محصور شده (Encapsulated) است.فاقد وابستگی به فریم‌ورک‌ها و تکنولوژی‌های زیرساختی هستند.قابلیت تست بالایی دارند.از زبان دامنه (Ubiquitous Language) استفاده می‌کنند.و Application Logic در این لایه وجود ندارد.لایه‌های معماری Domain-Centricدر معماری Domain-Centric، چهار لایه‌ی اصلی در نظر گرفته می‌شود:User Interface (UI)نمایش داده‌ها و دریافت فرمان‌ها از کاربر.Application Layerمسئول اجرای Use Caseها از طریق واگذاری (Delegation) به سایر لایه‌ها. فقط orchestration انجام می‌دهد.Domain Layerقلب سیستم است؛ منطق کسب‌وکار و قوانین در این لایه پیاده‌سازی می‌شوند. کاملاً مستقل از زیرساخت و فریم‌ورک است.Infrastructure Layerمسئول تعامل با دیتابیس، سیستم فایل، لاگرها و سرویس‌های خارجی است.تفکیک منطق‌هادر طراحی صحیح، باید بین دو نوع منطق تفکیک قائل شد:Application Logic: مربوط به ترتیب و مدیریت اجرای مراحل یک Use Case.Business Logic: شامل قوانین و رفتارهای دامنه که در Domain Layer پیاده‌سازی می‌شود.مثال: هنگام ثبت پیشنهاد روی یک مزایده (Auction)، بخش‌هایی از فرایند مانند بارگذاری مزایده، بررسی باز بودن آن، مقایسه قیمت و ذخیره‌سازی پیشنهاد جدید، شامل هر دو نوع منطق می‌شوند.در تصویر زیر یک sequence diagram را مشاهده می کنید.در بالای تصویر، لایه بندی که مدنظر بوده است، مشخص شده است. همانطور که ملاحظه می کنید، در User Interface، یک TransferController قرار دارد که برای عملیات انتقال وجه، یک Application Service را فراخوانی می کند. Application Service از طریق FundsTransferService کار Orchestration را انجام می دهد و logic برنامه در Domain Layer اتفاق می افتد. بررسی وابستگی‌ها و اصل Inversionیکی از اصول کلیدی در طراحی معماری، اصل Dependency Inversion Principle (DIP) است. بر اساس این اصل:ماژول‌های سطح بالا (High-Level Modules) نباید به ماژول‌های سطح پایین وابسته باشند.هر دو باید به Abstraction وابسته باشند.مثال:فرض کنید در لایه BLL کلاس SalaryCalculationService داریم که برای محاسبه حقوق از کلاس TaxRepository در DAL استفاده می‌کند. این وابستگی به یک ماژول low level است که خلاف DIP است. راه‌حل چیست؟ تعریف یک Abstraction توسط ماژول high level و پیاده‌سازی آن در ماژول low level.پکیج BLL را یک پکیج high level در نظر می گیریم. از این نظر که به مفاهیم business نزدیک تر است و موضوعات در آن مرتبط با business هستند. به این دلیل برای ما حجم پیچیدکی و اهمیت بیشتری دارد. پکیج DAL یک low level است. از این جهت که بیشتر technical است و policyهای business در آن کمتر دیده می شود. Dependency Inversion Principleاصل dependency inversion به این شکل عمل می کند که ماژول high level، سطح abstraction را تعریف می کند و از آن استفاده می کند. ماژول low level نیز آن abstraction را پیاده سازی می کند و از این طریق جهت وابستگی تغییر می کند. در نتیجه تغییرات نیز از سمت high level module به سمت low level module حرکت می کند.معماری‌های مدرن: Hexagonal، Onion و Clean Architectureمعماری‌هایی مانند Hexagonal Architecture، Onion Architecture و Clean Architecture، همگی اهداف مشترکی را دنبال می‌کنند:استقلال از UIاستقلال از دیتابیس و سرویس‌های خارجیامکان تست آسانجداسازی concerns به‌صورت ساختارمندرعایت Dependency Rule: وابستگی‌ها فقط به سمت داخل (Domain) است.اجزای اصلی در Clean ArchitectureEntities (Domain Model)Use Cases (Application Layer)Interface Adapters (Controller, Presenter, Gateway)Frameworks &amp; Drivers (UI, DB, External Services)اصل پایداری در طراحیStability چیست؟مفهوم Stability به هزینه تغییر مربوط می‌شود. اگر یک ماژول تغییر ناپذیر یا تغییر در آن دشوار باشد، گفته می‌شود که Stable است. در طراحی خوب، باید تلاش کنیم ماژول‌های Stable وابسته به ماژول‌های Unstable نباشند.Stable Dependency Principle (SDP)کامپوننت‌هایی که Stable هستند نباید به کامپوننت‌هایی وابسته باشند که زیاد تغییر می‌کنند.Stable Abstraction Principle (SAP)کامپوننت‌های Stable باید Abstract باشند تا بتوانند قابل توسعه و تغییر باشند.نمودار Stability vs Abstractnessدر این نمودار، محل مناسب برای کامپوننت‌ها ناحیه‌ی سبز رنگ است که هم پایدار و هم abstract هستند. دو ناحیه‌ی دیگر:Zone of Pain: Stable و Concrete → تغییر سختZone of Uselessness: Instable و Abstract → بدون کاربرد واقعیپیشنهاد ساختار بهتر برای Application Layerبرای حل وابستگی‌ها در Application Layer می‌توان یک پکیج جداگانه با عنوان مثلاً Application.Contracts تعریف کرد که در آن تنها abstractionها قرار بگیرند. سپس لایه Application فقط پیاده‌سازی این قراردادها را انجام دهد. این کار باعث حفظ استقلال و انعطاف‌پذیری در طراحی می‌شود.در این بخش، تلاش کردیم تا با بررسی معماری Domain-Centric، تفکیک منطق‌های مختلف، اصول طراحی پایدار و مفاهیم مدرن معماری، دید عمیق‌تری نسبت به پیاده‌سازی سیستم‌های پیچیده کسب‌وکار ارائه دهیم. در بخش‌های بعدی، وارد جزئیات بیشتری در رابطه با پیاده‌سازی Tactical Patternهای DDD خواهیم شد.</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Sat, 01 Oct 2022 13:21:59 +0330</pubDate>
            </item>
                    <item>
                <title>Domain Driven Design - بخش چهارم</title>
                <link>https://virgool.io/@mohsen-farokhi/domain-driven-design-%D8%A8%D8%AE%D8%B4-%DA%86%D9%87%D8%A7%D8%B1%D9%85-onw7hvfa7ofg</link>
                <description>در این پست، به بررسی مفهوم Consistency در طراحی مبتنی بر DDD می‌پردازیم. انواع مختلف یکپارچگی داده‌ها، از جمله Atomic Consistency و Eventual Consistency، و تأثیر آن‌ها بر طراحی سیستم‌های نرم‌افزاری مورد بحث قرار می‌گیرند. همچنین، ارتباط این مفاهیم با قواعد ACID، BASE و CAP تحلیل شده و با ارائه یک مثال عملی از سیستم حراجی (Auction)، نحوه پیاده‌سازی این اصول در یک مدل دامنه نشان داده می‌شود.Domain Driven Designدر طراحی سیستم‌های نرم‌افزاری، حفظ یکپارچگی داده‌ها (Consistency) یکی از چالش‌های اساسی است. در Domain Driven Design، این مفهوم نقش کلیدی در طراحی مدل دامنه و تعیین مرزهای Aggregateها دارد. در این پست، به بررسی دو رویکرد اصلی در یکپارچگی داده‌ها می‌پردازیم:Atomic Consistency Eventual Consistency انواع ConsistencyAtomic Consistencyدر این نوع یکپارچگی، عملیات به صورت Atomic انجام می‌شود، یعنی یا تمام مراحل با موفقیت اجرا می‌شوند یا هیچ‌کدام اجرا نمی‌شوند. مثال کلاسیـک این مفهوم، انتقال پول بین دو حساب است:اگر مبلغی از حساب A کم شود، باید حتماً به حساب B اضافه شود.در صورت بروز خطا در هر مرحله، تمام تغییرات Rollback می‌شوند.این رویکرد مبتنی بر اصول ACID (Atomic Consistent Isolated Durable) است.Eventual Consistencyدر این مدل، سیستم ممکن است در لحظه‌ای خاص ناسازگار باشد، اما در نهایت به حالت یکپارچه می‌رسد. برای مثال:در انتقال پول، ممکن است مبلغ از حساب A کم شود اما با تأخیر به حساب B اضافه شود.این روش در سیستم‌های توزیع‌شده و NoSQL رایج است و مبتنی بر قاعده BASE (Basic Availability Soft-state Eventual consistency) است.قاعده CAP و تأثیر آن بر Consistencyقاعده CAP بیان می‌کند که در یک سیستم توزیع‌شده، تنها دو مورد از سه ویژگی زیر را می‌توان همزمان تضمین کرد:Consistency (یکپارچگی)Availability (در دسترس بودن)Partition Tolerance (تحمل تقسیم‌بندی)مثال:فرض کنید یک دیتابیس به سه Node تقسیم شده است:اگر Consistency را انتخاب کنیم، سیستم باید منتظر Sync شدن تمام Nodeها بماند (کاهش Availability).اگر Availability را انتخاب کنیم، سیستم داده‌ی موجود را فوراً برمی‌گرداند (کاهش Consistency).اگر Partition Tolerance را کنار بگذاریم، سیستم دیگر توزیع‌شده نیست.کاربرد Consistency در Domain Driven Designدر DDD، مفهوم Aggregateها مرزهای تراکنشی را تعیین می‌کنند:درون یک Aggregate: از Transactional Consistency استفاده می‌شود.بین Aggregateها: از Eventual Consistency استفاده می‌شود.مثال عملی: سیستم حراجی (Auction)در یک سیستم حراجی، موجودیت‌های اصلی شامل:Auction (حراج)Bid (پیشنهاد)WinningBid (پیشنهاد برنده)Seller (فروشنده)Bidder (پیشنهاددهنده)Product (کالا) و Invariantهای سیستم شامل:قیمت شروع باید بزرگتر از صفر باشد.پیشنهاد اول باید بیشتر از قیمت شروع باشد.پیشنهادهای بعدی باید از آخرین پیشنهاد بیشتر باشند.پیشنهادها فقط روی حراج‌های باز ثبت شوند.طراحی Aggregate:موجودیت‌های Auction و WinningBid در یک Aggregate قرار می‌گیرند تا Transactional Consistency را تضمین کنند.موجودیت‌ Bid (تاریخچه پیشنهادها) به صورت Eventual Consistency از طریق Eventهای Async ذخیره می‌شود.مدیریت Eventual Consistencyدر این سیستم، ممکن است بین ذخیره‌ی WinningBid و Bid تأخیر وجود داشته باشد. اما از آنجا که کاربران بیشتر به آخرین پیشنهاد توجه دارند، این تأخیر مشکلی ایجاد نمی‌کند. راه‌حل‌های ساده مانند نمایش زمان آخرین به‌روزرسانی می‌تواند شفافیت را افزایش دهد.نتیجه‌گیریانتخاب نوع Consistency به نیازهای کسب‌وکار و معماری سیستم بستگی دارد:مفهوم Atomic Consistency برای عملیات‌های بحرانی مانند تراکنش‌های بانکی مناسب است.مفهوم Eventual Consistency در سیستم‌های توزیع‌شده و NoSQL کاربرد دارد.در DDD، طراحی Aggregateها نقش کلیدی در تعیین مرزهای تراکنشی و مدیریت یکپارچگی داده‌ها دارد.</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Fri, 30 Sep 2022 12:00:22 +0330</pubDate>
            </item>
                    <item>
                <title>Domain Driven Design - بخش سوم</title>
                <link>https://virgool.io/@mohsen-farokhi/domain-driven-design-%D8%A8%D8%AE%D8%B4-%D8%B3%D9%88%D9%85-hcvpnrlyvrxd</link>
                <description>Tactical Design و Model-Driven Developmentدر بخش‌های قبل، به مباحث Strategic Design پرداختیم و اهمیت ارتباط مستمر با Domain Expertها برای دستیابی به دانش دامنه و ایجاد Ubiquitous Language را بررسی کردیم. همچنین، نحوه شناسایی دامنه‌ها، مرزهای آن‌ها و ارزش‌گذاری دامنه‌ها را مورد بحث قرار دادیم. در این بخش، به مباحث Tactical Design و Model-Driven Development می‌پردازیم.Domain Driven Designمفهوم Tactical Design در Domain-Driven Designدر مباحث Tactical Design، پس از شناسایی Subdomainها و Bounded Contextها (BC)، به بررسی ساختار این BCها می‌پردازیم. در DDD، اصول و روش‌هایی وجود دارد که به ما کمک می‌کند تا دامنه را به‌ صورت مؤثر مدل‌سازی کنیم. این اصول در قالب Building Blockها یا اجزای سازنده‌ی مدل دامنه ارائه می‌شوند.در تصویر زیر، یک state diagram را می بینیم که از لحظه بوجود آمدن یک domain object تا از بین رفتن آن را مدل می کند.این کاری است که بصورت نرمال انجام می دهیم و احتمالا، develop در این حوزه را تجربه کرده ایم. Domain Modelمفهوم Domain Model یک مدل نرم‌افزاری است که بر اساس Problem Domain ایجاد می‌شود. این مدل قرار است نیازمندی‌های موجود در دامنه را برآورده کند. در زبان‌های شیءگرا، این مدل معمولاً به‌صورت کلاس‌های مختلف پیاده‌سازی می‌شود. به مجموعه‌ی این کلاس‌ها که با هم کار می‌کنند، Domain Model گفته می‌شود.در راستای مدل کردن در DDD، با اجزای سازنده domain model یا Building Blockها روبرو می شویم.Entityاولین جزء سازنده‌ی مدل دامنه، Entity است. Entityها اجزایی از مدل دامنه هستند که از طریق یک Identifier شناسایی و ردیابی می‌شوند. این شناسه به ما امکان می‌دهد تا به Entityها اشاره کنیم و آن‌ها را در طول چرخه‌ی حیاتشان دنبال کنیم.Value Objectدر مقابل Entityها، Value Objectها قرار دارند. Value Objectها اجزایی از مدل دامنه هستند که فاقد شناسه‌ی منحصر به فرد بوده و از طریق مقادیرشان شناسایی می‌شوند. به عنوان مثال، یک شیء به نام Money که دارای ویژگی‌های Amount و Currency است، یک Value Object محسوب می‌شود. اگر دو شیء Money با مقادیر یکسان داشته باشیم، این دو شیء از نظر ما برابر هستند.ویژگی های value objectIdentity-Less: به این معنا که Value Objectها فاقد شناسه‌ی منحصر به فرد هستند.Attribute-Based Equality: مقایسه‌ی دو Value Object بر اساس مقادیر آن‌ها انجام می‌شود.Immutable: به این صورت که Value Objectها معمولاً تغییرناپذیر هستند. برای تغییر آن‌ها، یک شیء جدید ایجاد می‌شود.Combinable: برخی Value Objectها می‌توانند با هم ترکیب شوند و یک شیء جدید ایجاد کنند.Self-Validation: به این معنی که Value Objectها خود را اعتبارسنجی می‌کنند و نمی‌توان آن‌ها را در حالت نادرست قرار داد.Invariantدر مدل‌سازی دامنه، Invariantها شرایطی هستند که باید همیشه برقرار باشند. نقض این شرایط می‌تواند منجر به مشکلات جدی در مدل کسب‌وکار شود. به عنوان مثال، در یک سیستم حراجی، StartingPrice باید بزرگ‌تر از صفر باشد و Bidهای جدید باید از WinningBid بزرگ‌تر باشند.Aggregateمفهوم Aggregate یک الگوی طراحی در DDD است که به ما کمک می‌کند تا گراف‌های بزرگ از اشیاء را به اجزای مستقل و کوچک‌تر تقسیم کنیم. هر Aggregate شامل یک Aggregate Root است که به عنوان نماینده‌ی آن Aggregate عمل می‌کند.قوانین AggregateAggregate Root باید یک Entity باشد: Aggregate Root باید دارای شناسه‌ی منحصر به فرد باشد.ارجاع به Aggregate Root: تنها می‌توانیم به Aggregate Root از خارج ارجاع دهیم و نمی‌توانیم به اجزای داخلی آن اشاره کنیم.Global Identity: Aggregate Root باید دارای شناسه‌ی منحصر به فرد در سطح global باشد.Transaction Boundary: هر Aggregate به عنوان یک مرز تراکنش در نظر گرفته می‌شود.حذف Aggregate: حذف یک Aggregate باعث حذف تمام اجزای آن می‌شود.Commit Aggregate: هنگام commit کردن یک Aggregate، تمام Invariantها باید برقرار باشند.نتیجه‌گیریدر این بخش، به مباحث Tactical Design در Domain-Driven Design پرداختیم و اجزای سازنده‌ی مدل دامنه مانند Entity، Value Object، Invariant و Aggregate را بررسی کردیم. این مفاهیم به ما کمک می‌کنند تا مدل‌های دامنه‌ای ایجاد کنیم که هم از نظر ساختاری منسجم باشند و هم نیازمندی‌های کسب‌وکار را به‌طور کامل پوشش دهند. در بخش‌های آینده، به بررسی بیشتر این مفاهیم و روش‌های پیاده‌سازی آن‌ها خواهیم پرداخت.به طور خلاصه، در حوزه‌ی Tactical Design، تمرکز از &quot;فضای مسئله&quot; به &quot;فضای راه‌حل&quot; منتقل می‌شود؛ یعنی وارد بخش فنی‌تری از مدل‌سازی دامنه می‌شویم که در آن ابزارها و الگوهایی برای پیاده‌سازی دقیق‌تر مفاهیم کسب‌وکار فراهم شده‌اند.در این رویکرد، مفاهیم کلیدی و بنیادینی وجود دارد که عبارتند از:✅ Entityموجودیتی است با هویت یکتا که در طول زمان می‌تواند تغییر کند اما همچنان از طریق شناسه‌اش قابل ردیابی است. هویت هر Entity معمولاً با یک کلید یکتا مانند Id مشخص می‌شود.✅ Value Objectدر مقابل، Value Objectها فاقد هویت مستقل هستند و بر اساس مقادیرشان شناسایی می‌شوند. آن‌ها اغلب:Immutable هستند (پس از ساخت، مقادیرشان قابل تغییر نیست)دارای قابلیت Self-validation می‌باشند (خودشان اعتبارسنجی می‌کنند که در حالت معتبر ساخته شوند)✅ Aggregate و Aggregate Rootهر Aggregate مجموعه‌ای از Entityها و Value Objectهاست که به‌عنوان یک واحد منطقی و یکپارچه در نظر گرفته می‌شود.در هر Aggregate، باید یک Aggregate Root وجود داشته باشد که از نوع Entity است و به‌عنوان نقطه‌ی ورود اصلی برای دسترسی و مدیریت کل Aggregate عمل می‌کند.ویژگی‌های مهم Aggregate شامل:مرز تراکنشی (Transactional Boundary): تمام تغییرات باید از طریق Aggregate Root و در یک محدوده‌ی تراکنشی انجام شوند.Invariants: شرایط و قواعدی که باید در سرتاسر عمر Aggregate برقرار باشند.وابستگی ضعیف به سایر Aggregateها: ارتباط میان Aggregateها فقط از طریق شناسه (Id) انجام می‌شود، نه نگهداری مستقیم مرجع به Entityهای دیگر.حذف یکپارچه: در صورتی که Aggregate Root حذف شود، تمام اجزای داخلی آن نیز حذف خواهند شد.</description>
                <category>Mohsen Farokhi - محسن فرخی</category>
                <author>Mohsen Farokhi - محسن فرخی</author>
                <pubDate>Thu, 29 Sep 2022 10:53:53 +0330</pubDate>
            </item>
            </channel>
</rss>