یکی از مفاهیم مهم در معماری نرم افزار، بحث زیرساخت هست. IaC این امکان رو به مهندسان میده که به صورت غیردستی، زیرساخت رو مدیریت و تنظیم کنند. زیرساخت هم شامل سرورها، شبکه، دیتابیس و … میشه. به بیان ساده، با نوشتن script یا فایل های مربوط به configureation، این امکان فراهم میشه که زیرساخت config بشه و همچنین قابلیت استفاده مجدد هم فراهم میشه (Reusability). این اسکریپت معمولا به صورت فایل های yaml یا json نوشته میشه.
همچنین دو نوع IaC وجود داره: Imperative و Declarative
در نوع Imperative، تمرکز روی مشخص کردن نحوه رسیدن به یک state خاص هست ولی در کدهای Declarative، صرفا وضعیت نهایی مورد انتظار در زیرساخت نوشته میشه و از ابزارها خواسته میشه که به اون state برسند. طبیعی هست که در نوع دوم، وضعیت قبلی زیرساخت برای ابزارها مشخص هست و ازش اطلاع دارن. نوع Declarative احتمالا چالش های کمتری نسبت به Imperative داره ولی کاستومایز کردن در اون هم سخت تره. به همین دلیل استفاده از ترکیب این دو پیشنهاد میشه.
همچنین IaC مزیت های زیادی داره از جمله اتومات کردن فرایند، داشتن version control، انعطاف پذیری، بالابردن سرعت و کاهش خطاهای انسانی و میتونه در پایپ لاین های CI/CD هم قرار بگیره.
ابزارهای رایج برای IaC موارد زیر هستند:
Terraform(Declarative), Cloud Formation, Ansible(Imperative), OpenToFu(Declarative), Puppet
2. API Gateway & Service Mesh
اغلب در بحث ماکروسرویس ها، تعداد زیادی سرویس در سمت بک اند وجود دارد، API Gateway همان نقطه ابتدایی دسترسی به این سیستم هاست و مانند یک رابط بین کلاینت و سرویس عمل میکنه و زمانی که یک درخواست ارسال میشه، مسئول اینه که اون درخواست رو به سرویس مرتبطش ارجاع بده و اطمینان حاصل میکنه هر چیزی که وارد میشه، از policy و کانفیگ مورد نظر ما تبعیت میکنه. علاوه بر routing، گیت وی کارهای مربوط به احراز هویت، rate limiting و کش کردن رو هم برعهده دارد.
ابزار های Gateway های رایج: NGINX، AWS API Gateway
توجه داریم API Gateway در صورتی که به صورت مناسب طراحی نشه میتونه به یک single point of failure تبدیل بشه.
مفهوم service mesh به API Gateway نزدیکه ولی service mesh لایه زیرساختیه که مسئولیت ارتباط داخلی سرویس ها با یکدیگر رو (با ایجاد یک پلتفرم centralized) بر عهده دارد و به بهینه سازی ارتباط بین اپلیکیشن ها کمک میکند به این صورت که برای هر سرویس، یک پروکسی به نام sidecar وجود داره (مثل Envoy) که اطمینان حاصل میکنه جریان ترافیک به صورت عادی برقراره. نکته مثبت service mesh اینه که لازم نیست هر بار یک ستاپ جدید ایجاد کنیم، همه این ها به صورت اتومات فراهم میشه، کافیه که وقتی capability های جدید اضافه میشه، سرویس ها بهش subscribe کنند. سرویس مش همچنین کارهایی مانند traffic routing, retry, circuit breaking رو انجام میده.
از ابزارهای Service Mesh هم میتونیم به Istio, Linkerd اشاره کنیم.
در نهایت API Gateway و Service mesh وجه اشتراک زیادی دارند، از جمله مدیریت ارتباط بین ماکروسرویس ها، افزایش امنیت (از طریق authentication و encrytion) و مدیریت ترافیک.
3. CQRS:
تکنیک CQRS یک پترن معماری ست که در اون، سرویس های مربوط به خوندن دیتا از سرویس های مربوط به ایجاد تغییر در دیتا، جداسازی میشه. بنابراین دو مدل برای دیتا وجود داره: مدل command و مدل Query. در مدل کامند، دستورات مربوط به آپدیت، ساخت و حذف وجود داره و در مدل کوئری، دستور خوندن از دیتابیس رو داریم.
به این ترتیب performance و scalability سیستم بهتر خواهد شد. البته این تکنیک بیشتر در مواقعی کاربرد داره که workload عملیات خوندن و عملیات تغییر در دیتا با هم تفاوت زیادی داشته باشه.
این روش به این خاطر مفیده که دیتابیس بهینه برای read و modification دیتا با هم تفاوت دارد. برای عملیات خوندن، دیتابیسی بهینه ست که نرمال نشده باشه و در نتیجه نیاز به join های زیادی نداشته باشیم. ولی برای ایجاد تغییرات، دیتابیس بهینه باید نرمال باشه.
دو حالت در خصوص پیاده سازی CQRS وجود داره. این امکان وجود داره read model و write model رو داخل یک data store داشته باشیم. یا اینکه یک data store جداگانه برای نوشتن و یک data store برای خوندن داشته باشیم. در این حالت میتونیم از دیتابیس های متفاوتی برای هر یک استفاده کنیم. به عنوان مثال، میتوان از دیتابیس های NoSQL برای خواندن و از دیتابیس های Relational برای نوشتن استفاده کرد.

4. Event-Driven Architecture:
این دیزاین پترن، به نوعی در مقابل Request driven architecture قرار می گیرد. و نحوه کار آن به این صورت است که سرویس ها در آن به شکل مستقیم با یکدیگر ارتباط ندارند و مبنای تعامل آن ها با یکدیگر، از طریق event هاییه که رخ میده. تعدادی از کامپوننت های سیستم، آن هایی هستن که این Event ها رو تولید و منتشر می کنن. سایر کامپوننت ها نیز آن هایی هستن که مسئول پاسخ دادن به این Event ها می باشند. البته یک المان دیگر، به نام Event bus یا Message broker هم به عنوان واسط وجود داره که ایونت های مرتبط رو به دریافت کننده های مربوطه route میکنه. ابزارهایی مثل Kafka یا RabbitMQ نمونه هایی از ابزار این بخش هستند. به بیان دیگر، broker ها مثل یک هاب عمل میکنن و event consumerها برای دریافت انواع خاصی از پیام ها subscribe شون میکنن.
مزیت این پترن اینه که کامپوننت ها نسبت به همدیگه، loosely coupled هستند و به این ترتیب Extensibility بالایی در سیستم خواهیم داشت. همچنین نوع ارتباطشون هم async هست.
معماری publish/subscribe، Message Queue و Event Sourcing از انواع معماری Event-Driven محسوب میشن. به عنوان مثال وقتی در یک سیستم ثبت سفارش، کاربری خرید انجام میده، این اتفاق یک سیگنال به همه اجزا، اعم از محاسبه فاکتور، ارسال ایمیل یا پیامک، انبارداری و حمل و نقل ارسال می کنه و هر یک از این مصرف کننده ها با توجه به functionality خودش، به این پیام واکنش نشون میده.

5. Serverless Architecture:
این نوع معماری به سناریویی گفته میشه که مدیریت سرورها در زمان اجرای کد تحت کنترل ما نیست، بلکه کل عملیات مربوط به زیرساخت، مانند نگهداری و Scale کردن، توسط خود cloud provider انجام میشه. به بیان دیگه، سرور داریم ولی تحت نظارت مستقیم ما نیست. در عوض کاری که انجام میدیم، توسعه FaaS (همان Function as a service) است. این پلتفرم ها همچنین به صورت Pay as you go کار میکنن و بر اساس مدت زمانی که کد روی سرور اجرا شده هزینه داره.
معماری serverless نیز به صورت event-based هست، یعنی درخواست های HTTP، آپلود کردن فایل، تغییر در db و موارد این چنینی باعث فراخوانی توابع میشن. این معماری از این جهت نسبت به ماشین های مجازی یا کانتینرها برتری داره که زمان خیلی کمتری رو برای آماده سازی نیاز داره (چند ثانیه در مقایسه با چند دقیقه) و همچنین scaling اش به شکل خودکار انجام میشه در حالی که در کانتینرها به پلتفرم هایی مثل کوبرنتیز نیاز داریم.
پلتفرم های رایج Serverless مانند: AWS, Azure, Google.
توجه داریم که در این معماری، مفاهیمی مثل cold start (یعنی طول کشیدن زمان بیشتری برای اجرا شدن یک تابع برای اولین بار یا بعد از مدت زمان زیادی غیرفعال بودن) و همچنین محدودیت concurrency نقش پررنگی دارند.

6. API-first Approach:
این روش به این مفهوم است که اولین چیزی که قبل از شروع کد مورد توجه و طراحی قرار می گیره، API ها هستن. در واقع با اولویت دادن به API، اون رو به عنوان بلوک های سازنده نرم افزار در نظر میگیرن. با ابزار مخصوص مانند Swagger و با حضور تیم های مختلف از جمله بک اند و فرانت اند و حتی QA، ساختار API طراحی و داکیومنت میشه و به این ترتیب مورد تایید همگی قرار میگیره. زمانی که ورودی ها و خروجی ها و status code های مربوط به یک endpoint از قبل مشخص شده باشن، هر یک از تیم ها میتونه به صورت موازی از اون استفاده کنه. این موضوع باعث افزایش سرعت توسعه و کاهش conflict ها در زمان integration میشه. همچنین تاثیر مثبتی رو تجربه توسعه دهنده (Developer experience) داره چرا که API ها از داکیومنت و ساختار خوبی برخوردارن.
به طور مشخص تر، وقتی از ابتدا صرفا کد نوشته میشه و بعدا سراغ API میریم، سمت فرانت اند به بخش اندکی از دیتا دسترسی دارن و این موضوع تسک هایی که میتونن تکمیل کنن رو کمتر میکنه، در حالی که اگر از ابتدا یک API مشخص داشته باشیم، همه بخش های نرم افزار به دیتای واحد و جامعی دسترسی دارن.
7. Domain Driven Design:
طراحی Domain driven یک دیدگاه طراحی سیستمه که حوزه اصلی بیزینس رو به عنوان مرکز در نظر می گیره. به همین دلیل در این نوع دیزاین، باید بیزینس به خوبی مدل شه چرا که کد هم قراره به گونه ای نوشته شه که با مدل ذهنی افراد بیزینس اصلی منطبق باشه. یعنی در نهایت یک مدل مشترک بین افراد تکنیکال و بیزینسی ایجاد میشه که برای هر دو قابل درکه. این نوع طراحی چند مفهوم پایه ای داره:
Domain: حوزه ای از فعالیت که نرم افزار ما روی آن تمرکز داره.
ubiquitous language: توافق روی یک سری از واژه های مشخص.
Entities: موجودیت ها
Value object: یک آبجکتی که صرفا با مقداری که بهش داده میشه تعریف میشه و هویت مستقل نداره. (مثل شماره ها و آدرس ها)
Aggregate: یک دسته از آبجکت های مرتبط با هم که با آن ها مثل یک واحد مجزا رفتار خواهد شد
Bounded context: تقسیم بندی هر دامنه از نرم افزار به بخش هایی که هر یک زبان، مدل، قوانین و داده های خاص خودش رو داره. (مثل بخش حساب کاربری، یا مدیریت محصول)
از مزایای DDD میشه به همراستا بودن با بیزینس اشاره کرد و اینکه در اغلب مواقع، سیستم ها ماژولار و decoupled هستن و طبیعتا ارتباط بین توسعه دهندگان و domain expert ها رو هم بهتر میکنه. البته برای کاربردهایی در scale کوچک توصیه نمیشه چون در این صورت over head اش زیاد خواهد بود.
8. Hexagonal architecture:
معماری هگزاگونال که به معماری ports و adaptors هم شناخته میشه، هدفش جداسازی هسته اصلی بیزینس از لایه های خارجی مثل دیتابیس و API ها و UI هست و به نوعی منطق بیزینس رو نسبت به مسائل تکنیکال ایزوله میکنه. به این ترتیب core اصلی بیزینس اطلاعی درمورد فریمورک ها و دیتابیس ها و اجزاء فنی سیستم نداره و port ها تعیین میکنن که این هسته اصلی چگونه با دنیای بیرونی ارتباط برقرار کنه. Adapter ها نیز اجزایی هستند که پورت ها رو پیاده سازی می کنند مثل http controller یا kafka
همچنین این پورت ها میتونن خارجی یا داخلی باشن (outbound, inbound) بسته به اینکه آیا توسط actor های خارجی برای صدا زدن اپلیکیشن اصلی استفاده میشن یا توسط اپ به کار گرفته میشن که با سرویس های خارجی ارتباط برقرار کنن.
این نوع معماری testability را تا حد قابل توجهی آسون تر میکنه و همچنین سیستم رو loosely coupled میکنه چرا که میشه دیتابیس ها، API ها و سایر اجزاء رو بدون تغییر دادن هسته اصلی، جایگزین کرد.
9. Event Sourcing
الگوی Event Sourcing یک پترن معماریه که در اون ما استیت فعلی یا نهایی سیستم رو ذخیره سازی نمیکنیم به صورت مستقیم، بلکه یک زنجیره یه متوالی از اتفاقات که رخ دادن رو سیو میکنیم و هر زمانی که بخواهیم استیت فعلی سیستم رو متوجه بشیم باید اون ایونتها رو کنار هم قرار بدیم (به نوعی از هر ترنزکشن لاگ گرفته میشه.)
طبیعتاً این کار باعث میشه که دیباگ کردن و track کردن خطاها آسونتر بشه ولی نیاز به فضای ذخیره سازی بیشتری وجود داره به خاطر اینکه هیچ چیزی حذف یا overwrite نمیشه و هر یک از اتفاقات باید به زنجیره اتفاقات قبلی append بشن. درحالی که اگر از این پترن استفاده نشه، همون رکورد قبلی، هر دفعه آپدیت میشه.
این الگو برای tracability و تحلیل رفتار بسیار مفیده چرا که در صورت کشف باگ، امکان بازسازی همون سناریو رو فراهم میکنه ولی از پیچیدگی بیشتری ممکنه برخوردار باشه و از طرفی هم حجم بالای داده ها چالش ایجاد کنه. البته برای حل مشکل حجم، میشه از تکنیک هایی مثل snapshot استفاده کرد، یعنی در پایان چرخه های مشخص از سیستم، وضعیت سیستم در آن لحظه ثبت بشه و از اون به بعد، مثل قبل عمل بشه. مثلا در یک فروشگاه لازم نیست برای محاسبه سود، هر دفعه تمام تراکنش ها از زمان تاسیس فروشگاه با هم تجمیع بشن؛ بلکه کافیه state مربوط به هفته پیش رو داشته باشیم و transaction های هفته جاری رو بر اون اعمال کنیم.
10. Low-code/No-code platforms:
این پلتفرمها محیط توسعهای هستند که به کاربرا این امکان رو میده که با کمترین مقدار کد زدن و با استفاده از رابط های گرافیکی و کارهایی مثل درگ اند دراپ یک اپلیکیشن توسعه بدن. در بعضی مواقع بخش low code مربوط میشه به وقتی که کسی میخواد customization پیشرفتهای داشته باشه ولی در خیلی از مواقع هم میشه بدون کد زدن و صرفا با انجام configuration، به خروجی مد نظر رسید. همچنین امکان اتصال به دیتابیس ها و وب سرویس ها و حتی deployment هم در برخی از این پلتفرم ها وجود دارد.
از نکات مثبت این نوع پلتفرمها development سریع هست، همچنین کاهش هزینهها رو هم نتیجه میده و اینکه افرادی که لزوماً توسعه دهنده نیستن هم میتونن با این ابزارها کار کنن، مثل تیم مارکتینگ یا منابع انسانی. از طرفی هم به پروتوتایپ زدن کمک میکنه. ولی از سمت دیگه مشکلی مثل vendor lock-in داره و ما کنترل کمتری روی ابزاری که در دستمون هست داریم، بنابراین ممکنه scalability خوبی هم نداشته باشیم.
به عنوان مثال، Webflow یکی از این ابزارهاست که برای ساخت وبسایت به کار میره یا از Glide برای ساخت اپلیکیشن موبایل می توان استفاده کرد.
11. Business Process Management Systems (BPMS):
مفهوم BPMS یک پلتفرم نرمافزاری که این امکان رو میده تا فرایندهای بیزینسی طراحی اجرا و مانیتور بشن. تنها فایده اون مربوط به اتومیشن نیست، باعث افزایش بهره وری و بهبود فرایندها میشه.
در کل خود یک فرایند بیزینسی مجموعهای از گامها و تسکها هستش که با ترتیب خاصی انجام میشن تا یک هدف بیزینسی محقق بشه، مثل فرایند اعطای وام یا دریافت خسارت از بیمه.
کارهایی که یک BPMS میتونه انجام بده شامل این موارد هست: مدل کردن اجرا سازی خودکار فرایندها، مانیتورینگ، بهینه سازی و تشخیص bottleneck و تخصیص تسک به افراد. این سیستم همچنین قابلیت integrate شدن با سایر ابزارها مثل ERP و CRM رو داره.
مشکل vendor lock in و همچنین overhead و پیچیدگی در اینجا هم دیده میشه.
از جمله نمونههای این ابزار:Appian و Bizagi و Bonita
12. Message Queue (such as Kafka and RabbitMQ):
مکانیزم Message queue یک روش برقراری ارتباط که اجازه میده تبادل پیام به صورت asyncronous بین قسمتهای مختلف یک سیستم (کامپوننت ها یا میکروسرویس ها) انجام بشه که باعث میشه از لحاظ زمانی decouple بشن.
یعنی فرستنده و دریافت کننده لزومی نداره که در یک زمان با هم تعامل داشته باشند و دریافت کننده حتی لازم نیست که اطلاع داشته باشه چه کسی پیام رو ارسال کرده. به جای این کار، اجزای سیستم پیام ها رو به یک صف می فرستن و مصرف کننده ها پیام ها رو دریافت می کنن.
غیر از ایزوله سازی و ایجاد استقلال بین تولید کننده پیام و دریافت کننده یکی دیگر از فواید این مکانیزم، scalability و پردازش آسانکرونه. مواردی که مسیج کیو در اونها بیشتر کاربرد داره شامل فرایندهای ارسال و دریافت ایمیل یا SMS در تعداد بالا یا پردازش ویدیو بعد از آپلوده.
نحوه کار هم به این صورت که ایجاد کننده پیام یک پیام رو به صف ارسال میکنه و سپس message broker اون رو ذخیره میکنه و دریافت کننده پیام بعداً پیام رو از داخل صف برمیداره.
چند نوع الگوی پیام رسانی مختلف هم وجود داره: Point to point, work queues, publish subscribe
همچنین معمولا در مواقعی که صرفا می خوایم از صف استفاده کنیم RabbitMQ جوابگو هست ولی اگر به history نیاز داشته باشیم یا بخوایم داده ها به چند مصرف کننده ارسال بشن، Kafka گزینه مطلوب تریه و اغلب برای رویدادهای سنگین تر به کار میره.
13. Container orchestration (such as Kubernetes):
مفهوم Container orchestration به مدیریت خودکار کانتینرها گفته میشه مثل اجرا کردن و اسکیل کردن اونها به خصوص زمانی که تعداد زیادی کانتینر در سرورها وجود داره.
به طور مثال اگر فرض کنیم که ۱۰ تا میکرو سرویس داشته باشیم که هر کدومشون در چند کانتینر در حال اجرا شدن هستند فرایندهای اجرا و پایان هر کدوم و همچنین آپدیت کردن اونها بدون down time اگر به صورت دستی بخواد صورت بگیره خیلی چالش برانگیزه.
فانکشنهای اصلی که kubernetes انجام میده شامل:
برنامهریزی زمان اینکه کدوم کانتینر روی کدوم سرور اجرا بشه، کانتینرهایی که کرش کردن restart بشن یا جایگزین بشن، تعداد کانتینرها بر اساس درخواست کم یا زیاد بشه، نرمافزارها با کمترین زمان down time آپدیت بشن، همچنین کانتینرها قابلیت برقراری ارتباط با همدیگه رو داشته باشن.
چند مفهوم در کوبرنتیز:
به کوچکترین واحد در کوبرنتیز پاد گفته میشه و معمولاً به هر پاد یک کانتینر اختصاص داده میشه.
نود (node) به یک سرور گفته میشه که در اون پادها ران میشن. به گروهی از نودها هم کلاستر گفته میشه.
14. Multi-Tenancy Architecture:
پترن Multi-tenancy یک الگوی معماری نرمافزاره که در اون یک نمونه از اپلیکیشن و زیرساختش مصرف کنندههای زیادی رو تامین میکنه هر کدوم از اون مصرف کنندهها فکر میکنه که داره از اپ خصوصی خودش استفاده میکنه و میتونه دیتای مخصوص خودش رو داشته باشه ولی در واقعیت همه این مصرف کنندهها دارن از یک بکند یکسان استفاده میکنن.
این نوع معماری چند تایپ مختلف داره:
1. اپ و دیتابیس مشترک به نحوی که همه مصرف کنندهها از یک اپ و دیتابیس استفاده میکنن
2. نوعی که در اون اپ مشترک ولی دیتابیس جدا هست
در نوع single tenancy هم اپ و هم دیتابیس جدا هستن.
در چنین اپلیکیشنهایی هر دو مصرف کننده به صورت مستقل از برنامه استفاده میکنند ولی کد و بکند مشترکه. دیتای کاربران مانند permissionها و تسکها امن و ایزوله ست.
از مزایای این نوع معماری کاهش هزینههاست به این دلیل که منابع زیرساختی کمتری استفاده میشه و به خاطر اینکه فقط یک کد بیس وجود داره نگهداری ازش راحتتره. فرآیند on boarding هم سریعتره به خاطر اینکه nodeهای جدید میتونن بدون نیاز به ایجاد تغییر در زیرساخت اضافه بشن.
اکثر اپهایی که امروز باهاشون سروکار داریم از این نوع معماری پیروی میکنند مثل جیمیل و zoom
15. Enterprise Integration Patterns:
الگوهای Enterprise integration یک دسته از دیزاین پترنها هستند که روشهای مرسومی رو توصیف میکنن که چند سیستم نرمافزاری رو از طریق پیامها با هم integrate میکنه و به زبان ساده تر، مجموع الگوهایی که برای حل مشکلات ارتباط بین سیستم های مختلف در سازمان ها به کار میره.
دلیل اهمیت این الگوها این هست که سیستمها باید بتونن با هم دیتا تبادل کنن و با همدیگه کار کنن، در حالی که distributed هستند و توسط تیمهای مختلفی توسعه داده شدن. EIP باعث ایجاد یه زبان مشترک بین دولوپر و معمار و تحلیلگر میشه.
ما نمیتونیم یه فانکشن رو به صورت عادی در سیستم صدا بزنیم؛ به طور مثال integrate کردن سیستمهای ecommerce و ERP و CRM نیاز به هماهنگی بیشتری داره.
همچنین چند مفهوم کلی در EIP داریم: زیرساخت انتقال پیام که شامل این موارده: پیامی که رد و بدل میشه و مثلاً میتونه در فرمت json باشه، یکی کانالی هستش که در اون پیام تبادل میشه که میتونه Kafka یا RabbitMQ باشه، و یک مسیج روتر. ساختار پیام ها که میتونه به صورت Command، یا ثبت یک رخداد باشه. البته گاهی اوقات نیازه که فرمت پیام دچار تغییر بشه (بخاطر ایجاد سازگاری بین دو سیستم)
مراجع:
https://www.spiceworks.com/tech/devops/articles/what-is-serverless/
https://redis.io/glossary/domain-driven-design-ddd/
https://www.crowdstrike.com/en-us/cybersecurity-101/cloud-security/serverless-architecture/
https://www.spiceworks.com/tech/tech-general/articles/event-driven-architecture/
https://www.postman.com/api-first/
https://learn.microsoft.com/en-us/azure/architecture/guide/architecture-styles/event-driven
https://www.kurrent.io/blog/snapshots-in-event-sourcing
https://www.solo.io/topics/service-mesh/service-mesh-vs-api-gateway
https://www.contentful.com/blog/what-is-api-first/
https://www.datadoghq.com/knowledge-center/serverless-architecture/
www.youtube.com/watch?v=efRu7odnEQo
https://www.youtube.com/watch?v=zpAA1t5AeLw
https://www.youtube.com/watch?v=hUVBT7wA6Kw
https://www.youtube.com/watch?v=i2eVTk2Fb40