کیانوش آرین 404443009
تمرین اول معماری نرمافزار
1. Chaos Engineering
در Chaos Engineering، برای این که عملکرد سیستم را تحت شرایط خاص بررسی کنیم خودمان مشکلات مختلفی در آن ایجاد میکنیم و بررسی میکنیم که سیستم چه واکنشی نشان میدهد. مثلا درگاه های خارجی (مثلا درگاه پرداخت، پیامک و ...) را قطع میکنیم، دیتابیس را قطع میکنیم، سرعت اینترنت سرور را کم میکنیم، یا سیستم را تحت فشار ترافیک بالا قرار میدهیم. پس از بررسی رفتار سیستم، میتوانیم چاره هایی بیاندیشیم تا اگر زمانی این مشکلات واقعا در پروداکشن پیش آمدند، سیستم Resilience و Robustness بیشتری داشته باشد. از معروف ترین ابزار هایی که برای Chaos Engineering پدید آمده اند میتوان Chaos Monkey را نام برد که توسط Netflix ایجاد شده که به طور رندوم VM های مختلف در محیط Production را از کار میاندازد تا بتوانیم رفتار سیستم را در آن شرایط بررسی کنیم.
2. Backend for Frontend
در بسیاری از اپلیکیشن های تحت وب، تنها یک بک اند توسعه میدهیم و در تمام فرانت اند هایمان (مثلا اپ سمت کاربر، اپلیکیشن سمت ادمین، در گوشی، دسکتاپ،...) از همان یک بک اند استفاده میکنیم. از جایی که منطق مورد نیاز در فرانت اند های مختلف تا حد زیادی شبیه به یکدیگر است توسعه ی یک بک اند برای تمامی آنها منطقی به نظر میرسد. اما مشکل آنجاست که نیازمندی های non-functional در فرانت اند های مختلف، مثلا سمت کاربر و ادمین، با یکدیگر متفاوت هستند و ممکن است اپ سمت کاربر هزاران درخواست در ساعت داشته باشد درحالی که اپ سمت ادمین تنها چند صد درخواست در روز دارد، یا فرانت اند های مختلف از نظر سطح امنیت با یکدیگر متفاوت باشند. بنابراین، معماری Backend for Frontend پیشنهاد میدهد که برای هر frontend مختلف، یک سرویس backend مخصوص به خودش ایجاد کنیم تا متناسب با نیاز های آن front-end به آن سرویس ارائه کند. این معماری چهار مزیت کلیدی دارد.
1. داده های ارائه شده به هر فرانت اند دقیقا به اندازه نیازش است، بنابراین باعث کاهش استفاده از اینترنت، شارژ موبایل و ... میشود.
2. وابستگی بین تیم های توسعه مختلف به دلیل جداسازی دغدغه ها کاهش مییابد.
3. پیچیدگی در فرانت اند های مختلف کاهش مییابد زیرا دیگر لازم نیست با بک اند بزرگ و کلی ارتباط برقرار کنند.
4. به دلیل اضافه کردن یک لایه BFF میانی، امنیت را افزایش میدهد.
3. AI4SE
به طور کلی هر گاه که از هوش مصنوعی برای کمک در قسمتی از مهندسی نرمافزار استفاده کنیم، به آن AI4SE گفته میشود. امروزه با پیشرفت های مدل های بزرگ زبانی ابزار های بسیاری از جمله Cursor, Windsurf, Claude Code و ... ایجاد شده که توانایی انجام کار هایی از جمله تولید کد، دیباگ کردن، ایجاد داکیومنتیشن، و حتی اجرای دستورات در shell و تست کردن نرمافزار را دارا هستند. به غیر از کمک در برنامه نویسی، میتوان از این ابزار ها در مراحل دیگر مهندسی نرمافزار از جمله تولید و بررسی مورد های کاربری، نیازمندی ها و ... استفاده کرد.
4. SE4AI
SE4AI به هنگامی گفته میشود که از اصول مهندسی نرم افزار برای ایجاد یا بهبود و مدیریت سیستم های هوش مصنوعی استفاده کنیم. برای مثال، میدانیم که سیستم های هوش مصنوعی غیر قابل پیش بینی هستند، و این غیر قابل پیش بینی بودن تست کردن عملکرد آنها را دشوار میکند. یکی از سوال های زمینه SE4AI این است که چگونه سیستمی طراحی کنیم که بتواند عملکرد ایجنت های هوش مصنوعی را تست کند. یا ممکن است نیاز داشته باشیم تا Guardrail هایی را بر روی مدل های هوش مصنوعی نصب کنیم تا پاسخ های غیرقابل قبول یا محرمانه به کاربران ندهند. این Guardrail ها باید سیستم های قابل اعتماد نرمافزاری باشند تا مطمئن باشیم در صورت تولید پاسخ های نامناسب، این پاسخ ها به کاربران نمایش داده نشود. یا مثلا برای تولید، آزمایش و مقایسه Prompt هایی که برای استفاده از مدل های هوش مصنوعی استفاده میکنیم نیاز به یک سیستم مدیریت متن داریم که باید تحت اصول مهندسی نرمافزار تولید شود. به این گونه کارکرد های مهندسی نرمافزار برای بهبود و مدیریت سیستم های هوش مصنوعی، SE4AI گفته میشود.
5. MLOps
ایجاد سیستم های مبتنی بر یادگیری ماشین شامل مراحل مختلفی از جمله جمع آوری داده، pre-process، یادگیری، ارزیابی، دیپلوِی، و مانیتورینگ میباشد. در MLOps، به طراحی pipeline هایی میپردازیم که قسمت هایی از این فرایند را خودکار کنند. برای مثال، با وارد شدن داده های جدید، به طور خودکار مدل را دوباره train کرده و دیپلوی و ارزیابی کنند. این کار CT (Continuous Training) نام دارد. همچنین، ورژنینگ نسخه های مختلف مدل، پارامتر ها و وزن های مدل به همراه مانیتور کردن عملکرد مدل ها و تشخیص Data Drift و Concept Drift از جمله کار هایی هستند که در MLOps انجام میشوند.
6. Infrastructure as Code
در Infrastructure as Code، بجای آن که سرور، دیتابیس، یا شبکه را به صورت دستی مدیریت و کانفیگ کنیم، برای انجام آن اسکریپت هایی مینویسیم تا آن عملیات را به طور خودکار انجام دهند. مثلا هر بار بعد از تعویض سرور، برای انجام تنظیمات اولیه بجای انجام دستی این تنظیمات، برای آنها اسکریپت و کانفیگ های آماده ای ایجاد میکنیم تا این تنظیمات راحت تر انجام شوند. از مزایای IaC میتوان امکان خودکار سازی، مستند سازی تنظیمات، عدم وابستگی به یک شخص خاص برای انجام تنظیمات، و مقیاس پذیری ساده تر را نام برد. Ansible و Terraform از معروف ترین ابزار ها جهت انجام این کار هستند. برای ایجاد IaC ها، دو روش Imperative و Declarative وجود دارد. در روش Imperative به ترتیب Command هایی که باید انجام شوند را بیان میکنیم، در حالی که در روش Declarative یک توصیف از وضعیت مطلوب سیستم (مثلا یک فایل yml) به ابزار های IaC ارائه میکنیم و این ابزار ها، عملیات مورد نیاز برای رسیدن به وضعیت مطلوب را برای ما انجام میدهند.
7. API Gateway and Service Mesh
در روش ارتباط API Gateway، یک Gateway کلی داریم که کلاینت ها برای تمامی عملیات خود به آن Gateway مراجعه کرده و API مورد نیاز خود را فراخوانی میکنند. این مدل یک مدل client to service است که در آن کلاینت ها به سرویس (همان API Gateway) مراجعه میکنند. درحالی که Service Mesh، یک مدل service to service میباشد و معمولا برای مدیریت ارتباطات میان میکروسرویس ها استفاده میشود. در این مدل، در کنار هر میکروسرویس یک پروکسی به نام Sidecar قرار میدهیم (که به آن Data Plane نیز گفته میشود) و این پروکسی، تمامی ارتباطات ورودی و خروجی میکروسرویس را مدیریت میکند. همچنین یک Control Plane داریم که پروکسی ها را مدیریت کرده و به هر Sidecar میگوید که چگونه ارتباطات را مدیریت کند، پروتکل های امنیتی را اعلام کرده و آنها را مانیتور میکند. این مدل ارتباطی سبب میشود تا مدیریت ارتباطات بین سرویس ها ساده تر شده، امنیت افزایش پیدا کرده و قابلیت مشاهده سیستم بالا برود.
8. Command Query Responsibility Segregation
معماری CQRS عملیاتی که در داده ها تغییر ایجاد میکنند (update, delete, create) را از عملیاتی که تغییری ایجاد نمیکنند (read) جدا کرده و دسته اول را command ها و دسته دوم را query ها نامگذاری میکند. همچنین، این معماری دیتامدل های مورد استفاده توسط کامند ها و کوئری ها را کاملا از یکدیگر جدا کرده و حتی این قابلیت را به ما میدهد که برای هر کدام از دیتابیس های مختلفی استفاده کنیم. این کار به پرفورمنس ما کمک میکند زیرا میتوان از دیتابیس های مختلف بهینه شده برای هر کدام از عملیات های خواندن و تغییر داده ها استفاده کرد. همچنین این معماری به ما قابلیت scalability زیادی برای دیتابیس کوئری ها میدهد زیرا این دیتابیس ها معمولا از سرعت بسیار بالا تری برخوردارند. همچنین با استفاده از این معماری میتوان میتوان مدیریت امنیت و پیچیدگی را بیشتر در سمت command ها نگاه داشت و آنها را از قسمت query ها جدا کرد.
9. Event-Driven Architecture
در معماری Event-Driven، کامپوننت های مختلف از طریق تولید و واکنش دادن به Event های مختلف با یکدیگر ارتباط برقرار میکنند. در این معماری، سه عضو کلیدی وجود دارند. اولین عضو، Event Producer ها هستند که همان کامپوننت های تولید کننده Event هستند و پس از ایجاد شرایط خاصی، یک Event تولید کرده و ارسال میکنند. این کامپوننت ها اطلاعی از دریافت کنندگان ایونت ندارند و تنها آنرا تولید کرده و ارسال میکنند. دومین عضو، Event Router ها هستند که در واقع middleware هایی هستند که ایونت ها را دریافت کرده و ایونت های متناسب با نیاز هر کامپوننت مصرف کننده را به آن هدایت میکنند. سومین عضو، Event Consumer ها هستند که ایونت ها را دریافت کرده و متناسب با آنها واکنش نشان میدهند. هر consumer به تعدادی Event خاص Subscribe کرده و هر گاه آن Event منتشر شود، از آن مطلع میشوند. این معماری مزایایی از جمله Loose Coupling و ارتباط Async میان کامپوننت ها را به ما میدهد زیرا کامپوننت ها برای مطلع شدن از یکدیگر، نیازمند به ارسال درخواست و دریافت پاسخ ندارند. همچنین، این معماری Scalability و Flexibility سیستم را افزایش میدهد.
10. Serverless Architecture
در معماری Serverless، بجای این که توسعه دهنده یک سرور خریداری کرده و تمام زیرساخت های لازم را روی آن سرور بالا بیاورد، از سرویس های Cloud مانند آمازون، گوگل، و یا Azure برای اجرای کد ها استفاده میکند. در این معماری، توسعه دهنده Function های مختلفی را تعریف میکند و آنها را بر روی Cloud قرار میدهد. این فانکشن ها به صورت Event-Driven اجرا شده و در ابتدا غیر فعال بوده، و پس از یک ایونت (مثلا درخواست کاربر، آپلود فایل، ...) فراخوانی شده و وظایف خود را انجام داده و دوباره خاموش میشوند. در این روش توسعه دهندگان لازم نیست هزینه کامل سرور را پرداخت کنند و میتوانند تنها به اندازه فراخوانی شدن فانکشن هایشان پول بپردازند. همچنین، Scaling و هندل کردن کاهش و افزایش بار درخواست ها توسط ارائه دهنده سرویس Cloud به طور خودکار انجام شده و از دغدغه های توسعه کننده حذف میشود.
11. API-first Approach
در این روش پیش از آغاز توسعه، ابتدا API ها به طور کامل طراحی شده و endpoint های آنها، فرمت و داده های درخواست و پاسخ و مستندات API ها به طور کامل مشخص میشوند تا تمام تیم ها از کارکرد آنها کاملا مطلع باشند. این به تیم های مختلف توسعه کمک میکند تا از فرمت نهایی درخواست ها آگاه باشند، و بتوانند توسعه بخش مربوط به خود را (مثلا front-end) با کمک داده های mock که از مستندات API استخراج شده پیش ببرند و به طور موازی کار کنند.
12. Domain Driven Design
در DDD، سعی میکنیم تا ساختار کلاس ها و کدمان تا جای ممکن شبیه به ساختار کسب و کار باشد. برای مثال، اگر برای یک فروشگاه دارای موجودیت های "مشتری"، "کالا" و "پرداخت" یک سیستم طراحی میکنیم، تقسیم بندی و نامگذاری کلاس های این سیستم را نیز به شکل "مشتری"، "کالا" و "پرداخت" انجام میدهیم. این روش یک زبان مشترک به تیم های مختلف از جمله افراد کسب و کار و افراد فنی میدهد، برای مثال وقتی کسی از "مشتری" صحبت میکند، همه میفهمند که منظور از این مشتری چه موجودیتی است. همچنین، در حوزه های گسترده ای که یک کلمه ممکن است در چندین مکان به معانی مختلف استفاده شود، context های مختلف تعریف میشوند. مثلا context انبار و context فروشگاه میتوانند هر دو دارای موجودیت "مشتری" باشند اما معنی این مشتری در این دو context متفاوت تعریف میشود. به این عمل، Bounded Context میگویند.
13. Hexagonal Architecture
در معماری Hexagonal، منطق کسب و کار با استفاده از تعدادی Adapter به طور کامل از دیگر قسمت ها محافظت میشود. در واقع بجای این که منطق کسب و کار به طور مستقیم با UI و DB در ارتباط باشد، تعدادی Adapter در میان این ارتباط قرار میگیرند و به عنوان واسط بین منطق کسب و کار و دیگر اجزا نقش ایفا میکنند. همچنین، تعدادی Port تعریف میشوند که قوانین و شرایطی که Adapter ها برای ارتباط با خارج باید از آنها پیروی کنند را مشخص میکنند. به دلیل Decouple شدن قسمت های مختلف نرمافزار، میتوان هر قسمت را به طور جداگانه و بدون نگرانی از تغییرات خارجی تست کرد، و یا کامپوننت های مختلف از جمله دیتابیس را بدون نگرانی از خراب کردن دیگر قسمت های نرمافزار تعویض کرد. همچنین، این معماری امکان توسعه جداگانه قسمت های مختلف نرمافزار توسط تیم های مختلف را فراهم میکند.
14. Event Sourcing
در سیستم های عادی، داده ها را به شکل یک مقدار عددی یا متنی نگه داری میکنیم. اما در Event Sourcing، بجای این که تنها یک عدد یا متن برای مقدار فعلی داده ها داشته باشیم، یک لیست نگهداری میکنیم که تمامی تغییرات داده از ابتدا تا انتها در آن نگهداری میشوند. مثلا، برای آدرس کاربران مقدار لیست میتواند اینگونه باشد:
address.added (Tehran)
address.updated (Shiraz)
این گونه بجای تنها مقدار فعلی، سابقه تمامی تغییرات داده ها را داریم و این به ما امکان انجام انواع تحلیل های داده، دیباگ با کمک بازسازی استیت گذشته برنامه و ... میدهد. همچنین، با استفاده از این روش عملیات update بجای تغییر ویرایش مقدار قبلی تنها لازم است یک مقدار جدید به لیست Append کند که از نظر پرفورمنس عملکرد بهتری دارد.
15. Low-Code / No-Code Platforms
این پلتفرم ها، با ایجاد زیر ساخت های تصویری و منطقی به کاربران امکان ایجاد برنامه هایی بدون کد نویسی یا با مقدار بسیار کم کد نویسی میدهند. ایجاد برنامه در این پلتفرم ها بسیار ساده تر از برنامه نویسی سنتی است و میتواند توسط هر شخصی که لزوما با اصول برنامه نویسی آشنا نیست با هزینه کمتر انجام شود. در مقابل، این برنامه ها انعطاف پذیری کمتری به توسعه دهنده میدهند و امکان انجام هر عملی را برای افراد فراهم نمیکنند. از این پلتفرم ها برای ایجاد و آزمایش دمو های اولیه و کاهش هزینه ها استفاده میشود. از جمله این پلتفرم ها میتوان Bubble, Zapier, Glide و Webflow را نام برد.
16. Business Process Management Systems
BPMS ها سیستم هایی هستند که فرایند های سازمانی را کامپیوتری و خودکار میکنند. برای مثال، اگر یک فرایند سازمانی شامل مراحلی از جمله ارسال نامه، بررسی توسط بخش مالی، امضای مدیر عامل، اعلام تایید هست، این فرایند ها داخل نرم افزار BPMS به شکل فایل های BPMN (Business Process Model and Notation) تعریف شده و مراحل آن هر بار به طور سیستماتیک به ترتیب اجرا میشود و به طور خودکار برای افراد مختلف از جمله مدیر عامل، بخش مالی، و ... ارسال میشوند. این برنامه ها معمولا در سازمان های بزرگ استفاده شده و به خودکار سازی فرایند ها، نظارت بر آنها، مانیتورینگ و گزارش گیری کمک فراوانی میکنند. از معروف ترین برنامه های BPMS میتوان به Camunda, jBPM, Bizagi اشاره کرد.
17. Message Queue (Kafka, RabbitMQ)
Message Queue ها واسط هایی هستند که در میان دو سیستمی که میخواهند با یکدیگر ارتباط برقرار کنند قرار میگیرند و بجای این که این سیستم ها به طور مستقیم با یکدیگر ارتباط برقرار کنند، پیام ها را به این Queue داده و یا از آن دریافت میکنند. این Queue ها در مدیریت ارتباطات مثلا هنگام شلوغ بودن و یا در دسترس نبودن سیستم ها کمک میکنند تا ارتباط به شکل Async انجام شده و سیستم ها بتوانند طبق زمان بندی خودشان و هر وقت که بخواهند پیام ها را دریافت و به آنها رسیدگی کنند. Kafka یکی از این ابزار هاست که به طور خاص برای سیستم های بزرگ با حجم بالای ارتباطات مثلا چندین میلیون پیام تولید شده و RabbitMQ یکی دیگر از این ابزار هاست که برای ارتباطات Reliable ایجاد شده است.
18. Containers (Docker) and Container Orchestration (Kubernetes)
کانتینر ها محیط هایی برای اجرای نرمافزار هستند که تمامی سورس کد، Dependency ها، تنظیمات، و محیط اجرای نرمافزار ها را در خود جمع آوری کرده و قابلیت اجرای آنها را بر روی هر سیستمی با Virtualization فراهم میکنند. با کمک کانتینر ها، اجرای برنامه در یک محیط جدید بدون نیاز به ورژن کنترل و انجام دوباره تنظیمات محیط برقرار میشود. Docker معروف ترین ابزار Containerization است که تمامی مولفه های مورد نیاز برای اجرای نرمافزار را در Docker Image ها جمع آوری میکند. هنگامی که تعداد زیادی کانتینر (صد ها و یا هزاران) داشته باشیم که به صورت توزیع شده برنامه را اجرا میکنند، به یک ابزار نیاز داریم که مدیریت لود، اسکیل کردن، ریکاوری از مشکلات، و به روز رسانی نرمافزار را برای این تعداد از کانتینر ها انجام دهد. به این کار Container Orchestration گفته میشود و توسط ابزار هایی از جمله Kubernetes به طور خودکار انجام میشود. این ابزار، هر کانتینر (یا تعدادی کانتینر به شدت مرتبط با یکدیگر) را در یک Pod قرار داده، و Pod ها را بین Node ها که سرور های اجرا کننده هستند پخش میکند. به تمامی Node هایی که برای اجرای نرمافزار با یکدیگر همکاری کرده و کار میکنند Cluster گفته میشود.
19. Multi-Tenacy Architecture
در معماری Multi-Tenacy، تعدادی گروه کاربر مختلف از یک نرمافزار استفاده میکنند، بدون این که داده ها و تنظیمات هر گروه بر استفاده از نرمافزار گروه دیگر تاثیر بگذارد. برای مثال، ممکن است که چندین شرکت از یک نرمافزار CRM (مدیریت ارتباطات با مشتری) استفاده کنند. در این صورت، داده های هر شرکت، باید به روشی از داده های دیگر شرکت ها جداسازی شود. چند روش برای انجام این کار وجود دارد. روش اول جداسازی کامل دیتابیس ها است، به طوری که نرم افزار مورد استفاده توسط هر گروه کاربر از یک دیتابیس کاملا جداگانه استفاده میکند. روش دوم، جداسازی Schema هاست، به طوری که نرم افزار همیشه از یک دیتابیس استفاده کرده، اما هر گروه کاربر Schema مخصوص به خود را دارد. روش سوم، استفاده از دیتابیس و Schema مشترک، و جداسازی رکورد های مربوط به هر گروه کاربر با استفاده از ستونی مانند TenantId میباشد. این روش کمترین هزینه را دارد اما از نظر امنیت و ایزوله سازی، ضعیف تر از روش های دیگر است.
20. Data Migration
Data Migration هنگامی است که میخواهیم داده ها را از یک پایگاه داده به یک پایگاه داده دیگر منتقل کنیم. برای مثال، ممکن است بخواهیم دیتابیس خود را از PostgresSQL به MongoDB تغییر دهیم یا بجای ذخیره سازی داده ها در یک دیتابیس Cloud، آنها را در یک دیتابیس جدید در سرور و کنار خود اپلیکیشن نگهداری کنیم. در این موارد، ممکن است به دلیل تفاوت های موجود در دیتابیس های مبدا و مقصد، مانند SQL و no-SQL بودن، تفاوت در دیتا مدل ها و یا قوانین پایگاه داده، مجبور شویم عملیاتی برای هماهنگ سازی داده ها با نوع جدید ذخیره سازی انجام دهیم که به این کار Data Transform گفته میشود.
منابع:
geeksforgeeks
aws amazon
bytebytego
redhat
wikipedia
samnewman
medium
گفت و گو با Chatgpt و Gemini