وقتی خبری از «بهترین روش‌ها» (Best Practices) نباشه، چی میشه؟ خب، خوبیِ روش‌های بهینه اینه که معمولاً بی‌دردسر و کم‌ریسک هستن و یه راه مطمئن برای رسیدن به یه هدف خاص به حساب میان. بهشون نمی‌گن «خوب» یا «بهتر»، بلکه میگن «بهترین» چون امتحان‌شون رو پس دادن—پس چرا ازشون استفاده نکنیم؟

ولی یه چیزی که خیلی زود تو معماری نرم‌ افزار یاد می‌گیری اینه که اینجا خبری از بهترین روش نیست. هر موقعیت یه داستان جداست. باید خوب بررسیش کنی، تصمیم بگیری، و بعد فقط نگی چی انتخاب کردی، بلکه باید توضیح بدی چرا اون انتخاب رو کردی.

خب، حالا چطور باید تو این دنیای جدید راه‌مون رو پیدا کنیم؟ خوشبختانه، قانون‌های معماری نرم‌ افزار هستن که می‌تونن راهنمای‌مون باشن. توی این فصل یاد می‌گیری چطور موقع تصمیم‌گیری، بده‌بستان‌ها رو تحلیل کنی. همچنین یاد می‌گیری چطور «سند تصمیمات معماری» (Architectural Decision Record) بسازی تا دلایل و روش‌های پشت هر تصمیم رو ثبت کنی. تا آخر این فصل، ابزارهایی دستت میاد که بتونی تو مسیر پرچالش معماری نرم‌ افزار، با اطمینان حرکت کنی.

شروع داستان با یه اپلیکیشن کفش‌بازیه

یه شرکت که اپ موبایلش بین کلکسیونرهای کفش (یا همون «sneakerhead»‌ها) حسابی محبوبه. کاربرا می‌تونن کفش‌های خاص رو بخرن، بفروشن یا با هم معامله کنن. با میلیون‌ها کفش ثبت‌شده، هر کسی می‌تونه اون مدلی رو که دنبالش بوده پیدا کنه یا عکس کفش‌هایی که نمی‌خواد رو آپلود کنه تا راحت‌تر فروش بره.

معماری اولیه‌ی این اپ، یه سرویس تکی بوده که پایین‌تر نشون داده شده.

اپلیکیشن Two Many Sneakers می‌دونه چطور با سرویس معامله ارتباط برقرار کنه تا اطلاعات رو دریافت یا به‌روزرسانی کنه—مثلاً عکس یه جفت نایک نو و دست‌نخورده. سرویس معامله هم به نوبه‌ی خودش اطلاعات رو از دیتابیس می‌گیره و اون رو آپدیت می‌کنه.

اوضاع کسب‌وکار حسابی خوبه. کلکسیونرهای کفش همیشه دنبال تنوع هستن، و تعداد مشتری‌های اپ خیلی سریع زیاد شده. حالا مشتری‌ها انتظار دارن نوتیفیکیشن‌های لحظه‌ای دریافت کنن—تا هر وقت یه جفت Air Jordan تمیز و خاص برای فروش گذاشته شد، سریع باخبر بشن.

امنیت همیشه یه دغدغه‌ی مهمه تو فروش آنلاین. هیچ‌کس دلش نمی‌خواد کفش تقلبی بخره، و اطلاعات کارت اعتباری هم باید امن بمونه. برای اینکه از کلاهبردارها جلوتر باشن، تیم مدیریتی Two Many Sneakers تصمیم گرفته تمرکز رو بذاره روی ارتقای قابلیت‌های تشخیص تقلب (fraud detection). قراره از تحلیل داده استفاده کنن تا رفتارهای مشکوک کاربرا رو شناسایی کنن و ربات‌ها رو فیلتر کنن.

کار از قبل شروع شده—حالا تنها کاری که تیم باید انجام بده اینه که سرویس معامله رو طوری تنظیم کنن که هر وقت یه اتفاق مهمی توی اپ افتاد، به سرویس‌های نوتیفیکیشن و تحلیل داده خبر بده.

تا اینجای کار چی می‌دونیم؟ باید بفهمیم این سرویس‌ها چطور قراره با هم ارتباط برقرار کنن. بیایم یه مرور کنیم از چیزهایی که تا حالا مشخص شده—و چیزهایی که هنوز نمی‌دونیم:

الان معماری سیستم خیلی ساده‌ست—سرویس معامله فقط با دیتابیس خودش حرف می‌زنه، همین. حالا باید کاری کنیم که این سرویس بتونه اطلاعات رو به سرویس نوتیفیکیشن و سرویس تحلیل داده هم بفرسته.

شنیده شده که احتمال داره بخش مالی شرکت (که مسئول رعایت قوانین و مقرراته) بخواد از سرویس معامله گزارش‌هایی دریافت کنه. یعنی معماری‌ای که طراحی می‌کنیم باید قابلیت گسترش داشته باشه (Extensible)—طوری که بشه سرویس‌های جدید رو راحت بهش اضافه کرد.

ما هنوز نمی‌دونیم دقیقاً چه جور اطلاعاتی باید برای سرویس‌های نوتیفیکیشن و تحلیل ارسال بشه—آیا هر دو سرویس یه جور داده دریافت می‌کنن یا کاملاً متفاوتن؟ و وضعیت بخش مالی هم هنوز مشخص نیست، پس اونم یه عامل نامعلوم دیگه‌ست.

برای اینکه قضیه روشن باشه: یه‌سری چیزها رو می‌دونیم، ولی کلی چیز هم هست که هنوز نمی‌دونیم. به دنیای معماری نرم‌ افزار خوش اومدی!

نکته: به‌عنوان معمارهای سیستم، باید ویژگی‌های معماری اون رو مشخص کنیم.

حتی همین تمرین ساده هم کلی پیچیدگی داره. یه چیزهایی رو می‌دونیم، یه چیزهایی رو فکر می‌کنیم می‌دونیم، و کلی چیز هم هست که اصلاً نمی‌دونیم. پس چطور باید درباره‌ی معماری فکر کنیم؟

کاملاً درست می‌گی. تو بیشتر پروژه‌های واقعی، لیست ویژگی‌های معماری معمولاً ترکیبیه از «چیزی که قطعاً می‌خوایم» و «چیزی که شاید بعداً بخوایم». حتی خود مشتری‌ها هم نمی‌تونن دقیق بگن در نهایت چی می‌خوان—اگه می‌تونستن که عالی می‌شد!

اینجا با یه دسته از ناشناخته‌ها طرفیم که بهش می‌گن «چیزهایی که نمی‌دونی که نمی‌دونی» یا همون unknown unknowns. خیلی وقت‌ها همین ناشناخته‌ها وسط پروژه ظاهر می‌شن و حتی بهترین برنامه‌ریزی‌ها رو به‌هم می‌زنن.

راه‌حل چیه؟ باید با ذهنیت چابک (Agile) و تکرارشونده (Iterative) جلو رفت. باید بپذیری که هیچ‌چیز، مخصوصاً معماری نرم‌ افزار، ثابت نمی‌مونه. چیزی که امروز جواب می‌ده، ممکنه فردا بشه بزرگ‌ترین مانع موفقیت. ذات معماری نرم‌ افزار همینه—مدام در حال تغییر و رشد، هم‌زمان با شناخت بیشتر از مسئله و نیازهای جدید مشتری‌ها.

ارتباط با سرویس‌های پایین‌دستی

هدف‌مون اینه که سیستم معامله به‌صورت خودکار به سرویس‌های گزارش‌گیری و تحلیل داده خبر بده. فعلاً فرض کنیم می‌خوایم از سیستم پیام‌رسانی استفاده کنیم. اما اینجا یه سؤال مهم پیش میاد—از بین صف (queue) یا موضوع (topic)، کدوم رو انتخاب کنیم؟

🔹 نکته: اگه با پیام‌رسانی، صف‌ها یا موضوع‌ها آشنایی زیادی نداری، اشکالی نداره—قراره همینجا همه‌ی چیزهایی که لازم داری رو یاد بگیری.

قبل از اینکه جلوتر بریم، بیایم تفاوت بین صف و موضوع رو روشن کنیم. بیشتر پلتفرم‌های پیام‌رسانی دو مدل ارتباطی برای فرستنده‌ی پیام (که اینجا همون سرویس معامله‌ست) ارائه می‌دن تا با یک یا چند دریافت‌کننده (سرویس‌های پایین‌دستی) ارتباط برقرار کنه:

مدل اول: صف (Queue)

ارتباط نقطه‌به‌نقطه‌ست. یعنی فرستنده دقیقاً می‌دونه پیامش قراره به کدوم دریافت‌کننده برسه. اگه بخواد به چند سرویس پیام بده، باید برای هر کدوم یه صف جداگانه بسازه و پیام رو جداگانه ارسال کنه.

مثلاً اگه سرویس معامله بخواد اطلاعات معاملات رو هم به سرویس تحلیل و هم به سرویس گزارش‌گیری بفرسته، باید برای هر کدوم یه صف جدا داشته باشه.

نکته: اگه بخوای راحت‌تر تصورش کنی، صف‌ها (queues) مثل یه گروه چت هستن—افرادی که می‌خوای بهشون پیام بدی رو انتخاب می‌کنی، پیام رو می‌نویسی و می‌فرستی. هر کسی که تو اون صف باشه، پیام رو دریافت می‌کنه.

وقتی از گزینه‌ی دوم یعنی topic استفاده می‌کنی، وارد مدل پخش سراسری (broadcasting) می‌شی. فرستنده فقط پیام رو تولید و ارسال می‌کنه—هر سرویس پایین‌دستی که بخواد اون پیام‌ها رو دریافت کنه، می‌تونه مشترک اون topic بشه. فرستنده نه می‌دونه و نه براش مهمه که چندتا سرویس دارن گوش می‌دن.

یه جورایی مثل اینه که یه ایستگاه رادیویی راه بندازی—هر کسی که بخواد، می‌تونه موج رو بگیره و گوش بده، بدون اینکه فرستنده لازم باشه تک‌تک شنونده‌ها رو بشناسه.


هر دو گزینه خوب به نظر می‌رسن—پس چطور باید انتخاب کنیم؟

تحلیل بده‌بستان‌ها (Trade-offs)

تو معماری نرم‌ افزار، همیشه باید بین گزینه‌ها انتخاب کنی—و معمولاً هیچ انتخابی بدون هزینه نیست. مثل اینه که بخوای کلی عکس با کیفیت بالا توی گوشی‌ت نگه‌داری: یا باید حافظه‌ی بیشتر بخری (که گرون‌تره)، یا عکس‌ها رو فشرده‌سازی کنی (که کیفیت‌شون پایین میاد).

این مثال ساده نشون می‌ده که بهینه‌سازی یه ویژگی معمولاً به قیمت قربانی‌کردن ویژگی دیگه‌ای تموم می‌شه. تو طراحی معماری، باید آگاهانه تصمیم بگیری که کدوم ویژگی‌ها برات اولویت دارن—و آماده باشی برای هزینه‌هایی که اون انتخاب‌ها به همراه دارن.

هر دو گزینه خوب به نظر می‌رسن—پس چطور باید انتخاب کنیم؟

اگه بدونی کدوم ویژگی‌های معماری برای پروژه‌ت از همه مهم‌ترن، می‌تونی شروع کنی به فکر کردن درباره‌ی راه‌حل‌هایی که اون ویژگی‌ها رو به حداکثر برسونن. اما هر راه‌حلی که یه ویژگی (یا چندتا) رو تقویت کنه، معمولاً به قیمت تضعیف ویژگی‌های دیگه تموم می‌شه. مثلاً یه راه‌حل که مقیاس‌پذیری بالایی داره، ممکنه باعث بشه پیاده‌سازی یا پایداری سیستم سخت‌تر بشه.

هر راه‌حلی که انتخاب کنی، یه‌سری بده‌بستان داره—یه‌سری مزایا و یه‌سری معایب.

وظیفه‌ی تو دو بخشه: اول اینکه بدونی هر راه‌حل چه بده‌بستان‌هایی با خودش داره، و دوم اینکه اون راه‌حلی رو انتخاب کنی که بیشترین خدمت رو به مهم‌ترین ویژگی‌های معماری پروژه‌ت می‌کنه.

نکته: ریچ هیکی، سازنده‌ی زبان برنامه‌نویسی Clojure، میگه: «برنامه‌نویس‌ها از مزایای همه‌چیز خبر دارن، ولی از بده‌بستان‌های هیچ‌چیز نه.» ما دوست داریم اینو اضافه کنیم: «معمارها باید هر دو رو بفهمن.»

نمی‌تونی همه‌چیز رو با هم داشته باشی. باید مشخص کنی کدوم ویژگی‌های معماری از همه مهم‌ترن، و اون راه‌حلی رو انتخاب کنی که بیشترین امکان رو برای تحقق اون ویژگی‌ها فراهم می‌کنه.

تحلیل مزایا و معایب: نسخه صف‌ها

وقتی داریم یه روش رو بررسی می‌کنیم، فقط نباید دنبال خوبی‌هاش باشیم؛ باید ببینیم چه ایرادهایی هم ممکنه داشته باشه تا بتونیم تصمیم درست‌تری بگیریم. حالا بریم سراغ صف‌ها.

اگه سرویس معاملاتی بخواد به چند سرویس مختلف پیام بده، برای هرکدوم باید یه صف جدا داشته باشیم. مثلاً اگه سرویس اعلان و سرویس تحلیل داده‌ها هرکدوم اطلاعات متفاوتی لازم دارن، می‌تونیم پیام‌های مخصوص خودشون رو براشون بفرستیم. اینطوری سرویس معاملاتی دقیقاً می‌دونه با کی در ارتباطه، و همین باعث می‌شه یه سرویس دیگه (که شاید قابل اعتماد نباشه) نتونه راحت به اطلاعات دسترسی پیدا کنه—که خب برای امنیت خیلی مهمه. تازه چون هر صف جداست، می‌تونیم هرکدوم رو جداگانه بررسی کنیم و اگه لازم شد، مستقل از بقیه بزرگ‌ترش (Scale) کنیم.

سرویس معاملاتی به‌شدت به مصرف‌کننده‌هاش وابسته‌ست—دقیقاً می‌دونه چندتا هستن و با کدوم‌ها در ارتباطه. اما هنوز مطمئن نیستیم که آیا باید به سرویس تطابق (compliance) هم پیام بفرستیم یا نه. اگه یه روز لازم بشه، باید سرویس معاملاتی رو دوباره دست‌کاری کنیم تا بتونه پیام‌ها رو به یه صف سوم هم بفرسته. خلاصه‌ش اینه که وقتی از صف‌ها استفاده می‌کنیم، داریم از قابلیت توسعه‌پذیری (Extensibility) صرف‌نظر می‌کنیم.

حالا می‌فهمی وقتی می‌گیم «تحلیل مزایا/معایب» منظورمون چیه؟

مزایا/معایب استفاده از صف‌‌ها

مزایا:

  • امکان ارسال پیام‌های متنوع برای مصرف‌کننده‌های مختلف
  • قابلیت پایش و مقیاس‌پذیری مستقل برای هر صف (که به افزایش مقیاس‌پذیری کمک می‌کنه)
  • امنیت بالاتر، چون ارتباط‌ها کنترل‌شده‌تر و محدودتر هستن

معایب:

  • وابستگی زیاد بین سرویس معاملاتی و مصرف‌کننده‌ها باعث می‌شه توسعه‌پذیری محدود بشه
  • سرویس معاملاتی باید به چند صف مختلف وصل بشه
  • نیاز به زیرساخت‌های اضافه داره

تحلیل مزایا و معایب: نسخه تاپیک‌ها

حالا بیاییم ببینیم استفاده از تاپیک‌ها چه مزایایی داره. خب، نکته مثبتش واضحه—سرویس معاملاتی فقط پیام‌ها رو به یه تاپیک ارسال می‌کنه، و هر کسی که بخواد اون پیام‌ها رو دریافت کنه، فقط کافیه مشترک اون تاپیک بشه. مثلاً اگه سرویس تطابق هم بخواد وارد ماجرا بشه، فقط باید عضو تاپیک بشه؛ نیازی نیست هیچ تغییری توی سرویس معاملاتی بدیم. وابستگی کم بین سرویس‌ها؟ عالیه!

اما تاپیک‌ها یه‌سری ایراد هم دارن. اول اینکه نمی‌تونی پیام رو برای هر سرویس به‌صورت اختصاصی تنظیم کنی—یه پیام عمومی می‌فرستی که همه باید همونو بگیرن، چه بخوان چه نه. مقیاس‌پذیری هم همین‌طوره؛ چون فقط یه تاپیک داری، همه‌چیز باید با هم بزرگ یا کوچیک بشه. و نکته مهم‌تر اینکه هر کسی می‌تونه مشترک اون تاپیک بشه بدون اینکه سرویس معاملاتی خبردار بشه—که تو بعضی شرایط ممکنه یه ریسک امنیتی باشه.

مزایا:

  • وابستگی کم بین سرویس‌ها، که توسعه‌پذیری رو راحت‌تر می‌کنه
  • سرویس معاملاتی فقط یه نقطه برای ارسال پیام‌ها داره، ساده و متمرکز

معایب:

  • همه سرویس‌ها یه پیام یکسان دریافت می‌کنن، نمی‌شه برای هرکدوم پیام اختصاصی فرستاد
  • نمی‌تونیم تاپیک رو جداگانه پایش یا مقیاس‌پذیر کنیم، که به مقیاس‌پذیری آسیب می‌زنه
  • چون هر کسی می‌تونه مشترک تاپیک بشه، امنیت پایین‌تره

قانون اول معماری نرم‌ افزار: در معماری نرم‌ افزار، همه‌چیز مزایا و معایبی داره.

در معماری نرم‌ افزار، خطوط واضح و مرتب خیلی کم پیدا می‌شن و چیزی به اسم «بهترین روش» وجود نداره. هر تصمیمی که می‌گیری، پای عوامل زیادی وسطه—و خیلی وقت‌ها این عوامل با هم در تضاد هستن. قانون اول یه درس مهمه، پس جدی‌اش بگیر. بنویسش روی یه یادداشت بچسبونش به مانیتورت. حتی اگه لازم شد، برعکسش رو روی پیشونیت خالکوبی کن تا توی آینه ببینی! هر کاری که لازمه، انجام بده.

اگر در معماری نرم‌ افزار به تصمیمی برخوردی که هیچ مصالحه‌ای نداره، یعنی هنوز به‌اندازه کافی دقیق بررسیش نکردی.

همیشه همه‌چیز برمی‌گرده به مصالحه‌ها (مزایا/معایب)

بعضی‌ها همیشه یه تکنیک، روش یا ابزار خاص رو انتخاب می‌کنن، بدون توجه به اینکه مسئله‌ی فعلی چی هست. معمولاً هم سراغ چیزی می‌رن که قبلاً باهاش موفق بودن. گاهی هم دچار چیزی می‌شن که با شوخی بهش می‌گیم «سندروم شیء براق» —یعنی فکر می‌کنن یه تکنولوژی یا روش جدید می‌تونه همه‌ی مشکلاتشون رو حل کنه.

اما فارغ از موفقیت‌های گذشته یا وعده‌های آینده، یادت باشه: هر مزیتی یه نقطه‌ضعف هم داره. فقط دو تا سؤال مهم وجود داره: «آیا مزایا بهت کمک می‌کنن یه اپلیکیشن موفق بسازی؟» و «آیا می‌تونی با معایبش کنار بیای؟»

هر وقت کسی از یه روش خاص تعریف کرد، جواب درست اینه: «مزایا/معایبش چی هستن؟»

نکته: برای اینکه سوء‌تفاهم نشه، منظورمون این نیست که نباید از ابزارها و روش‌های جدید استفاده کنی—بالاخره این خودش نشونه‌ی پیشرفته، درسته؟ فقط یادت نره موقع تصمیم‌گیری، مصالحه‌ها (مزایا/معایب) رو هم در نظر بگیری.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *