<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های محمد میرزائیان</title>
        <link>https://virgool.io/feed/@mohammad.mirzaeyan</link>
        <description>برنامه نویس جاوا</description>
        <language>fa</language>
        <pubDate>2026-06-16 08:43:04</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/1095/avatar/emupGT.png?height=120&amp;width=120</url>
            <title>محمد میرزائیان</title>
            <link>https://virgool.io/@mohammad.mirzaeyan</link>
        </image>

                    <item>
                <title>بهترین روش های پیاده سازی تاییدات در موتور فرآیندی</title>
                <link>https://virgool.io/@mohammad.mirzaeyan/%D8%A8%D9%87%D8%AA%D8%B1%DB%8C%D9%86-%D8%B1%D9%88%D8%B4-%D9%87%D8%A7%DB%8C-%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-%D8%AA%D8%A7%DB%8C%DB%8C%D8%AF%D8%A7%D8%AA-%D8%AF%D8%B1-%D9%85%D9%88%D8%AA%D9%88%D8%B1-%D9%81%D8%B1%D8%A2%DB%8C%D9%86%D8%AF%DB%8C-riwdgif9gnnb</link>
                <description>یکی از موضوعاتی که همیشه وقتی توی پروژه های مختلف از موتورهای فرآیندی استفاده می کردیم ذهن منو درگیر می کرد این بود که چطور روال تاییدات بین مدیران یا کارشناسان را پیاده سازی کنیم . و خوب البته ایده ها و چالش های مختلفی در کنار این موضوع مطرح بودند .  که این چالش ها را از اونجایی که ممکنه برای شما هم جالب باشه و یا اینکه روش های حل مختلفی رو براش داشته باشید اینجا لیست می کنم : ۱- چالش اول ، امکان داینامیک بودن روال تاییدات بود ، یعنی کاربر از طریق سامانه این قابلیت را در اختیار داشته باشد که خودش روال تاییدات رو تنظیم کند . ۲- چیدمان بر اساس چارت سازمانی ، هر سازمانی بر اساس چارت خود و با توجه به ادبیات سازمانی خود روال تاییدات را تعیین می کند ۳- امکان ویرایش اطلاعات رکورد مربوط به کسب وکار در حین روال تاییدات ، در برخی موارد حتی تاریخچه ویرایش رکورد ها توسط کاربران نیز مهم است . از اونجایی که پرداختن به همه این موضوعات در حد یک مقاله ممکن نیست من تصمیم گرفتم توی این مقاله فقط روش هایی که برای حل چالش اول یعنی داینامیک کردن این روال تاییدات تا به حال استفاده کردم را توی این مقاله بنویسم . نکته ای که قبل از خوندن ادامه این مقاله باید در نظر بگیرید این هست که باید با مفاهیم و المنت های موجود در BPMN 2.0 آشنا باشید ، همچنین مطالب ذکر شده بر روی موتور فرآیندی Camunda پیاده سازی و تست شده است اما ممکن است مطالب عنوان شده در این مقاله بر روی سایر موتور های فرآیندی نیز قابل پیاده سازی و انجام باشد مانند Activiti و Flowable.همین طور برای تعامل با  Camunda  نیز از Integration  آن با Spring استفاده شده است . نحوه تعیین سلسله مراتب تاییدات قبل از ارائه روش های پیاده سازی فرآیند به کمک BPMN یکی از مهم ترین موضوعات امکان تعیین سلسله مراتب تاییدات توسط مدیران سیستم و یا کارشناسان ارشد سامانه است ، این موضوع را می توان با توجه به ادبیات موجود در سطح سازمان ها به روش های مختلف مدیریت کرد اما ساده ترین روش برای حل آن را می توان به کمک مدل داده ای زیر نمایش داد :مدل داده ای برای تعیین سلسله مراتب تاییداتقطعا در هر سامانه در یک جدول اطلاعات کاربران نگهداری می شود که به کمک جدول USER نمایش داده شده و به ازای هر فرآیند در سطح سامانه می توان اولویت ۱و۲و۳و.. تاییدات را به کمک جدول CONFIRMATION_PRIOIRTY به کاربران مختلف نسبت داد . در ادامه با روش های پیاده سازی این موضوع در BPMN  آشنا خواهیم شد . روش اول ( پیاده سازی ضمنی حلقه )  امکان ایجاد حلقه ضمنی در فرآیند های BPMN  در حالات مختلفی ممکن است پیش بیاید و کارشناسان فرآیندی و یا برنامه نویسان  در حالات مختلفی ممکن است اقدام به ایجاد یک حلقه ضمنی کنند .  برای آشنایی بیشتر با این روش قبل از هر نکته ای BPMN  آن را در تصویر زیر مشاهده می کنیم : ایجاد حلقه ضمنی در BPMN در نظر بگیرید که فرآیند قرار داده شده برای ثبت درخواست مرخصی کارمندان است ، ابتدا کارمند درخواست مرخصی خود را در گام ثبت اطلاعات فرآیند ثبت خواهد کرد و آن را جهت تایید ارجاع خواهد داد ، فرآیند تاییدات توسط کارشناسان سیستم در فرم سلسله مراتب تاییدات به این شکل تنظیم شده است که ابتدا رییس شخص درخواست دهنده بایستی آن را تایید کرده و سپس کارشناس کارگزینی آن را تایید نماید ، هر کدام از این افراد درخواست را رد نمایند درخواست مورد نظر به کارمند ثبت کننده بازگشت داده خواهد شد . اگر بخواهیم به شکل بهتری که این حلقه ضمنی دقیقا کدام بخش از فرآیند است ، در تصویر المنت هایی که حلقه ضمنی را ایجاد کرده اند با رنگ سبز مشاهده می کنید : ایجاد حلقه ضمنی در BPMN روش دوم - استفاده از Multiple Instanceیکی دیگر از روش هایی که برای ایجاد سلسله مراتب تاییدات می توان در فرآیند های BPMN  استفاده کرد ، ایجاد حلقه تاییدات به کمک فعالیت های  Multiple Instance است . لازم به ذکر هست که در این روش هم سلسله مراتب تاییدات به روش گفته شده در ابتدای مقاله توسط کارشناسان ارشد سامانه تعیین خواهد شد و در فرآیند BPMN  فراخوانی و مورد استفاده قرار خواهد گرفت . شکل BPMN گفته شده در روش قبلی به شکل زیر تغییر خواهد کرد : در روش قبلی ما با ایجاد Loop  در BPMN چندین UserTask  را ایجاد می کردیم اما در روش فعلی ما یک UserTask  با قابلیت Multiple Instance داریم  و خود این فعالیت به تعداد مورد نیاز UserTask  های لازم را در موتور فرآیندی ایجاد خواهد کرد . دقت کنید در ادامه هر جا در مورد فعالیت والد صحبت شد منظور  UserTask اصلی است و هر جا از فعالیت فرزند صحبت شد منظور UserTask هایی است که به وسیله فعالیت  Multiple Instance ایجاد شده است . در موتورهای فرآیندی Task  ها دارای انواع مختلفی هستند ، همچنین Task  ها رو می توان با برخی علامت گذاری ها به برخی قابلیت ها مجهز کرد ، این علامت گذاری ها شامل  Loops ، Multiple Instance و یا  Compensations  هستند .  همین طور می توان بر روی یک تسک از چندین علامت گذاری استفاده کرده و آن ها با هم ترکیب کرد و به رفتار مورد نیاز رسید .  در این بخش ما Multiple Instance را برای حل مساله مورد نظر استفاده خواهیم کرد . در یک فرآیند می توان از فعالیت های Multiple Instance  استفاده کرد ، استفاده از این فعالیت ها یکی از روش های مناسب برای تعریف تکرارها در گام های مختلف یک مدل فرآیندی است . در مفاهیم برنامه نویسی یک حلقه for-each  معادل یک فعالیت Multiple Instance در یک فرآیند است . فعالیت Multiple Instance  به ما اجازه می دهد یک فعالیت را چندین بار برای یک گام خاص از فرآیند تکرار کنیم و یا یک Sub-process را به ازای یک مجموعه ( Collection ) به صورت Sequential و یا  Parallel اجرا کنیم . فعالیت های زیر قابلیت اجرا به صورت multi-instance  را دارند : Service TaskSend TaskUser TaskBusiness Rule Task Script TaskReceive TaskManual TaskEmbedded Sub ProcessCall ActivityTransaction Sub Process همچنین باید به این نکته دقت کرد که Gateway  ها و Event  ها نمی توانند به صورت Multiple Instance  اجرا شوند . اگر یک فعالیت به صورت Multiple Instance  تعریف شود به کمک یک خط کوچک در زیر آن فعالیت مشخص می شود ، سه خط عمودی مشخص خواهد کرد که این فعالیت به صورت Parallel  اجرا خواهد شد و همچنین سه خط افقی مشخص می کند که این فعالیت به صورت Sequential  اجرا می شود . نمونه ای از فعالیت Parallel  و Sequentialتفاوت بین فعالیت های Parallel و Sequential این خواهد بود ، در هنگام ورود به این فعالیت چنانچه فعالیت Parallel باشد تمام فعالیت های زیر مجموعه آن در همان لحظه ایجاد خواهد شد و اجرای آن ها به صورت موازی اتفاق می افتد ، و چنانچه فعالیت Sequential  باشد فعالیت های زیر مجموعه آن پس اتمام هر فعالیت و به ترتیت ایجاد خواهند شد . هر فعالیت Multiple Instance  ( منظور فعالیت والداست ) دارای متغیر های زیر خواهد بود : تعداد کل فعالیت ها ( nrOfInstances )تعداد فعالیت های فعال جاری (nrOfActiveInstaces ):  منظور Instance  هایی است که هنوز پایان نیافته اند . برای فعالیت های Sequential  این متغیر همیشه یک خواهد بود . تعداد فعالیت های اتمام یافته (nrOfCompltedInstances) : تعداد فعالیت هایی که در حال حاضر تکمیل شده اند . این مقادیر می توانند به کمک متد execution.getVariable(x) برگردانده شده و مورد استفاده قرار بگیرند . به علاوه ، هر فعالیت ایجاد شده برخی متغیر های محلی مربوط به خود را دارد که این متغیر ها توسط سایر فعالیت ها قابل مشاهده نیستند و در سطح ProcessInstance  ذخیره نمی شوند . همچنین به کمک متغیر LoopCounter می توان index مربوط به for-each  مورد نظر را پیدا کرد . برای تبدیل فعالیت ها ،  به یک فعالیت multi-instance بایستی xml مربوط به آن فعالیت دارای المنت multiInstanceLoopCharacteristics باشد : مشخصه isSequential مشخص خواهد کرد که این فعالیت به صورت Sequential یا Parallel  اجرا شود . یکی از نکات بسیار مهمی که در مورد فعالیت Multiple Instance  اهمیت دارد این است که ، تعداد نمونه های فعالیت ها یک بار محاسبه خواهد شد و این محاسبه زمان ورود به فعالیت انجام خواهد شد. راه های متفاوتی برای تنظیم این موضوع وجود دارد . که در ادامه به تشریح این روش ها می پردازیم . Loop Cardinality یکی از روش هایی که می توان به کمک آن تعداد تکرار یا تعداد فعالیت های Multiple Instance را مشخص کرد ، تعیین مستقیم متغیر Loop Cardinality  است . باید دقت شود که تعداد تکرارها قابلیت تعیین به صورت ثابت و داینامیک را دارد ، اگر بخواهیم برای مثال گفته شده در مقاله یعنی تایید مرخصی کارمندان این متغیر را تعیین کنیم ، از آنجایی که تعیین روال تاییدات در اختیار کارشناسان سامانه است و ممکن است تغییر کند حتما باید به صورت داینامیک تنظیم شود و بایستی تعداد کارشناسان تایید کننده در این متغیر قرار بگیرد  . ما در نظر گرفتیم که رییس شخص درخواست دهنده و کارشناس کارگزینی بایستی درخواست مرخصی کارمندان را تایید کنند در نتیجه در این حالت مقدار متغیر Loop Cardinality  برابر ۲ خواهد بود .  این مشخصه از دو روش قابل تنظیم است ، روش اول از طریق Properties  های مربوط به فعالیت مورد نظر در نرم افزار Camunda Modeler  و روش دوم به کمک xml که تصویر هر دوی این روش ها در زیر قابل مشاهده است . تنظیم متغیر Loop Cardinality  در نرم افزار Camunda Modeler در xml مربوط به فایل bpmn  نیز به صورت زیر می توان این مشخصه را مقدار دهی کرد : تنظیم متغیر Loop Cardinality  از طریق xmlهمچنین می توان این متغیر را از طریق expression  نیز مقدار دهی کرد  که در واقع مقدار دهی داینامیک به این مشخصه به این روش صورت می گیرد : همچنین این قابلیت وجود دارد از طریق فراخوانی یک وب سرویس به مقدار این متغیر دست پیدا کرد و مقدار مورد نظر را در این متغیر قرار داد . Collectionیک روش دیگر برای مشخص کردن تعداد فعالیت ها مشخص کردن نام یکی از Process Variable  ها است که در واقع یک collection است ،  استاندارد BPMN روشی را معرفی کرده است که می توان از طریق متغیر LoopDataInputRef این Collection  را مشخص کرد اما بایستی دقت شود که این متغیر از طریق xml قابل مقدار دهی است و از  طریق نرم افزار Camunda Modeler  قابل مقدار دهی نمی باشد . در این روش به ازای هر عضو موجود در collection مورد نظر یک نمونه یا instance  ایجاد خواهد شد . همچنین پارامتری وجود دارد که شما می توانید به صورت اختیاری تنظیم کنید ، که هر Instance  به کدام فیلد از آبجکت collection  شما دسترسی داشته باشد و بتواند با آن کار کند :در نظر بگیرید که متغیر assigneeList شامل مقادیر [ رضا ، علی ، محمد ] است . قطعه xml بالا ، سه Task  را به صورت  Sequential  ایجاد خواهد کرد . هر کدام از excution ها دارای یک متغیر با نام assignee  هستند که از مقادیر مجموعه تعریف شده هستند و برای assign کردن Task  استفاده خواهند شد . یکی  ایرادات بزرگی که دو متغیر loopDataInputRef  و  InputDataItem دارند این است که بر اساس قواعد و محدودیت های BPMN 2.0 نمی توانند شامل expression باشند و همین طور باید از طریق xml  مقدار دهی شوند . از این رو موتور فرآیندی Camunda  این موضوع را به کمک مشخصه های collection و element variable  بر روی multi-instance  ها حل کرده است که به سادگی این موضوع از طریق نرم افزار Camunda Modeler  قابل تنظیم است . اگر بخواهیم مثال گفته شده ، یعنی تایید مرخصی کارمندان را به کمک این روش حل کنیم ما بایستی یک متد در اختیار داشته باشیم که لیستی از کاربران تایید کننده را از مدل داده ConfirmationPriority  برای ما فراخوانی کند که در متغیر Collection  قرار می گیرد و شناسه کاربری افراد تاییده کننده  ( یعنی رییس شخص درخواست دهنده و کارشناس کارگزینی ) به عنوان  Element Variable استفاده خواهد شد . مدل داده ای جهت درک بهتر مجددا قرار داده شد . شرط اتمام Multiple Instanceیک فعالیت Multiple Instance زمانی پایان می یابد که تمام فعالیت های زیر مجموعه مربوط به آن پایان یافته باشند . اما این قابلیت وجود دارد که یک expression مشخصی کنیم که هر بار یک Instance  از این فعالیت تمام می شود بررسی شود اگر نتیجه این expression  برابر با صحیح (True) بود ما بقی Instance  های باقی مانده از بین برود و اجرا نشود و  اجرای Multiple Instance  خاتمه بیابد ، یعنی عملا کار به ادامه ی فرآیند ، یعنی بعد از فعالیت multi-instance  می رود . نمونه ای از این رفتار را در فرآیند زیر می بینیم که از طریق متغیر Complete Condition  در نرم افزار Camunda Modeler  مشخص شده است  : در مثال گفته شده که به روش  Parallel پیاده سازی شده است وقتی  60% تسک ها تکمیل می شوند مابقی آن ها حذف شده و فرآیند ادامه پیدا خواهد کرد . می توان حالتی را نیز برای روش Sequential  در نظر گرفت که وقتی نظر یکی از کارشناسان رد کردن درخواست بود فعالیت ادامه پیدا نکند :Loops یکی دیگر از قابلیت های ایجاد حلقه ها در  موتور های فرآیندی Loop  ها هستند ، اما Loop در حال حاضر توسط موتور فرآیندی Camunda  پشتیبانی نمی شود . اما این چشم انداز در Camunda  دیده شده که قابلیت پشتیبانی از استاندارد Loop  مربوط به Bpmn  را به موتور فرآیندی خود اضافه کند . برای رفع این محدودیت روشی که در ابتدای مقاله عنوان شد یعنی ایجاد حلقه به طور ضمنی روش مناسبی خواهد بود . نتیجه گیری یکی از بزرگترین ایراداتی که Multiple Instance  ها دارند این است که تعداد تکرار آن ها بایستی قبل از ورود به آن ها مشخص شود  که این موضوع ممکن است در برخی حالات آن ها را  گزینه مناسبی برای ایجاد حلقه های تکرار ندانیم . اما قطعا برای برخی حالات کاملا مناسب خواهند بود . جهت درک بهتر در مثال تایید مرخصی کارمندان چنانچه در حین فرآیند تایید مرخصی یکی از کارمندان سلسه مراتب تاییدات توسط مدیر ارشد سیستم تغییر کند و یک فرد اضافه یا حذف شود این تغییر بر روی فرآیند های در حال اجرا تاثیر نخواهد گذاشت . روش های ذکر شده کاربرد های دیگری نیز می توانند داشته باشند و لزوما برای ایجاد روال تایید بین کارشناسان مفید نخواهد بود ،  ممکن است حالات مختلفی در فرآیند های شما وجود داشته باشد که به کمک این روش ها حل بشوند .</description>
                <category>محمد میرزائیان</category>
                <author>محمد میرزائیان</author>
                <pubDate>Thu, 26 Mar 2020 19:31:02 +0430</pubDate>
            </item>
                    <item>
                <title>حفظ State صفحات در Angular به کمک Ngrx</title>
                <link>https://virgool.io/angular-iran/%D8%AD%D9%81%D8%B8-state-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%AF%D8%B1-angular-%D8%A8%D9%87-%DA%A9%D9%85%DA%A9-ngrx-pjc2wxgcpguv</link>
                <description>فریمورک های جاوا اسکریپتی توی چند سال اخیر تغییرات زیادی کردند و رقابت سنگینی بین اون ها شکل گرفته  ، چند تا از اون ها مثل Angular ,React و Vuejs  در سال های اخیر طرفدارهای زیادی پیدا کردند و هزاران مقاله برای مقایسه اون ها با هم تو سطح وب نوشته شده.  اما من تقریبا تا اواخر سال ۹۶ سراغ هیچ کدوم از این فریمورک ها نرفته بودم و غالبا توی پروژه هایی که کار میکردم از JSP  برای پیاده سازی لایه UI استفاده میکردیم و استدلالمون این بود که این فریمورک ها باید به یک سطح بلوغی برسند تا بتونیم برای پروژه هایی که داریم بریم سراغشون و بهشون اعتماد کنیم . به هر حال سال ۹۶ وقتی داشتیم تو شرکت یه پروژه جدید رو شروع می کردیم تصمیم گرفتیم که توی این پروژه از Angular برای پیاده سازی لایه UI  استفاده کنیم . اون زمان که این کار رو شروع کردیم Angular  روی نسخه ۴ بود و طی این چند سال چالش های متفاوتی برامون تو پیاده سازی لایه یو آی پیش اومد که هر کدومشون داستان های مفصل خودشو داشت . خوب لازمه کمی در مورد کاری که می کردیم توضیح بدم ، ما یه نرم افزار سازمانی توسعه می دادیم که کسب و کار های مربوط به سازمان های مختلف در حوزه انبارداری و نگهداری و تعمیرات را پوشش می داد  و پر از فرم های CRUD  بود  ، بعد از مدتی متوجه شدیم یکی از مهم ترین چالش های ما توی پیاده سازی یو آی به کمک انگیولار حفظ کردن State  صفحات است . چون ما یه سری فرم داشتیم که کاربر میومد داخل اون ها و بین هزاران رکورد به کمک کنترل های سرچ موجود ، در کامپوننت گرید رکورد مورد نظرش رو پیدا می کرد و وقتی وارد مود مشاهده اون رکورد می شد و دوباره به گرید برمیگشت همه تلاش هایی رو که برای پیدا کردن اون رکورد یا رکورد ها کرده بود باید تکرار می کرد ، چون همه اطلاعات سرچ از بین رفته بودند !!!! با یه مقدار سرچی که توی اینترنت کردم متوجه شدم که Redux  راه حل این موضوع هست که به کمک کتابخانه Ngrx در انگیولار قابل پیاده سازی هست . در این مقاله فرض شده است که شما آشنایی ابتدایی با مفاهیم rxjs دارید . ریداکس (Redux ) چیست ؟ ریداکس (Redux) یک پترن برای مدیریت کردن State  نرم افزار است . این Pattern بسیار شبیه به پترن Flux که متعلق به Facebook ئه هست و همین طور از Flux  الهام گرفته است . شاید الان برای شما هم مثل من این سوال به وجود آمده باشه که اصلا State  چی هست ؟  به طور کلی به مجموعه ی اطلاعاتی در مورد View  یک Application  مثل نمایش یا عدم نمایش فیلدها و المنت های مختلف State  گفته میشه . هدف اصلی Redux فراهم کردن یک Predictable State Container است . ترجمه مناسب این جمله را واقعا نمی دونستم چی بنویسم ولی اگر بخوایم ترجمه ای براش ارائه بدیم باید بگیم یک کانتینر قابل پیش بینی وضعیت !!! اما نکته ای که به نظرم بیشتر از ترجمه اش مهم باشه اینه که اصلا مفهوم این جمله چیه ؟  در جواب این سوال باید گفت که مفاهیم موجود در Redux و متوجه شدن اون هاست که به ما کمک می کنه مفهوم این جمله رو متوجه بشیم ، اما برای ادامه کار باید همین قدر بدونیم که هر اتفاقی که توی سطح Application میفته  ، ما میدونیم که این اتفاق وضعیت Application رو به چه چیزی تغییر میده . در ادامه سعی می کنیم به قواعد Redux  بپردازیم تا بتونیم مفهوم این جمله رو بهتر درک کنیم . قواعد اصلی Redux ریداکس شامل سه قاعده اساسی هست که به کمک این سه قاعده معرفی میشه . Single source of truthتنها یک منبع حقیقت وجود دارد و آن هم Store  است .  تمام State های مربوط به Application در یک درخت وضعیت ذخیره خواهند شد .( اینکه Store  چی هست جلوتر باید باهاش آشنا بشیم . )State is read-only تنها راه تغییر دادن State موجود ایجاد Action جدید است ،‌در غیر این صورت state های موجود در Store  فقط خواندنی هستند . ( و باز هم در مورد Action جلوتر صحبت خواهیم کرد . ) Changes are made with pure functionsتغییرات در Store  به کمک Reducer  ها اتفاق می افتد که آن ها هم Pure Function  هستند ( در مورد reducer  ها و Pure Function  ها توضیح خواهم داد . ) با NgRx  بیشتر آشنا بشیمدر تصویر زیر روال اصلی  Ngrx  برای مدیریت state  صفحات رو می بینیم که هر کدوم از این بخش ها عملیاتی رو انجام میدن و ما رو به هدف نهایی می رسونن .  در این قسمت سعی می کنم گام به گام این مراحل رو توضیح بدم . در ابتدا سعی میکنم شکل بالا را بدون در نظر گرفتن مفاهیم جزئی هر المنت توضیح بدم . به صورت کلی شروع تغییرات از component شروع میشود پس گام اول کامپوننت می باشد . هر تغییری در کامپوننت باعث ایجاد یک action میشود (اصطلاحا کامپوننت ما یک action را dispatch می کنه  ) در این نقطه دو مرحله ممکن است اتفاق بیفتد که به action ما بستگی دارد۱- اکشن ما Effect ای دارد :نکته : منظور از Effect این است که آیا برای مثال API Call ای شکل میگیرد یا خیر در این حالت Effect مورد نظر از طریق service مربوطه انجام می شود و سپسaction توسط reducer در Store ذخیره میکند (مثال : زدن دکمه جستجو )۲-اکشن ما Effect ای ندارد:در این حالت reducer مستقیما تغییرات (action) را در store ذخیره میکند (مثال : نمایش کد کالا با فشرده شدن checkbox)در نهایت تغییرات توسط کاموپوننت و به واسطه selector ها دریافت میشود.حال با جزئیات بیشتری به موارد موجود در عکس میپردازیمدر اکثر حالت ها تغییرات از Component  های ما شروع می شود ، در نظر بگیرید که ما در یک صفحه چک باکسی بر روی View  داریم ( مثلا مشاهده رکورد های فعال و غیر فعال) و کلیک کردن کاربر بر روی این چک باکس منجر به dispatch شدن یک Action  می شود . نمونه ای از یک چک باکس در حالت غیر فعالنمونه ای از یک چک باکس در حالت فعال Action اتفاق افتادن هر رخداد در کامپوننت های Angular  موجب ایجاد شدن  یک Action  خواهد شد . هر Action  دارای دو مشخصه اساسی است : مشخصه Type : یک رشته خواندنی است که نوع action را مشخص میکند برای مثال فرض کنیم میخواهیم در صورتی که روی یک چک باکس در view کلیک کردیم و مقدار آن را به فعال تغییر دادیم ، تنها رکورد هایی که دارای وضعیت فعال هستند را نمایش دهیم . برای این کار باید مشخصه type را با مقدار SHOW_ACTIVE_RECORDS مقدار دهی کنیم ( دقت کنید که مقدار این مشخصه توسط شما تعیین می شود و میتواند هر چیزی باشد در حقیقت اسم اکشن می باشد )مشخصه Payload : نوع و مقدار این مشخصه بستگی به اطلاعاتی دارد که این Action می خواهد به Reducer  بفرستد دارد ، در مثالی که ما ذکر کردیم می تواند خالی باشد و یا با یک مقدار true/false  مقدار دهی شود . Reducer همانطور که قبلا اشاره شد Reducer  ها Pure Function هایی هستند که دارای دو ورودی هستند ، یک ورودی State  قبلی و ورودی دوم یک Action  خواهد بود . Pure Functions قبلا اشاره کردیم که Reducer  ها از دسته ی Pure Function  ها هستند ، اما در مورد این نوع از توابع تعریفی ارائه نکردیم . این توابع ، توابعی هستند که مقدار خروجی آن ها مستقیما به مقدار ورودی آن ها بستگی دارد  ، در نتیجه اگر شما یک Pure Function  را با یک مقدار خاص فراخوانی کنید همیشه خروجی یکسان خواهید داشت . برای مثال تابع زیر مثالی از یک Pure Function   است : function calculateSquareArea(x) {
   return x * x;
}در مقابل Pure Function  ها ، Impure Function ها قرار دارند که مقدار خروجی آن ها مستقیما از ورودی آن ها تاثیر نمی گیرد و ممکن است با یک ورودی خاص در مواقع مختلف خروجی های متفاوتی داشته باشند ، توابعی مانند Date.now , Math.random مثالی از Impure Function ها هستد .  اگر Action مورد نظر Effect  ای ایجاد نکند : Reducer  روال بررسی و پردازش این Action  را شروع کرده ( غالبا به کمک دستورات switch/case ) و State  جدید را  به واسطه Selector ها به Component بر می گرداند که حاصل ترکیب State  قبلی با اتفاقاتی که در این Action  رخ داده ، خواهد بود .  اگر Action  مورد نظر  Effect ای ایجاد کند  : به عنوان مثال به اجرا شدن یک سرویس http برای دریافت اطلاعات می تواند یک Effect  باشد. اگر Action ما دارای تاثیرات یا Effect باشد باید آن ها قبل از اجرا شدن Reducer بررسی و اجرا بشوند سپس Action  مورد نظر جهت پردازش در اختیار Reducer قرار می گیرد .Effectsدر اکوسیستم Ngrx به کمک Effect  ها به این امکان دست پیدا خواهیم کرد که با تاثیراتی که خارج از روال انگیولار و کامپوننت های آن هستند تعامل داشته باشیم  . Effect ها دقیقا مشابه Reducer  ها به اتفاقاتی که در یه App می افتد گوش می دهند و بلافاصله بعد از Dispatch شدن یک Action اگر شرایط تعریف شده را داشته باشند تاثیرات لازم بر روی آن Action  را انجام می دهند که غالبا این تاثیرات ارسال یا دریافت اطلاعات به / از یک Api  است . در نهایت  Action دیگری را با شرایط جدید  Dispatch  خواهند کرد . لازم است دقت کنیم که بعد از پایان یک Effect یک Action جدید با توجه به failed یا success شدن آن Effect  ایجاد خواهد شد . Storeاساسی ترین بخش Redux  همین Store  است ، که تمام توضیحاتی را که ارائه دادیم را در کنار هم قرار می دهد . ( Action,reducer ) و مهم ترین کار را انجام می دهد : وضعیت Application را حفظ می کند . به بیان ساده ظرفی که تمام اطلاعات ما را در خود نگه می دارد Store  است . حالا Store  وضعیت يا State  جدید را در خود دارد . اینState می تواند یک  Object بسیار بزرگ باشد ،  از این رو برای خواندن يک Slice از State ها و استفاده از آن در کامپوننت های مورد نظر Selector  ها در کتابخانه Ngrx معرفی می شوند . Selectors همانطور که اشاره کردیم State می تواند یک درخت بسیار بزرگ باشد ، و قطعا معنی نمی دهد در  هر نقطه از کد ما تمام این Object  یا درخت را  داشته باشیم ، چرا که هر بار به بخشی از آن احتیاج داریم . کتابخانه Ngrx برای ما تابع select را فراهم کرده که بخشی از  State ها را برای ما Fetch کرده و تغییرات لازم را انجام داده و در اختیار ما قرار می دهد . نصب کتابخانه Ngrx برای اضافه کردن و نصب Ngrx  بر روی Application خودمون از دستور زیر استفاده می کنیم : ( دقت کنید که در هنگام اجرای این دستور کامپیوتر شما باید به اینترنت متصل باشد . )npm install @ngrx/store --saveبدین ترتیب کتابخانه Ngrx بر روی Application ما نصب و به فایل package.json اضافه خواهد شد . ایجاد يک Storeبرای ایجاد یک Store  در سطح Application  اگر تنها یک module  داشته باشیم و آن هم App.module.ts باشد به شکل زیر عمل خواهیم کرد ( قسمتی که باید اضافه شود در کد زیر Underline شده است ) : 
import {StoreModule} from &#039;@ngrx/store&#039;;@NgModule({    bootstrap: [AppComponent],    declarations: [        AppComponent    ],    imports: [        StoreModule.forRoot(reducer),    ],    exports: [ ],    providers: [ ]})export class AppModule {    constructor() {    }} در صورتی که reducer خاصی در App.module نداریم می توانیم نحوه تعریف را به شکل زیر تغییر بدهیم :   StoreModule.forRoot({})نکته قابل توجه این است که در صورت استفاده از Lazy Module  در Angular  بایستی به ازای هر کدام از module ها تعریف Store  را انجام دهیم ، نحوه تعریف Store  برای Lazy Module  ها ، به عنوان مثال ماژول مربوط به فرم products  به شکل زیر عمل می کنیم : StoreModule.forFeature(&#039;products&#039;, reducers)در این حالت هم در صورت عدم نیاز به reducer  به شکل زیر عمل خواهیم کرد : StoreModule.forFeature(&#039;products&#039;, {})ساختار بندی پوشه ها برای Storeیکی از مهم ترین موضوعات برای داشتن یک Codebase  منظم و تمیز استفاده از یک ساختار بندی استاندارد هست ، در مورد ساختار پوشه هایی که برای Store  احتیاج هست روش های مختلفی توصیه شده اما من ترجیح میدم به ازای هر Domain Class  در فایل ها یک فولدر داشته باشم و داخل اون یک پوشه به نام Store  مانند ساختار زیر  بسازم: ساختار مناسب فولدرها برای مدیریت Stateاستخراج State  ها مهم ترین موضوع در استفاده از Redux  و Ngrx  استخراج  State  های App  مورد نظر است و همچنین Action  هایی که منجر به تغییر این State  ها می شوند . برای مثال به تصویر زیر دقت کنید : بخش جستجو در فرم کاربراناین تصویر قسمت جستجو مربوط به فرم مشاهده کاربران می باشد ، کاربر از طریق کنترل های موجود می تواند بر اساس نام ، نام خانوادگی و یا نام کاربری در بین کاربران اقدام به جستجو نماید .  دکمه های &quot;جستجو و پاک کردن&quot; می تواند منجر به ایجاد یک Action  شود . تا اینجای کار تقریبا با مفاهیم موجود آشنا شدید و  همینطور کمی هم با مفاهیم استفاده از Ngrx در سطح کد ، اما برای استفاده از این Library روش های مختلفی  وجود دارد که هر کدوم مزایا و معایب خودشون رو ممکنه داشته باشند اما به هر حال روشی که من استفاده می کنم رو سعی می کنم به صورت گام به گام و بر اساس فایل هایی که قبل تر گفتم توی پوشه Store باید بزاریم ، توضیح میدم . product.action.ts در این فایل باید در ابتدا یک enum  تعریف کنیم که تمام Action هایی که ممکن است اتفاق بیفتد را را در آن تعریف کرده باشیم : export enum ProductActionTypes {
    Search = &#039;[Product] Search&#039;,
    SearchComplete = &#039;[Product] Search Complete&#039;,
    SearchError = &#039;[Product] Search Error&#039;
}در گام بعدی در همین فایل باید به ازای هر کدام از مقادیر این enum کلاس هایی را بنویسیم که اینترفیس Action   را از مسیر &#x27;@ngrx/store&#x27; در کتابخانهNgrx  پیاده سازی می کنند : import {Action} from &#039;@ngrx/store&#039;;
export class SearchComplete implements Action {
    readonly type = ProductActionTypes.SearchComplete;

    constructor(public payload: Product[]) {
    }
}همان طور که قبل تر در مورد Action ها توضیح دادم دو بخش مهم یک اکشن  type و payload آن هستند و همان طور که در کد بالا می بینید در constructor  این کلاس payload  مربوط اکشن SearchComplete  وجود دارد و آن هم لیستی از اطلاعات مربوط به دیتا مدل Product  است  و همین طور مقدار type از enum مربوطه مقدار دهی کردیم . product.efftects.tsهمان طور که اشاره کردیم Effect  ها مسئول ارتباط با Api  ها هستند ، نکته ای که وجود دارد این است که کلاس های Effect  بایستی دارای @Injectable  باشند . import {Actions, Effect, ofType} from &#039;@ngrx/effects&#039;;
import {catchError, debounceTime, map, skip, switchMap, takeUntil} from &#039;rxjs/operators&#039;;
import {Action} from &#039;@ngrx/store&#039;;

@Injectable()
export class ProductEffects {

    constructor(
        private actions$: Actions,
        private productService: ProductService,
        @Optional()
        @Inject(SEARCH_DEBOUNCE)
        private debounce: number,
        @Optional()
        @Inject(SEARCH_SCHEDULER)
        private scheduler: Scheduler
    ) {
    }

    @Effect()
    search$: Observable&lt;Action&gt; = this.actions$.pipe(
        ofType&lt;Search&gt;(ProductActionTypes.Search),
        debounceTime(this.debounce || 300, this.scheduler || asyncScheduler),
        map(action =&gt; action.payload),
        switchMap(query =&gt; {
            if (query === undefined || query === null) {
                return empty();
            }
            const nextSearch$ = this.actions$.pipe(
                ofType(ProductActionTypes.Search),
                skip(1)
            );
            return this.productService.get(query).pipe(takeUntil(nextSearch$),
                map((products: Product[]) =&gt; new SearchComplete(products)),
                catchError(err =&gt; of(new SearchError(err)))
            )
        })
    );
}در این کد در واقع ما به اتفاقاتی که در Component مورد نظر می افتد گوش می دهیم و اکشن هایی که منجر  به ایجاد Effect می شود را شناسایی کرده و تاثیرات لازم را اعمال می کنیم .product.reducer.tsimport {createEntityAdapter, EntityAdapter, EntityState} from &#039;@ngrx/entity&#039;;

export interface State extends EntityState&lt;Product&gt; {
    selectedProductId: string | null;
}

export const adapter: EntityAdapter&lt;Product&gt; = createEntityAdapter&lt;Product&gt;({
    selectId: (product: Product) =&gt; product.id,
    sortComparer: false,
});

export const initialState: State = adapter.getInitialState({
    selectedProductId: null,
});

export function reducer(state = initialState, action: ProductActionsUnion): State {
    switch (action.type) {
        case ProductActionTypes.SearchComplete:
        default: {
            return state;
        }
    }
}و در نهایت در فایل search.reducer.ts هم مشابه فایل product.reducer.ts برای مدیریت بخش search reducer  هایی که لازم است را می نویسیم . index.tsدر این فایل تمام selector های مورد نیاز برای استخراج state  های مورد نظر از Store را می نویسیم ،همین طور دو فایل product.reducer.ts و search.reducer.ts را در این کلاس import می کنیم که در component ها فقط با import کردن  product/store/reducers به selector  ها و reducer های مربوطه دسترسی داشته باشیم .نمونه ای از یک Selector  به صورت زیر خواهد بود : import {createSelector} from &#039;@ngrx/store&#039;;
export const getProductEntitiesState = createSelector(
    getProductsState,
    state =&gt; state.products
);در کامپوننت ها چه تغییراتی باید بدهیم ؟ بعد از انجام تمام این کارها در کامپوننت های مورد نظر باید تغییراتی را اعمال کنیم تا بتونیم اطلاعات مورد نیاز را به کمک selector  ها از store  خوانده و کامپوننت را با وضعیت مورد نظر به روز کنیم. به ازای هر کدام از selector  ها باید یک Observable  تعریف کنیم که اطلاعات مورد نظر را از store  فراخوانی کرده و در اختیار ما قرار دهد : @Component({
    selector: &#039;product-list&#039;,
    templateUrl: &#039;./product.component.html&#039;
})
export class ProductComponent {
    products$: Observable&lt;Product[]&gt;;

    constructor() {
        this.products$ = store.pipe(select(productReducer.getSearchResults)); // نحوه تعریف selector
    }
}
جمع بندی به طور کلی موضوع مدیریت State  صفحات به کمک Redux  به نظر من موضوع پیچیده ای برای برنامه نویس ها هست ، و نیاز هست که اون ها موضوعات و جزئیات مختلفی رو بهش تسلط داشته باشند تا بتونن این کار رو انجام بدن ، به اضافه اینکه این روش کدهای زیادی رو هم تولید میکنه اما در حال حاضر به نظر میرسه روشی باشه که اکثر حالات را پوشش بده ، البته لازم به ذکر هست که کتابخونه های دیگه ای هم به غیر از Ngrx  برای مدیریت این موضوع وجود داره (مثل Ngxs و Akita) و شما می تونید اون ها رو هم بررسی کنید . اما من سعی کردم اینجا تجربه ای که توی این حوزه داشتم را مکتوب کنم تا طی تکامل فریمورک های جاوا اسکریپتی ( که احتمالا روشی ساده تر برای این موضوع ارائه خواهد شد ) در آینده ، هم خودم و هم دوستانی که این مقاله رو خوندند بتونیم مقایسه ی بهتری بین روش های موجود انجام بدیم .منتظر نظرات و پیشنهادات شما هستم . منابع :Angular: NGRX a clean and clear IntroductionPure vs Impure functionsAngular NgRx: Getting StartedNgRx in AG04</description>
                <category>محمد میرزائیان</category>
                <author>محمد میرزائیان</author>
                <pubDate>Wed, 11 Dec 2019 18:51:27 +0330</pubDate>
            </item>
                    <item>
                <title>مشخصه های اصلی یک تراکنش ( ACID )</title>
                <link>https://virgool.io/baharan-co/%D9%85%D8%B4%D8%AE%D8%B5%D9%87-%D9%87%D8%A7%DB%8C-%D8%A7%D8%B5%D9%84%DB%8C-%DB%8C%DA%A9-%D8%AA%D8%B1%D8%A7%DA%A9%D9%86%D8%B4-acid-eflnyz1zrdyx</link>
                <description>تا حالا به این مساله فکر کردید که وقتی در مورد یک تراکنش صحبت می کنیم مشخصه های اصلی اون چه چیزهایی هستند ؟ یکی از مفاهیم مهم در سامانه های اطلاعاتی انجام یک تراکنش است . همه برنامه نویس هایی که در حال توسعه سامانه های اطلاعاتی هستند باید در مورد تراکنش و مشخصه های اصلی اون اطلاعات و آگاهی کامل داشته باشند . اصلا تعریف تراکنش چی هست ؟ یک تراکنش مجموعه ای از عملیات خواندن / نوشتن در سطح پایگاه داده است که تمامی این عملیات با هم ، دقت کنید همه با هم ، یا به موفقیت می رسند و یا در صورت خطا هیچ کدام از آن ها اجرا نمی شود .هر تراکنش برای اینکه نگرانی های ما رو در مورد امنیت و ثبات اطلاعات تامین کنه باید ۴ تا مشخصه داشته باشه : Atomicity ConsistencyIsolationDurability اتمیک بودن ( Atomicity )این ویژگی در تراکنش ها به همه یا هیچ  ( All or nothing ) معروف است ، در واقع تراکنش در صورتی موفق است که تمام دستوراتی که در آن اجرا می شوند موفق باشند و بر اساس این قاعده دیتابیس باید قادر باشد که در صورت عدم موفقیت دستور n ام ، تمامی دستورات قبلی را رول بک کند . توی این عکس دو تا تراکنش نشون داده شده اولی سه تا عملیات جمع رو انجام داده و هر سه موفق بودند و تراکنش با موفقیت به پایان رسیده و در تراکنش دوم عملیات تفریقی که انجام شده منجر به منفی شدن متغیر X شده و تراکنش به طور کلی حتی با موفق بودن عملیاتی که بر روی متغیر Z انجام شده به حالت قبلی برگردانده ( RollBack ) شده است . ثبات ( Consistency )یک تراکنش در واقع بایستی در هنگام تغییر وضعیت ، اطلاعات را از یک وضعیت صحیح به یک وضعیت صحیح دیگر ببرد .در این تغییر وضعیت دیتابیس هم سعی می کند که هیچ کدام از کلید ها و نوع های داده ای و Trigger ها نباید نقض شوند . برای درک بهتر مساله تصویری که برای توضیح ثبات (‌Atomicity) گذاشتم رو تکرار می کنم : در سطح دیتابیس به کمک یک  check constraint سعی شده از منفی شدن متغیر X جلوگیری بشه و  این اتفاق توی تراکنش موجب نقص این قاعده شده که دیتابیس از انجام ادامه کار جلوگیری کرده است . محرمانگی ( Isolation )مهم ترین مشخصه ی  یک تراکنش سطح محرمانگی آن است . ما می دونیم که اگر تنها یک کاربر به طور همزمان از دیتابیس استفاده کند نگرانی در مورد احتمال ایجاد مغایرت و آنومالی در اطلاعات نخواهیم داشت و اگر اجازه استفاده همزمان به کاربران جهت دسترسی به دیتابیس بدیم کارایی سیستم رو افزایش دادیم ، کاربر شماره یک با جدول یک کار می کند و کاربر شماره دو با جدول دو . اما دسترسی همزمان معضلاتی رو هم ایجاد می کند که دیتابیس بایستی جهت حل این موضوعات مسائل مختلفی در تراکنش ها مدیریت کند. سطح دسترسی یک تراکنش به تغییرات تراکنش های همزمان با خودش رو سطح محرمانگی اون تراکنش تلقی می کنند . دیتابیس این توهم رو برای ما ایجاد می کند که اگر دو کاربر به صورت همزمان و در یک لحظه به اطلاعات یک جدول جهت انجام تراکنش نیاز دارند ، این دو تراکنش قطعا پشت سر هم و یکی پس از دیگری اجرا می شوند و همزمان اجرا نخواهند شد .سطوح مختلف این سطح محرمانگی در دسترسی تراکنش ها به تغییرات یکدیگر بسیار مهم است . برای درک بهتر در نظر بگیرید که تراکنش شماره یک در حال بازیابی کارمندان یک سازمان برای به روز رسانی حقوق آنهاست ، دقیقا در همان لحظه در بخش نیرو انسانی و به واسطه تراکنش دوم فرد جدیدی به سازمان اضافه می شود آیا تراکنش اول بایستی به اطلاعات این فرد جدید دسترسی داشته باشد ؟ دیتابیس با مدیریت سطوح محرمانگی و با توجه به کسب و کار شما اجازه ی جدا سازی این تراکنش ها به نحوی که به اطلاعات یکدیگر دسترسی نداشته باشند دارد ، همین طور در طرف مقابل امکان ایجاد دسترسی را نیز در اختیار شما قرار داده است . دوام یا ماندگاری ( Durability ) یک تراکنش صحیح وقتی به پایان می رسد و با موفقیت در دیتابیس ثبت می شود، فقط توسط یک تراکنش صحیح دیگر قابل تغییر هست و در هیچ شرایطی از دیتابیس پاک نخواهد شد ،  همین طور این تراکنش هیچ کدام از قوانین را نقض نکرده است . در نظر بگیرید که شما در یک سیستم خرید بلیط هواپیما اقدام به خرید یک بلیط می کنید و عملیات پرداخت بانکی را انجام می دهید و اطلاعات مربوط به صندلی خود را چاپ می کنید ، دقیقا لحظه ای بعد از پرینت شما سامانه فروش بلیط هواپیما مربوط به خط هوایی مورد نظر دچار نقص فنی شده و از دسترس خارج می شود ، اطلاعات بلیط شما بعد از در دسترس قرار گرفتن مجدد این سامانه باید در دیتابیس حفظ شده باشد و توسط فرد دیگری قابل خریداری نباشد. مفاهیمی که در مورد ‌ACID توضیح دادم موضوع تازه ای نیست و Jim Gray خیلی وقت پیش از این که من به دنیا بیام تو این مقاله این موضوعات رو مطرح کرده بود ، من فقط سعی کردم مطالبی رو که حس می کنم تو این حوزه وقت گذاشتم و مطالعه کردم آروم آروم یه جایی جمع و منتشر کنم که شاید به درد بقیه برنامه نویس ها هم بخوره . امیدوارم که تنبلی نکنم و ادامه این موضوعات رو هم همین جا تو ویرگول بنویسم . </description>
                <category>محمد میرزائیان</category>
                <author>محمد میرزائیان</author>
                <pubDate>Fri, 14 Dec 2018 02:39:26 +0330</pubDate>
            </item>
            </channel>
</rss>