خواندنیهای 23 فروردین
به دنیای مجازی و ادبیات مخصوص آن خوش آمدید ...
A simple terminal UI for both docker and docker-compose, written in Go with the gocui library.
مرور چند تجربه کوتاه با Microsoft virtual pc
به settings مربوط به ماشین مجازی خود مراجعه کنید. یک قسمت آن shared folders نام دارد. در اینجا میشود یک فولدر روی ویندوز اصلی را با ویندوز مجازی به اشتراک گذاشت و فایل رد و بدل کرد.
آغاز کار با WPF
آشنایی اولیه
WPF مخفف عبارات Windows Presentation Foundation است که ویکی پدیا این گونه ترجمه میکند : بنیاد نمایش ویندوزی. در برنامه نویسی «ویندوز فرم» ما تمرکز دقیقی بر ساخت رابط کاربری برنامه به خصوص در رزولوشنهای مختلف نداریم و در بسیاری از اوقات کد با رابط کاربری به شدت وابسته میشد که با ارائه WPF از نسخهی سوم دات نت فریم ورک به بعد، این مشکل حل شد و همچنین عملیات refactoring را بسیار سادهتر کرد. در حالت ویندوز فرم به خاطر وابستگی شدید کد و UI، عملیات بهینه سازی کد اصلا موفق نبود.
WPF از ترکیب عناصر دو بعدی و سه بعدی، اسناد، موارد چند رسانهای و رابط کاربری تشکیل شدهاست و موتور رندر آن بر اساس اطلاعات برداری از کارت گرافیک جهت نمایش ظاهر برنامه کمک میگیرد که باعث تهیه برنامهای با رابط کاربری سریعتر، مقیاس پذیرتر و بدون وابستگی به رزولوشن میشود.
جداسازی رفتارها و ظاهر برنامه
همانطور که گفتیم بخش رابط کاربری دیگر مستقل از کد برنامه شده است و ظاهر برنامه توسط زبان نشانه گذاری XAML ایجاد میشود و بخش کد هم با یکی از زبانهای موجود در مجموعه دات نت نوشته خواهد شد. نهایتا این دو بخش توسط رویدادها، فرامین و DataBinding با یکدیگر متصل میشوند. از مزایای جدا بودن این ویژگی:
- عدم وابستگی این دو بخش
- طراح و کدنویس میتوانند هر کدام به طور جداگانه کار کنند.
- ابزارهای طراحی میتوانند به طور جداگانهای بر روی اسناد XML کار کنند بدون اینکه نیاز به درگیری با کدنویسی داشته باشند.
یکی از برنامه هایی که به طراحی رابط کاربری با پشتیبانی از XAML میپردازد برنامه Microsoft Experssion Blend از مجموعه Blend است
Rich Composition
یکی از ویژگیهای XAML، ساخت اشیاء ترکیبی هست که به راحتی با ترکیب تگها با یکدیگر و قرار دادن هر شیء داخل یک شیء دیگر میتوان به یک شیء جدید دست یافت؛ مثل قرار دادن مجموعه ویدیوها در یک لیست. شیء زیر از ترکیب سه شیء تصویر و متن و دکمه ایجاد شده است:
<Button Margin="148,123,126,130"> <StackPanel Orientation="Horizontal"> <Image Source="speaker.png" Stretch="Uniform"/> <TextBlock Text="Play Sound" VerticalAlignment="Center" Margin="10" /> </StackPanel> </Button>
Highly Customizable
با استفاده از مفهوم Style همانند آنچه که در Html و CSS دارید میتوانید اشیاء خود را خصوصی سازی کنید و ظاهر آن شیء را به طور کل تغییر دهید.
Resolution Independence
عدم وابستگی به رزولوشن یا وضوح تصویر دارد و به جای واحد پیکسل، از یک واحد منطقی که یک نود و ششم اینچ است، بهره میبرد. از آنجا که این سیستم بر اساس وکتور ایجاد شده است، مقیاس پذیری آن در تغییر اندازه یا وضوح تصویر به شدت بالا رفته است.
به زودی در قسمت اول این سری کار را با XAML آغاز خواهیم کرد.
مایکرو سرویس چیست ؟
در یک تعریف کوتاه، در معماری مایکرو سرویس، توسعه یک نرم افزار بهصورت مجموعهای از سرویسهای کوچک میباشد که این سرویسها بهصورت کاملا مستقلی قابلیت استقرار دارند و هر کدام از این سرویسها میتوانند توسط تیمهای جداگانهای با پلتفرم توسعه و زبان برنامه نویسی و بانک اطلاعاتی جداگانهای توسعه داده شوند و با یک مکانیزم سبک وزن مانند Http با یکدیگر در ارتباط باشند.چرا معماری مایکرو سرویس؟
در مطالب بعدی در موردی مشخصههای مایکرو سرویسها صحبت خواهیم کرد.
- در سایت Stack overflow از آن استفاده میشود (^).
- در SQL Server Azure حالت پیش فرض ایجاد دیتابیسها و تراکنشهای جدید است (^).
- در Entity framework 6 حالت پیش فرض تراکنشهای ایجاد شده، قرار گرفته است (^ ).
و ... در Oracle، تنها حالت مدیریت مسایل همزمانی است! (البته به نام MVCC، اما با همین عملکرد)
اما READ_COMMITTED_SNAPSHOT در SQL Server چیست و کاربرد آن کجا است؟
اگر استفاده گسترده و سنگینی از SQL Server داشته باشید، حتما به پیغامهای خطای deadlock آن برخوردهاید:
Transaction (Process ID 54) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
در SQL Server 2005 برای بهبود مقیاس پذیری SQL Server و کاهش خطاهای deadlock، مکانیزم READ_COMMITTED_SNAPSHOT معرفی گشت.
به صورت خلاصه زمانیکه که تراکنش مورد نظر تحت حالت READ COMMITTED SNAPSHOT انجام میشود، optimistic reads and pessimistic writes خواهیم داشت (خواندنهای خوشبینانه و نوشتنهای بدبینانه). در این حالت تضمین میشود که خواندن اطلاعات داخل یک تراکنش، شامل اطلاعات تغییر یافته توسط سایر تراکنشهای همزمان نخواهد بود. همچنین زمانیکه در این بین، اطلاعاتی خوانده میشود، بر روی این اطلاعات برخلاف حالت READ COMMITTED قفل قرار داده نمیشود. بنابراین تراکنشهایی که درحال خواندن اطلاعات هستند، تراکنشهای همزمانی را که در حال نوشتن اطلاعات میباشند، قفل نخواهد کرد و برعکس.
نحوه فعال سازی READ_COMMITTED_SNAPSHOT
فعال سازی READ_COMMITTED_SNAPSHOT باید ابتدا در سطح یک بانک اطلاعاتی SQL Server انجام شود:
ALTER DATABASE testDatabase SET ALLOW_SNAPSHOT_ISOLATION ON; ALTER DATABASE testDatabase SET READ_COMMITTED_SNAPSHOT ON;
حالت READ_COMMITTED_SNAPSHOT خصوصا برای برنامههای وبی که تعداد بالایی Read در مقابل تعداد کمی Write دارند، به شدت بر روی کارآیی و بالا رفتن سرعت و مقیاس پذیری آنها تاثیر خواهد داشت؛ به همراه حداقل تعداد deadlockهای حاصل شده.
در Entity framework وضعیت به چه صورتی است؟
EF از حالت پیش فرض مدیریت مسایل همزمانی در SQL Server یا همان حالت READ COMMITTED در زمان فراخوانی متد SaveChanges استفاده میکند.
در EF 6 این حالت پیش فرض به READ_COMMITTED_SNAPSHOT تغییر کرده است. البته همانطور که عنوان شد، پیشتر باید بانک اطلاعاتی را نیز جهت پذیرش این نوع تراکنشها آماده ساخت.
اگر از نگارشهای پایینتر از EF 6 استفاده میکنید، برای استفاده از حالت READ_COMMITTED_SNAPSHOT باید صراحتا IsolationLevel را مشخص ساخت:
using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel= IsolationLevel.Snapshot })) { // update some tables using entity framework context.SaveChanges(); transactionScope.Complete(); }
چرا به ابزارهای مدیریت حالت نیاز داریم؟
به محض رد شدن از مرز پیاده سازی امکانات اولیهی یک برنامه، نیاز به ابزارهای مدیریت حالت نمایان میشوند؛ خصوصا زمانیکه نیاز است با اطلاعات قابل توجهی سر و کار داشت. مهمترین دلیل استفادهی از یک ابزار مدیریت حالت، مدیریت منطق تجاری برنامه است. منطق نمایشی برنامه مرتبط است به نحوهی نمایش اجزای آن در صفحه؛ مانند نمایش یک صفحهی مودال، تغییر رنگ عناصر با عبور کرسر ماوس از روی آنها و در کل منطقی که مرتبط و یا وابستهی به هدف اصلی برنامه نیست. از سوی دیگر منطق تجاری برنامه مرتبط است با مدیریت، تغییر و ذخیره سازی اشیاء تجاری مورد نیاز آن؛ مانند اطلاعات حساب کاربری شخص و دریافت اطلاعات برنامه از یک API که مختص به برنامهی خاص ما است و به همین دلیل نیاز به ابزاری برای مدیریت بهینهی آن وجود دارد. برای مثال اینکه در کجا باید منطق تجاری و نمایشی را به هم متصل کرد، میتواند چالش بر انگیر باشد. چگونه باید اطلاعات کاربر را ذخیره کرد؟ چگونه React باید متوجه شود که اطلاعات ما تغییر کردهاست و در نتیجهی آن کامپوننتی را مجددا رندر کند؟ یک ابزار مدیریت حالت، تمام این مسایل را به نحو یکدستی در سراسر برنامه، مدیریت میکند.
اگر از یک ابزار مدیریت حالت استفاده نکنیم، مجبور خواهیم شد تمام اطلاعات منطق تجاری را در داخل state کامپوننتها ذخیره کنیم که توصیه نمیشود؛ چون مقیاس پذیر نیست. برای مثال فرض کنید قرار است تمام اطلاعات state را داخل یک کامپوننت ذخیره کنیم. هر زمانیکه بخواهیم این state را از طریق یک کامپوننت فرزند تغییر دهیم، نیاز خواهد بود این اطلاعات را به والد آن کامپوننت ارسال کنیم که اگر از تعداد زیادی کامپوننت تو در تو تشکیل شده باشد، زمانبر و به همراه کدهای تکراری زیادی خواهد بود. همچنین اینکار سبب رندر مجدد کل برنامه با هر تغییری در state آن میشود که غیرضروری بوده و کارآیی برنامه را کاهش میدهد. به علاوه در این بین مشخص نیست هر قسمت از state، از کدام کامپوننت تامین شدهاست. به همین جهت نیاز به روشی برای مدیریت حالت در بین کامپوننتهای برنامه وجود دارد.
داشتن تنها یک محل برای ذخیره سازی state در برنامه
همانطور که در قسمت 8 ترکیب کامپوننتها در سری React 16x بررسی کردیم، هر کامپوننت در React، دارای state خاص خودش است و این state از سایر کامپوننتها کاملا مستقل و ایزولهاست. این مورد با بزرگتر شدن برنامه و برقراری ارتباط بین کامپوننتها، مشکل ایجاد میکند. برای مثال اگر بخواهیم دکمهای را در صفحه قرار داده و توسط این دکمه درخواست صفر شدن مقدار هر کدام از شمارشگرها را صادر کنیم، با صفر کردن value هر کدام از این کامپوننتها، اتفاقی رخ نمیدهد. چون state محلی این کامپوننتها، با سایر اجزای صفحه به اشتراک گذاشته نمیشود و باید آنرا تبدیل به یک controlled component کرد، بطوریکه دارای local state خاص خودش نیست و تمام دادههای دریافتی را از طریق this.props دریافت میکند و هر زمانیکه قرار است دادهای تغییر کند، رخدادی را به والد خود صادر میکند. بنابراین این کامپوننت به طور کامل توسط والد آن کنترل میشود. تازه این روش در مورد کامپوننتهایی صدق میکند که رابطهی والد و فرزندی بین آنها وجود دارد. اگر چنین رابطهای وجود نداشت، باید state را به یک سطح بالاتر انتقال داد. برای مثال باید state کامپوننت Counters را به والد آن که کامپوننت App است، منتقل کرد. پس از آن چون کامپوننتهای ما، از کامپوننت App مشتق میشوند، اکنون میتوان این state را به تمام فرزندان App توسط props منتقل کرد و به اشتراک گذاشت. این مورد هم مانند مثال انتقال اطلاعات کاربر لاگین شدهی به سیستم، به تمام زیر قسمتهای برنامه، نیاز به ارسال اطلاعات از طریق props یک کامپوننت، به کامپوننت بعدی را دارد و به همین ترتیب برای مابقی که به props drilling مشهور است و روش پسندیدهای نیست.
Redux چیست؟ ذخیره سازی کل درخت state یک برنامه، در یک محل. به این ترتیب به یک شیء جاوا اسکریپتی بزرگ خواهیم رسید که در برگیرندهی تمام state برنامهاست. یکی از مزایای آن امکان serialize و deserialize کل این شیء، به سادگی است. برای مثال توسط متد JSON.stringify میتوان آنرا در جائی ذخیره کرد و سپس آنرا به صورت یک شیء جاو اسکریپتی در زمانی دیگر بازیابی کرد. یکی از مزایای آن، امکان بازیابی دقیق شرایط کاربری است که دچار مشکل شدهاست و سپس دیباگ و رفع مشکل او، در زمانی دیگر.
تاریخچهای از سیستمهای مدیریت حالت
همه چیز با AngularJS 1x شروع شد که از data binding دو طرفه پشتیبانی میکرد. هرچند این روش برای همگام نگه داشتن View و مدل برنامه، مفید است، اما در Viewهای پیچیده، برنامه را کند میکند. در همین زمان فیسبوک، روش مدیریت حالتی را به نام Flux ارائه داد که از data binding یک طرفه پشتیبانی میکرد. به این معنا که در این روش، همواره اطلاعات از View به مدل، جریان پیدا میکند. کار کردن با آن سادهاست؛ چون نیازی نیست حدس زده شود که اکنون جریان اطلاعات از کدام سمت است. اما مشکل آن عدم هماهنگی model و view، در بعضی از حالات است. Flux از این جهت به وجود آمد که مدیریت حالت در برنامههای React آن زمان، پیچیده بود و مقیاس پذیری کمی داشت (پیش از ارائهی Context و Hooks). در کل Flux صرفا یکسری الگوی مدیریت حالت را بیان میکند و یک کتابخانهی مجزا نیست. بر مبنای این الگوها و قراردادها، میتوان کتابخانههای مختلفی را ایجاد کرد. از این رو در سال 2015، کتابخانههای زیادی مانند Reflux, Flummox, MartyJS, Alt, Redux و غیره برای پیاده سازی آن پدید آمدند. در این بین، کتابخانهی Redux ماندگار شد و پیروز این نبرد بود!
توابع خالص و ناخالص (Pure & Impure Functions)
پیش از شروع بحث، نیاز است با یکسری از واژهها مانند توابع خالص و ناخالص آشنا شد. این نکات از این جهت مهم هستند که Redux فقط با توابع خالص کار میکند.
توابع خالص: تعدادی آرگومان را دریافت کرده و بر اساس آنها، مقداری را باز میگردانند.
// Pure const add = (a, b) => { return a + b; }
توابع ناخالص: این نوع توابع سبب تغییراتی در متغیرهایی خارج از میدان دید خود میشوند و یا به همراه یک سری اثرات جانبی (side effects) مانند تعامل با دنیای خارج (وجود یک console.log در آن تابع و یا دریافت اطلاعاتی از یک API خارجی) هستند.
// Impure const b; const add = (a) => { return a + b; }
// Impure const add = (a, b) => { console.log('lolololol'); return a + b; }
// Impure const add = (a, b) => { Api.post('/add', { a, b }, (response) => { // Do something. }); };
روشهایی برای جلوگیری از تغییرات در اشیاء در جاوا اسکریپت
ایجاد تغییرات در آرایهها و اشیاء (Mutating arrays and objects) نیز ناخالصی ایجاد میکند؛ از این جهت که سبب تغییراتی در دنیای خارج (خارج از میدان دید تابع) میشویم. به همین جهت نیاز به روشهایی وجود دارد که از این نوع تغییرات جلوگیری کرد:
// Copy object const original = { a: 1, b: 2 }; const copy = Object.assign({}, original);
برای مثال در React، برای انجام رندر نهایی، در پشت صحنه کار مقایسهی اشیاء صورت میگیرد. به همین جهت اگر همان شیءای را که ردیابی میکند تغییر دهیم، دیگر نمیتواند به صورت مؤثری فقط قسمتهای تغییر کردهی آنرا تشخیص داده و کار رندر را فقط بر اساس آنها انجام دهد و مجبور خواهد شد کل یک شیء را بارها و بارها رندر کند که اصلا بهینه نیست. به همین جهت، ایجاد تغییرات مستقیم در شیءای که به state آن انتساب داده میشود، مجاز نیست.
متد Object.assign، چندین شیء را نیز میتواند با هم یکی کند و شیء جدیدی را تشکیل دهد:
// Extend object const original = { a: 1, b: 2 }; const extension = { c: 3 }; const extended = Object.assign({}, original, extension);
// Copy object const original = { a: 1, b: 2 }; const copy = { ...original };
// Extend object const original = { a: 1, b: 2 }; const extension = { c: 3 }; const extended = { ...original, ...extension };
روشهایی برای جلوگیری از تغییرات در آرایهها در جاوا اسکریپت
متد slice آرایهها نیز بدون ذکر آرگومانی، یک کپی از آرایهی اصلی را ایجاد میکند:
// Copy array const original = [1, 2, 3]; const copy = [1, 2, 3].slice();
// Copy array const original = [1, 2, 3]; const copy = [ ...original ];
// Extend array const original = [1, 2, 3]; const extended = original.concat(4); const moreExtended = original.concat([4, 5]);
معادل قطعه کد فوق در ES-6 و به همراه spread operator آن به صورت زیر است:
// Extend array const original = [1, 2, 3]; const extended = [ ...original, 4 ]; const moreExtended = [ ...original, ...extended, 5 ];
مفاهیم ابتدایی Redux
در Redux برای ایجاد تغییرات در شیء کلی state، از مفهومی به نام dispatch actions استفاده میشود. action در اینجا به معنای رخدادن چیزی است؛ مانند کلیک بر روی یک دکمه و یا دریافت اطلاعاتی از یک API. در این حالت مقایسهای بین وضعیت قبلی state و وضعیت فعلی آن صورت میگیرد و تغییرات مورد نیاز جهت اعمال به UI، محاسبه خواهند شد.
اصلیترین جزء Redux، تابعی است به نام Reducer. این تابع، یک تابع خالص است و دو آرگومان را دریافت میکند:
تابع Reducer، بر اساس action و یا رخدادی، ابتدا کل state برنامه را دریافت میکند و سپس خروجی آن بر اساس منطق این تابع، یک state جدید خواهد بود. اکنون که این state جدید را داریم، برنامهی React ما میتواند به تغییرات آن گوش فرا داده و بر اساس آن، UI را به روز رسانی کند. به این ترتیب کار اصلی مدیریت state، به خارج از برنامهی React منتقل میشود.
در این تصویر، تابع action creator را هم ملاحظه میکند که کاملا اختیاری است. یک action میتواند یک رشته و یا یک عدد باشد. با پیچیده شدن برنامه، نیاز به ارسال یکسری متادیتا و یا اطلاعات بیشتری از اکشن رسیدهاست. کار action creator، ایجاد شیء action، به صورت یک دست و یکنواخت است تا دیگر نیازی به ایجاد دستی آن نباشد.
مزایای کار با Redux
- داشتن یک مکان مرکزی برای ذخیره سازی کلی حالت برنامه (به آن «source of truth» و یا store هم گفته میشود): به این ترتیب مشکل ارسال خواص در بین کامپوننتهای عمیق و چند سطحی، برطرف شده و هر زمانیکه نیاز بود، از آن اطلاعاتی را دریافت و یا با قالب خاصی، آنرا به روز رسانی میکنند.
- رسیدن به بهروز رسانیهای قابل پیش بینی state: هرچند در حالت کار با Redux، یک شیء بزرگ جاوا اسکریپتی، کل state برنامه را تشکیل میدهد، اما امکان کار مستقیم با آن و تغییرش وجود ندارد. به همین جهت است که برای کار با آن، باید رویدادی را از طریق actionها به تابع Reducer آن تحویل داد. چون Reducer یک تابع خالص است، با دریافت یک سری ورودی مشخص، همواره یک خروجی مشخص را نیز تولید میکند. به همین جهت قابلیت ضبط و تکرار را پیدا میکند؛ همان بحث serialize و deseriliaze، توسط ابزاری مانند: logrocket. به علاوه قابلیت undo و redo را نیز میتوان به این ترتیب پیاده سازی کرد (state جدید محاسبه شده، مشخص است، کل state قبلی را نیز داریم یا میتوان ذخیره کرد و سپس برای undo، آنرا جایگزین state جدید نمود). افزونهی redux dev tools نیز قابلیت import و export کل state را به همراه دارد.
- چون تابع Reducer، یک تابع خالص است و همواره خروجیهای مشخصی را به ازای ورودیهای مشخصی، تولید میکند، آزمایش کردن، پیاده سازی و حتی logging آن نیز سادهتر است. در این بین حتی یک افزونهی مخصوص نیز برای دیباگ آن تهیه شدهاست: redux-devtools-extension. تابع خالص، تابعی است که به همراه اثرات جانبی نیست (side effects)؛ به همین جهت عملکرد آن کاملا قابل پیش بینی بوده و آزمون پذیری آن به دلیل نداشتن وابستگیهای خارجی، بسیار بالا است.
Context API خود React چطور؟
در قسمت 33 سری React 16x، مفهوم React Context را بررسی کردیم. پس از معرفی آن با React 16.3، مقالات زیادی منتشر شدند که ... Redux مردهاست (!) و یا بجای Redux از React context استفاده کنید. اما واقعیت این است که React Redux در پشت صحنه از React context استفاده میکند و تابع connect آن دقیقا به همین زیر ساخت متصل میشود.
کار با Redux مزایایی مانند کارآیی بالاتر، با کاهش رندرهای مجدد کامپوننتها، دیباگ سادهتر با افزونههای اختصاصی و همچنین سفارشی سازی، مانند نوشتن میانافزارها را به همراه دارد. اما شاید واقعا نیازی به تمام این امکانات را هم نداشته باشید؛ اگر هدف، صرفا انتقال سادهتر اطلاعات بوده و برنامهی مدنظر نیز کوچک است. React Context برخلاف Redux، نگهدارندهی state نیست و بیشتر هدفش محلی برای ذخیره سازی اطلاعات مورد استفادهی در چندین و چند کامپوننت تو در تو است. هرچند شبیه به Redux میتوان اشارهگرهایی از متدها را به استفاده کنندگان از آن ارسال کرد تا سبب بروز رویدادها و اکشنهایی در کامپوننت تامین کنندهی Contrext شوند (یا یک کتابخانهی ابتدایی شبیه به Redux را توسط آن تهیه کرد). بنابراین برای انتخاب بین React Context و Redux باید به اندازهی برنامه، تعداد نفرات تیم، آشنایی آنها با مفاهیم Redux دقت داشت.
در واقع ما یک سیستمی داریم که شامل مدلی است از دیتاهای ما و از این مدل برای کوئری گرفتن از دیتابیس استفاده میشود، که البته برای بیشتر پروژههای نرم افزاری، معماری درست و ترجیح داده شدهای هم میباشد.
زمانیکه نیازهای پروژه روز به روز افزورده و پیچیدهتر میشود، مدل CRUD بصورت پیوسته از ارزشش کاسته میشود و از آن سادگی اولیهی در درک و خوانایی آن دور خواهد شد.
ذات CQRS بر آن است که شما مدلهای مختلفی را برای خواندن و نوشتن دیتا داشته باشید. الگوی آن چیزی شبیه به تصویر زیر است
ES از برنامه نویسان میخواهد که مدل سنتی CRUD را فراموش کرده و بجای آن تغییراتی را که روی دیتا صورت گرفته، نیز درج نمایند. اینکار به وسیلهی یک دیتابیس Append-only انجام میشود که به نام Event Store شناخته میشود.
در این معماری ما همهی تغییرات روی دیتا را به صورت Serialize Event ذخیره میکنیم که میتواند دوباره در هر زمانی اجرا شده و current state هر objectی را در اختیار بگذارد.
این روش به ما کمک بزرگی میکند تا وضعیت یک object را در گذشته به راحتی پیدا کنیم و از آن میتوان به غیر از فوایدی که دارد، به عنوان یک Logger نیز استفاده نمود. به دلیل اینکه جزء به جزء تغییرات بر روی state سیستم، در آن ثبت شده است. از آنجاییکه دیتا بصورت serialize ذخیره میشود، بارگزاری آن نیز با سرعت بالایی انجام خواهد شد.
public class Movie : AggregateRoot { public string Title { get; set; } public DateTime ReleaseDate { get; set; } public int RunningTimeMinutes { get; set; } public Movie() { } public Movie(Guid movieId, string title, DateTime releaseDate, int runningTimeMinutes) { //پیاده سازی خواهد شد } }
public class CreateMovieCommand : ICommand { public string Title { get; set; } public DateTime ReleaseDate { get; set; } public int RunningTimeMinutes { get; set; } public CreateMovieCommand(string title, DateTime releaseDate, int runningTime) { Title = title; ReleaseDate = releaseDate; RunningTimeMinutes = runningTime; } }
public class CreateMovieCommandHandler : CommandHandler<CreateMovieCommand> { protected IDomainRepository _repository; public CreateMovieCommandHandler(IDomainRepository repository) { _repository = repository; } public override void Handle(CreateMovieCommand command) { var movie = new Domain.Movie(Guid.NewGuid(), command.Title, command.ReleaseDate, command.RunningTimeMinutes); _repository.Save(movie); } }
public class MovieCreatedEvent : DomainEvent { public Guid MovieId { get { return AggregateRootId; } set { AggregateRootId = value;} } public string Title { get; set; } public DateTime ReleaseDate { get; set; } public int RunningTimeMinutes { get; set; } public MovieCreatedEvent(Guid movieId, string title, DateTime releaseDate, int runningTime) { MovieId = movieId; Title = title; ReleaseDate = releaseDate; RunningTimeMinutes = runningTime; } }
public class Movie : AggregateRoot { public string Title { get; set; } public DateTime ReleaseDate { get; set; } public int RunningTimeMinutes { get; set; } public Movie(Guid movieId, string title, DateTime releaseDate, int runningTimeMinutes) { Apply(new MovieCreatedEvent(Guid.NewGuid(), title, releaseDate, runningTimeMinutes)); } }
public class MovieEventHandler : IHandleDomainEvents<MovieCreatedEvent> { public void Handle(MovieCreatedEvent createdEvent) { using (MoviesContext entities = new MoviesContext()) { entities.Movies.Add(new Movie() { Id = createdEvent.AggregateRootId, Title = createdEvent.Title, ReleaseDate = createdEvent.ReleaseDate, RunningTimeMinutes = createdEvent.RunningTimeMinutes }); entities.SaveChanges(); } } }
protected void OnMovieCreated(MovieCreatedEvent domainEvent) { Id = domainEvent.AggregateRootId; Title = domainEvent.Title; ReleaseDate = domainEvent.ReleaseDate; RunningTimeMinutes = domainEvent.RunningTimeMinutes; }
- ذاتا پیاده سازی این مدل سخت و دشوار است و از آنجاییکه سادگی در پیاده سازی سیستمهای نرم افزاری، یک اصل مهم محسوب میشود، بنابراین استفاده از این مدل محدود میشود به سیستمهای نرم افزاری که مزیتهای گفته شده در قسمت فوق برایشان حیاتی محسوب شود.
- برای پیاده سازی سیستمی با این مدل احتیاج به تیم توسعهای است که با مفاهیم آن کاملا آشنا باشد.
- هر چند امروزه فضای فیزیکی برای ذخیره سازی دیتا ارزان محسوب میشود، اما به هر حال استفاده از این مدل به همراه ES، حجم زیادی از Disk space را خواهد گرفت.
- همانطور که دیدید برای پیاده سازی یک Insert ساده، حجم زیادی کد نوشته شدهاست. بنابراین تولید اینگونه نرم افزارها به زمان بیشتری نیاز دارد.