سبکهای معماری زیادی وجود دارن.
هر سبک بهدلیلی بهوجود اومده و فلسفهی خاص خودش رو دربارهی نحوه و زمان استفاده داره.
درک فلسفهی هر سبک بهت کمک میکنه تشخیص بدی آیا اون سبک برای دامنهی کاری تو مناسب هست یا نه. این همون چیزیه که در این فصل بهش میپردازیم—در واقع، ادامهی این کتاب بهطور کامل به سبکهای معماری اختصاص داره!
این فصل بـُعد چهارم معماری نرم افزار هست. اگه یادت رفته تو فصل اول توضیح دادیم که معماری نرم افزار از ۴ بـُعد تشکیل شده.
قبل از شروع: یه نگاهی به محلهات بنداز، بعد یه سریال یا فیلمی ببین که در نقطهای دیگه از دنیا ساخته شده.
چند نوع سبک معماری خونه میبینی؟ واقعاً صدها سبک مختلف وجود داره—همه تحت تأثیر موقعیت جغرافیایی، شرایط آبوهوایی، و سلیقهی شخصی صاحبان خونهها.
هر روز سبکهای جدیدی خلق میشن.

در مورد سبکهای معماری نرمافزار هم همینطور هست.
سبکهای زیادی وجود دارن که حتی در کتابی به این بزرگی، با تمام تلاشمون، فقط سطحی از اونها رو میتونیم پوشش بدیم.
این فصل چارچوبی برای تفکر دربارهی معماری و سبکهای معماری بهطور کلی بهت میده.
سپس در فصلهای بعدی، به چند سبک معماری خاص عمیقتر میپردازیم و فلسفهی اونها رو بررسی میکنیم—با استفاده از چیزهایی که در این فصل یاد میگیری.
درک چند سبک کلیدی، پایهی خوبی برات فراهم میکنه تا سبکهای دیگهای که در مسیرت باهاشون روبهرو میشی رو هم بهتر بفهمی.
دنیای سبکهای معماری
اگه مدتی در توسعهی نرمافزار فعالیت کرده باشی، احتمالاً با سبکهای مختلف معماری مثل Monolith و Microservices آشنا شدی.
برای اینکه بتونیم بهصورت سیستماتیک دربارهی این سبکها فکر کنیم، اونها رو در دو دسته قرار میدیم:
- دستهی اول مربوط به نحوهی تقسیمبندی کد هست:
یا بر اساس دغدغههای فنی، یا بر اساس دغدغههای دامنهی کسبوکار (Domain). - دستهی دوم مربوط به نحوهی استقرار سیستم هست:
آیا کل کد سیستم بهصورت یک واحد تحویل داده میشه، یا بهصورت چندین واحد مستقل؟

همونطور که میبینی، راههای مختلفی برای دستهبندی و تحلیل سبکهای معماری وجود داره.
البته این دستهبندیها همهچیز رو پوشش نمیدن—سبکهای معماری خاص دامنهای هم وجود دارن که بهطور ویژه برای حل مسائل خاص طراحی شدن—اما حجم یک کتاب محدود هست.
هر دسته، برخی از ویژگیهای معماری سبکهای خودش رو آشکار میکنه.
برای مثال، سبکهایی که بهصورت یک واحد تحویل داده میشن، فهمشون راحتتره؛ اما اونهایی که بهصورت چند واحد مستقل ارائه میشن، معمولاً بهتر مقیاسپذیر هستن.
بیایم هر دسته رو بررسی کنیم.
تقسیمبندی: فنی در برابر دامنهای
برگرد به آخرین باری که در یک رستوران شیک شام خوردی. وقتی وارد شدی، احتمالاً یک میزبان به استقبالت اومد و تو رو تا میز همراهی کرد.
سِروِر نوشیدنی و منو بهت پیشنهاد داد و غذاهای ویژه رو توضیح داد.
سرآشپز و سایر آشپزها غذات رو آماده کردن.
وقتی غذات تموم شد، مسئول تمیزکاری میز رو جمع کرد و دوباره چید.
وظایف کارکنان رستوران بر اساس دغدغههای فنی از هم جدا شدهن.
مسئول تمیزکاری وظیفهش خوشآمدگویی نیست، و احتمالاً نمیخوای غذات رو بپزه.
حالا برگرد به آخرین اپلیکیشنی که روش کار کردی. آیا لایهی کنترلر داشت؟ سرویس داشت؟ لایهی Persistence یا دیتا داشت؟
اگه آره، تبریک: تو قبلاً روی معماریای با تقسیمبندی فنی کار کردی.
در معماری با تقسیمبندی فنی، کد بر اساس دغدغههای فنی تقسیم میشه—ممکنه لایهی نمایش، لایهی سرویسهای تجاری یا بیزینس، و غیره وجود داشته باشه.
اصل حاکم در اینجا، جداسازی بر اساس دغدغههاست—که اکثر افراد اون رو بهصورت لایههای افقی در نظر میگیرن.

از طرف دیگه، یه فودکورت رو تصور کن.
تعداد زیادی رستوران داره که هرکدوم در نوع خاصی از غذا تخصص دارن: پیتزا، سالاد، غذای آسیایی، همبرگر.
بهعبارت دیگه، هر رستوران یک دامنهی خاص داره.
نکته
ممکنه هرکدوم از این رستورانها سِروِر و مسئول تمیزکاری داشته باشن.
اما در سطح بالا، هر رستوران در نوع خاصی از غذا تخصص داره.
در معماریهای با تقسیمبندی دامنهای، ساختار سیستم با دامنهی کاری همراستا میشه.
بهجای تقسیمبندی بر اساس نقشها، کد (و سیستمها) بهگونهای جدا میشن که با مسئلهای که قصد حلش رو داری همراستا باشن.

در معماری با تقسیمبندی دامنهای، لایههای نمایش (Presentation) و سرویسها (Services) کجا قرار میگیرن؟
در معماری با تقسیمبندی دامنهای، دامنه بهعنوان «شهروند درجهیک» در نظر گرفته میشه، و پیادهسازی فنی فقط در حد پیادهسازی باقی میمونه.
کامپوننتهای منطقی معماری حول محور دامنه سازماندهی میشن، نه بر اساس نقشی که ایفا میکنن.
در معماری با تقسیمبندی فنی، کامپوننتها ممکنه در Namespaceهایی مثل app.presentation.customer یا app.services.customer قرار بگیرن—یعنی دامنهی customer درون تقسیمبندی فنی قرار داره.
اما در معماری دامنهمحور، Namespaceها به شکل app.customer.presentation و app.customer.services هستن—یعنی ساختار حول دامنه چیده شده.
تقسیمبندی دامنهای منطقی بهنظر میرسه. راستش، بهتر هم بهنظر میاد. پس چرا کسی باید از تقسیمبندی فنی استفاده کنه؟
ترجیح میدیم از ارزشگذاریهایی مثل «بهتر» یا «بهترین» در بحث سبکهای معماری استفاده نکنیم. (قراره از این جمله زیاد بشنوی!)
انتخاب سبک معماری همیشه تحت تأثیر عوامل مختلفی هست—از جمله دامنهی کاری و ویژگیهای معماری موردنیاز.
تقسیمبندی فنی برای تیمهایی که تخصصمحور هستن عالیه—مثلاً اگه تیمهایی از متخصصان Frontend، توسعهدهندگان Backend، و مدیران پایگاه داده داشته باشی.
اما تقسیمبندی دامنهای سیستم رو بهتر با مسئلهی واقعی همراستا میکنه.
مدل استقرار: یکپارچه (Monolithic) در برابر توزیعشده (Distributed)
بیایم یه بازی کنیم—ما یه کلمه میگیم، و تو اولین چیزی که به ذهنت میرسه رو بگو. آمادهای؟ «Monolith».
نمیدونیم برای تو چی تداعی میکنه، ولی برای ما یاد چیزی مثل یه صخرهی عظیم یا یه یخچال طبیعی میافته—چیزی بزرگ.
دقیقاً همین مفهومیه که معماریهای یکپارچه (Monolithic) منتقل میکنن.
در معماری یکپارچه، تمام کامپوننتهای منطقی که اپلیکیشن رو تشکیل میدن بهصورت یک واحد استقرار پیدا میکنن.
یعنی کل اپلیکیشن بهعنوان یک فرایند واحد اجرا میشه.

در مقابل، در معماری توزیعشده (Distributed)، کامپوننتهای منطقی تشکیلدهندهی اپلیکیشن رو به چندین واحد (معمولاً کوچکتر) تقسیم میکنی.
هرکدوم از این واحدها در فرایند (Process) جداگانهای اجرا میشن و از طریق شبکه با هم ارتباط برقرار میکنن.
این تفاوت نکات زیادی داره، پس بیایم دربارهی مزایا و معایب هر دو نوع معماری صحبت کنیم.
مزایای مدل استقرار یکپارچه (Monolithic):
در فصل ۲ یاد گرفتی که ویژگیهای معماری همیشه روی جنبهای ساختاری از طراحی تأثیر میذارن.
معماریهای یکپارچه برخی از این ویژگیها رو بهتر از معماریهای توزیعشده پشتیبانی میکنن، و دونستن نقاط قوتشون بهت کمک میکنه تصمیم بگیری چه زمانی از اونها استفاده کنی.
چون سیستمهای یکپارچه در یک فرایند اجرا میشن، توسعهی اونها—حداقل در مراحل اولیه—آسونتره.
و چون بهصورت یک واحد استقرار پیدا میکنن، ردیابی خطاها هم خیلی راحتتره.
بیایم نگاهی بندازیم به مزایا و معایب هر دو مدل استقرار، و از معماریهای یکپارچه شروع کنیم. اینها مزایاش هستن:
- سادگی (Simplicity)
- معمولاً اپلیکیشنهای Monolithic یک کدبیس واحد دارن، که توسعه و درک اونها رو سادهتر میکنه.
- هزینه (Cost)
- ساخت و اجرای Monolithها ارزانتره، چون سادهتر هستن و زیرساخت کمتری نیاز دارن.
- قابلیت اطمینان (Reliability)
- یک Monolith مثل یک جزیرهست—تقریباً هیچ تماس شبکهای نداره، که معمولاً به معنای اپلیکیشنهای قابلاعتمادتره.
- امکانپذیری (Feasibility)
- اگه عجله برای ورود به بازار داری، Monolithها ساده و نسبتاً ارزون هستن، و بهت اجازه میدن سریعتر آزمایش و تحویل بدی.
- قابلیت اشکالزدایی (Debuggability)
- اگه باگ یا خطایی دیدی، اشکالزدایی آسونه چون کل کد در یک مکان قرار داره.
- اینها فقط بخشی از مزایای Monolithها هستن.
معایب مدل استقرار یکپارچه (Monolithic):
برخی از نقاط قوت معماریهای Monolithic ممکنه با رشد اپلیکیشن به چالش تبدیل بشن.
بسیاری از ویژگیهای عملیاتی که در فصل ۲ دربارهشون صحبت کردیم—مثل مقیاسپذیری (Scalability) و قابلیت اطمینان (Reliability)—با بزرگتر و پیچیدهتر شدن اپلیکیشنهای Monolithic دچار افت میشن.
- مقیاسپذیری (Scalability)
- اگه بخوای فقط یک بخش از اپلیکیشن رو مستقل از بقیه مقیاسگذاری کنی، به مشکل میخوری.
- در Monolithها همهچیز یا با هم مقیاسپذیر میشن یا هیچچیز.
- قابلیت اطمینان (Reliability)
- چون کل اپلیکیشن بطور یک واحد مستقر میشه، هر باگی که در سیستم بشه، کل سیستم رو تحت تأثیر قرار میده نه فقط یه بخش کوچیک رو.
- نکته: «قابلیت اطمینان جزو موارد مزایای مدل استقرار یکپارچه هم بود!»
- تکاملپذیری (Evolvability)
- با رشد اپلیکیشنهای Monolithic، اعمال تغییرات سختتر میشه.
- چون کل اپلیکیشن یک کدبیس واحده، نمیتونی برای بخشهای مختلف از تکنولوژیهای متفاوت استفاده کنی.
- قابلیت استقرار (Deployability)
- اعمال هر تغییری نیازمند استقرار مجدد کل اپلیکیشن هست، که میتونه ریسک زیادی ایجاد کنه.
اینها فقط چند مورد از معایب هستن که خواستیم بهشون اشاره کنیم—نه کل لیست.
مزایای مدلهای استقرار توزیعشده (Distributed):
در معماریهای توزیعشده، کامپوننتهای منطقی بهصورت واحدهای جداگانه مستقر میشن.
این موضوع باعث میشه بتونی بعضی بخشهای اپلیکیشن رو مستقل از بقیه مقیاسگذاری کنی.
و چون کامپوننتهای منطقی بهصورت فیزیکی جدا هستن، معماریهای توزیعشده باعث کاهش وابستگی (Coupling) میشن.
پس معماریهای توزیعشده برای چه ویژگیهای معماری مناسبن؟
در ادامه چند مورد رو بررسی میکنیم:
- مقیاسپذیری (Scalability)
- کامپوننتهای منطقی بهصورت جداگانه مستقر میشن، بنابراین میتونی هر بخش رو مستقل از بقیه مقیاسگذاری کنی.
- ماژولار بودن (Modularity)
- چون کامپوننتها باید وابستگی کمی (coupling) داشته باشن، معماریهای توزیعشده توصیه به ماژولار بودن بالایی دارن.
- قابلیت تست (Testability)
- هر واحد استقرار فقط گروه خاصی از کامپوننتهای منطقی رو شامل میشه، که تست کردن رو—حتی با رشد اپلیکیشن—آسونتر میکنه.
- یادداشت جانبی: «معماریهای توزیعشده خیلی قابل تستتر از Monolithها هستن.»
- قابلیت استقرار (Deployability)
- معماریهای توزیعشده از واحدهای کوچک زیاد پشتیبانی میکنن، که با اصول مهندسی مدرن مثل
CI/CDو تست خودکار همراستا هستن. - یادداشت جانبی: «داشتن واحدهای کوچک با تستپذیری خوب، ریسک استقرار تغییرات رو کاهش میده.»
- معماریهای توزیعشده از واحدهای کوچک زیاد پشتیبانی میکنن، که با اصول مهندسی مدرن مثل
- تحمل خطا (Fault Tolerance)
- حتی اگه یکی از بخشهای سیستم از کار بیفته، بقیهی سیستم میتونن به کار خودشون ادامه بدن.
همونطور که احتمالاً متوجه شدی، معماریهای توزیعشده در بسیاری از نقاط ضعف معماریهای یکپارچه عملکرد بهتری دارن.
اما آیا عکس این قضیه هم صادقه؟
بیایم بررسی کنیم.
معایب مدلهای استقرار توزیعشده (Distributed):
نمیشه فقط مزایا داشت بدون معایب. همهچیز به مصالحهها (Trade-offs) بستگی داره، درسته؟ پای انتخاب و سبک معماری که وسط باشه، همیشه باید بین مزایا و معایب سبکها توازن برقرار کرد.
- کارایی (Performance)
- معماریهای توزیعشده شامل سرویسهای کوچکی هستن که از طریق شبکه با هم ارتباط برقرار میکنن.
- این ارتباطات شبکهای میتونن روی کارایی تأثیر بذارن—البته راههایی برای کاهش این اثر وجود داره، ولی باید حتماً در نظر گرفته بشه.
- هزینه (Cost)
- استقرار چندین واحد به معنای نیاز به سرورهای بیشتره.
- علاوه بر اون، این سرویسها باید با هم ارتباط برقرار کنن، که نیازمند زیرساخت شبکه و نگهداری اون هست.
- پیچیدگی (Simplicity)
- سیستمهای توزیعشده ساده نیستن.
- از درک نحوهی عملکرد گرفته تا اشکالزدایی، همهچیز چالشبرانگیزه.
- نمیتونیم به اندازهی کافی تأکید کنیم که معماریهای توزیعشده چقدر پیچیدهان!
- قابلیت اشکالزدایی (Debuggability)
- خطا ممکنه در هر سرویسی که در پاسخگویی به درخواست نقش داره رخ بده.
- چون کامپوننتهای منطقی بهصورت جداگانه مستقر شدن، ردیابی خطاها میتونه بسیار دشوار باشه.
- اشکالزدایی در سیستمهای توزیعشده نیازمند تفکر عمیق دربارهی لاگبرداری و معمولاً جمعآوری لاگهاست—که خودش هزینهزا هست.
معماریهای توزیعشده انجام بعضی کارها رو آسون میکنن، اما در عوض بعضی چیزها رو واقعاً سخت میکنن.
مراقب باش!
خیلی راحت میشه سختیهای محاسبات توزیعشده رو دستکم گرفت!
با وجود تمام مزایاشون، معماریهای توزیعشده به شبکه وابستهان.
و معماران نرمافزار اغلب پیچیدگیهایی رو که از این وابستگی ناشی میشن، دستکم میگیرن.
برای اینکه بهتر بدونی باید مراقب چه چیزهایی باشی، عبارت “The Fallacies of Distributed Computing” رو جستجو کن—فهرستی که در دههی ۱۹۹۰ توسط L. Peter Deutsch و دیگران در شرکت Sun Microsystems گردآوری شده.
گفتوگوهای کنار آتش
موضوع گفتوگوی امشب:
معماریهای یکپارچه (Monolithic) و توزیعشده (Distributed) به این پرسش پاسخ میدن:
«کدوم یکی امروزه بیشتر کاربرد داره؟»
معماری یکپارچه:
خوبه که هنوز منو استفاده میکنن. واقعاً تو همهچیزو پیچیده میکنی.
معماری توزیع شده:
از این طرز فکر خوشم نمیاد. شاید توسعهت سادهتر باشه، ولی نمیتونی با سرعت کسبوکارها هماهنگ بشی. کسبوکارها باید سریع حرکت کنن، و تو نمیتونی اون چیزی که لازمه رو تحویل بدی.
معماری یکپارچه:
ممکنه ساده باشم، ولی توسعهم سریعتره. نمیتونم تصور کنم کسی بخواد یه محصول اولیه (MVP) رو با تو بسازه—هیچوقت لانچ نمیشه!
معماری توزیع شده:
ممکنه اینو قبول کنم—ولی من مطمئن میشم که اون محصول به خط پایان میرسه. اگه اون محصول موفق بشه، تو کمک میکنی یا فقط مانع میشی؟ من موفقیت در مقیاس بالا رو تضمین میکنم.
معماری یکپارچه:
آها! و من خیلی ارزونترم. میدونی که بیشتر کسبوکارها نمیخوان پولشون رو هدر بدن، درسته؟ نمیتونم تصور کنم کسی بخواد با تو یه نمونهی اولیه بسازه.
معماری توزیع شده:
کسبوکارها همچنین دنبال پول درآوردن هستن. وقتی اپلیکیشنها رشد میکنن، تو فقط یه چاه هزینهای. من نماد چابکیام—به تیمها و سازمانها کمک میکنم با رشدشون مقیاسپذیر بشن.
من تست کردن رو آسون میکنم، در حالی که تو فقط بدهی فنی جمع میکنی.
معماری یکپارچه:
خوبه که تستپذیر هستی—تا حالا یه stack trace مفید دیدی؟ معلومه که نه. تو همهجا پخش شدی. موفق باشی توی ردیابی اینکه خطا چرا و کجا اتفاق افتاده.
حداقل وقتی من خطا میدم، یه stack trace واضح تحویل میدی.
معماری توزیع شده:
درسته… ولی وقتی تو خراب میشی، کل سیستم فرو میریزه. من درجهی بالایی از تحمل خطا دارم. نیاز به مقیاسگذاری یه سرویس داری؟ فقط همون سرویس رو مقیاس بده. مقیاسگذاری تو خیلی سخت و طاقتفرساست.
معماری یکپارچه:
حداقل من فقط یه فرایندم. هیچ ترافیک شبکهی اضافی اینجا نیست. تو فقط حرف میزنی—همهی سرویسهات مدام دارن با هم حرف میزنن.
و این فقط در صورتیه که شبکه همیشه قابلاعتماد باشه، چون بدون اون، هیچی نداری! خدا به دادت برسه اگه شبکه قطع بشه.
بعلاوه، با من نیازی به کلی زیرساخت شبکه نداری. میدونی نگهداری اون زیرساختها چقدر گرونه؟!
معماری توزیع شده:
خب، این هزینهی رشد در مقیاسه. تیمها شاید با تو شروع کنن، ولی اگه بخوان رشد کنن، میان سراغ من—و تو رو پشت سر میذارن.
معماری یکپارچه:
چی؟ داری میگی من دیگه به درد نمیخورم؟ خب، دفعهی بعدی که یه تیم بخواد سریع وارد بازار بشه، منو صدا نزن—بعد ببینیم چقدر واقعاً قوی هستی.
معماری توزیع شده:
حسش متقابله، رفیق. وقتی محصول اولیهی تیم موفق شد و معماریشون نتونست توجهها رو تحمل کنه، منو صدا نزن!
تو این فصل یاد گرفتی چطور سبکهای مختلف معماری رو دستهبندی کنی. داشتن یه چارچوب بهت کمک میکنه راحتتر اونها رو درک کنی.
و یادت باشه—هر بخش از اون چارچوب، هم مزایا و هم معایب سبکهای معماری رو نشون میده.
در فصل بعدی، وارد بررسی عمیق سبکهای معماری بهصورت جداگانه میشیم.
نکات کلیدی
- سبکهای معماری زیادی وجود دارن—در واقع، اونقدر زیاد که قابل شمارش نیستن.
- راههای مختلفی برای دستهبندی سبکهای معماری وجود داره. یکی از اونها، بر اساس سبک تقسیمبندی (Partitioning Style) هست.
- سبکهای معماری میتونن بهصورت تقسیمبندی فنی (Technically Partitioned) یا تقسیمبندی دامنهای (Domain Partitioned) باشن.
- در سبکهای تقسیمبندی فنی، کد بر اساس دغدغههای فنی جدا میشه—مثلاً لایهی ارائه (
Presentation Layer) و لایهی سرویسها (Services Layer). - در سبکهای تقسیمبندی دامنهای، کد بر اساس دامنهی مسئله (Domain) تقسیم میشه.
- راه دیگه برای دستهبندی سبکهای معماری، مدل استقرار (Deployment Model) هست.
- معماریهای یکپارچه (
Monolithic) تمام کامپوننتهای منطقی اپلیکیشن رو بهصورت یک واحد واحد مستقر میکنن. - معماریهای توزیعشده (
Distributed) کامپوننتهای منطقی رو بهصورت جداگانه و در قالب چندین واحد مستقر میکنن. - معماریهای Monolithic فهم و اشکالزداییشون آسونه، و معمولاً ساختشون ارزونتره (حداقل در مراحل اولیه) و گزینهی خوبی هستن وقتی عجله برای ورود به بازار وجود داره.
- با رشد اپلیکیشنی که معماریهای Monolithic پیاده سازی شده، مقیاسگذاریشون سخت میشه. یا باید کل اپلیکیشن رو مقیاس بدی، یا هیچچیز رو.
- معماریهای Monolithic ممکنه قابلاعتماد نباشن—یه باگ میتونه کل اپلیکیشن رو از کار بندازه.
- معماریهای Distributed بسیار مقیاسپذیر هستن، چون کامپوننتها جداگانه مستقر میشن و میتونن مستقل از هم مقیاسگذاری بشن.
- معماریهای Distributed ماژولار بودن بالایی دارن، که تست کردن رو آسونتر میکنه.
- معماریهای Distributed توسعه، نگهداری، و اشکالزداییشون بسیار پرهزینهست.
- در معماریهای Distributed برای انجام کار، سرویسها باید از طریق شبکه با هم ارتباط برقرار کنن—که پیچیدگی بیشتری ایجاد میکنه.
