فصل ۶. معماری لایه‌ای: جداسازی دغدغه‌ها

کتابمعماری نرم افزار

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

معماری لایه‌ای به همین دلیل تبدیل شده به اون گزینه‌ی ساده:
فهمش آسونه، پیاده‌سازیش راحته، و از الگوهای طراحی‌ای استفاده می‌کنه که توسعه‌دهنده‌ها از قبل باهاشون آشنان.

بیایم لایه‌های این معماری رو یکی‌یکی بررسی کنیم.

شرکت فرضی Naan & Pop: جمع‌آوری نیازمندی‌ها

سانگیتا غذاهای ساده رو دوست داره، برای همین یه رستوران خانوادگی با الهام از غذاهای هندی راه انداخت به اسم Naan & Pop، که تخصصش توی ساندویچ‌ها و نوشابه‌ست.
این رستوران به یه وب‌سایت نیاز داره که مشتری‌ها بتونن سفارش آنلاین بدن.
چون Naan & Pop یه استارت‌آپه با بودجه‌ی محدود، وب‌سایت باید ساده باشه و سریع ساخته بشه.

سانگیتا چند نیاز مشخص داره.

زمان ورود به بازار

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

تفکیک مسئولیت‌ها

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

ساده، اما قابل گسترش

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

سانگیتا یه‌کم تجربه‌ی توسعه‌ی نرم‌افزار داره و می‌دونه که خیلی از این اهداف نیازمند تفکیک درست مسئولیت‌ها هستن.
اون این نیازمندی‌ها رو به تیم توسعه‌ای که برای این پروژه استخدام کرده منتقل می‌کنه.
تو هم عضوی از اون تیمی—پس با دقت گوش کن.

گفت‌وگوی درون تیمی

الکس(Alex): مدیر پروژه‌مون همین الان نیازمندی‌ها و اهداف اپلیکیشن وب Naan & Pop رو فرستاد. خیلی ساده‌ست. نمی‌تونیم یه فریم‌ورک یا کتابخونه‌ی آماده پیدا کنیم که بیشتر کارها رو انجام بده؟
مارا(Mara): اون راه‌حل هدف سادگی رو پوشش می‌ده. ولی سانگیتا قابلیت گسترش هم می‌خواد، و فریم‌ورک‌های آماده معمولاً یه‌کم خشک و محدودن.
سم(Sam): چه نوع گسترشی مد نظرشه؟
مارا: اگه رستوران موفق بشه، ممکنه بخوایم سایت از رابط‌های کاربری مختلف پشتیبانی کنه، یا نقاط اتصال برای سرویس‌های تحویل غذا بسازیم.
الکس: آره—اپلیکیشن‌های ساده‌ی آماده ممکنه نتونن تفکیک مسئولیت‌هایی که سانگیتا برای اون نوع گسترش نیاز داره رو فراهم کنن.
سم: ولی ما وقت نداریم یه معماری پیچیده بسازیم!
الکس: این غیرممکن به نظر می‌رسه—چطور می‌تونیم یه معماری درست با تخصص‌های مختلف بسازیم وقتی این‌قدر محدودیت زمانی داریم؟
سم: خوشبختانه قبلاً با اعضای دیگه‌ی تیم روی ویژگی‌های معماری (برای قابلیت‌های اپلیکیشن) و طراحی دامنه (برای رفتارش) کار کردیم. فقط باید معماری مناسب رو انتخاب کنیم.
مارا: اینا مصالحه‌های (Trade-offs) جدی و اهداف متضادی هستن. ما به یه سبک معماری ساده نیاز داریم که اجازه بده مسئولیت‌ها رو حول حوزه‌های فنی مثل رابط کاربری، داده، منطق تجاری و غیره جدا کنیم. این‌طوری، اضافه کردن یه رابط کاربری جدید فقط روی یه لایه تأثیر می‌ذاره.
الکس: «تفکیک مسئولیت‌ها…» همین عبارت رو توی کتاب Head First Design Patterns خوندم! داشتم درباره‌ی الگوی طراحی Model-View-Controller می‌خوندم.
سم: آره، ولی اون یه الگوی طراحیه—چطور می‌خوای اون رو به معماری ترجمه کنی؟
مارا: خیلی از الگوهای طراحی در نهایت وارد معماری می‌شن، چون اهدافشون اغلب هم‌پوشانی دارن. ولی در حالی که الگوهای طراحی فقط روی عناصر طراحی تمرکز دارن، معماری باید محدودیت‌های دنیای واقعی رو هم در نظر بگیره.
بیایم کتاب رو باز کنیم ببینیم می‌تونیم Model-View-Controller رو به معماری تبدیل کنیم یا نه.

الگوهای طراحی (Design patterns)

برای توضیح مفهوم الگوهای طراحی، کتاب تأثیرگذار Head First Design Patterns از الگوی طراحی Model-View-Controller (MVC) استفاده می‌کنه—که قابلیت‌ها رو بر اساس هدفشون از هم جدا می‌کنه.

نکته:
«الگوی طراحی» یه راه‌حل با زمینه‌ی مشخص برای یه مسئله‌ی رایج در طراحی نرم‌افزاره.

در MVC، مدل (Model) نمایانگر منطق تجاری و موجودیت‌های داخل اپلیکیشنه؛
نما (View) نمایانگر رابط کاربریه و کنترلر (Controller) جریان کاری رو مدیریت می‌کنه و اجزای مدل رو به هم وصل می‌کنه تا عملکرد اپلیکیشن رو فراهم کنه.

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

لایه‌بندی MVC

الگوهای طراحی راه‌حل‌های منطقی برای مسائل ارائه می‌دن،
اما معماری باید با محدودیت‌های دنیای واقعی مثل پایگاه‌های داده، رابط‌های کاربری، و جزئیات اجرایی دیگه سروکار داشته باشه.

مصاحبه‌ی امروز

لایه‌لایه کردن با ستاره‌ی معماری: لایه

Head First: خوش اومدی، لایه، به استودیوی لوکس ما. می‌دونم برنامه‌ت شلوغه، ممنون که وقت گذاشتی.
Layer: قابلی نداره. همون‌طور که گفتی، من حسابی مهمم. حتی یه سبک معماری رو به اسم من نام‌گذاری کردن!
Head First: بیایم وارد جزئیات بشیم، لایه. چرا باید یه معماری کامل رو بر پایه‌ی تو بنا کرد؟
Layer: سؤال خوبی بود. من باعث می‌شم معماری اپلیکیشن‌ها قابل فهم و منظم باشه، چون هر لایه یه مسئولیت مشخص داره.
Head First: یعنی این معماری فقط برای آدم‌های وسواسیه؟
Layer: نه! جدا کردن عملکردهای مشابه در لایه‌های مختلف باعث می‌شه راحت‌تر پیداشون کنیم و تغییرشون بدیم. مثلاً اگه تیم بخواد یه پایگاه داده‌ی جدید اضافه کنه، فقط باید لایه‌ی پایایی (Persistence Layer) رو تغییر بده.
Head First: آهان، پس سازمان‌دهی باعث می‌شه کشف و به‌روزرسانی راحت‌تر بشه. دلیل خوبی برای یه معماریه.
Layer: سازمان‌دهی یکپارچه خوبه، ولی تنها دلیل برای انتخاب من نیست.
Head First: منظورت چیه؟
Layer: نمی‌خوام پُز بدم، ولی ما لایه‌ها خیلی انعطاف‌پذیریم—می‌شه ازمون برای کلی چیز استفاده کرد!

Head First: خب، می‌دونم معمولاً برای رابط‌های کاربری حضور داری و جای خوبی برای منطق تجاری فراهم می‌کنی.
Layer: قطعاً، بار اصلی اون‌ها رو ما به دوش می‌کشیم. ولی تیم‌ها می‌تونن ما رو برای انواع رابط‌های کاربری شکل بدن. مثلاً یه لایه‌ی سرویس می‌تونه رابطی برای اپلیکیشن‌های دیگه باشه که می‌خوان با این یکی تعامل داشته باشن.
Head First: یه مثال خوب داری از اینکه تیم‌ها چطور ازت استفاده کردن؟
Layer: معلومه! با یه تیمی کار کردم که برنامه‌های وفاداری برای یه هتل رو مدیریت می‌کرد. هر خریدی که کاربر انجام می‌داد می‌تونست امتیاز بگیره، بسته به وضعیت عضویتش، سال‌های عضویت، و کلی چیز پیچیده‌ی دیگه. اون تیم یه لایه‌ی امتیازدهی ساخت که همه‌ی محاسبات رو توی یه جا نگه می‌داشت.
Head First: خب، به نظر مفید میاد. می‌تونی درباره‌ی شایعات اخیر درباره‌ی رابطه‌ی سردت با طراحی دامنه‌محور (DDD) توضیح بدی؟

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

اما هنوز نفهمیدم چطوری کار می‌کنه. درخواست کاربر چطور توی ساختار لایه‌ای‌ که داریم می‌سازیم جا می‌گیره؟

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

لایه‌لایه کردن

برای اپلیکیشنی مثل سایت Naan & Pop، تیم شما قراره کامپوننت‌های منطقی‌ای بسازه که با مسئله هم‌خوانی داشته باشن. اما چطور این کامپوننت‌ها رو پیاده‌سازی می‌کنین؟

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

  • com.naanpop.orderapp.presentation → لایه‌ی ارائه (رابط کاربری)
  • com.naanpop.orderapp.workflow → لایه‌ی جریان کاری
  • com.naanpop.orderapp.model → لایه‌ی مدل (منطق تجاری و موجودیت‌ها)
  • com.naanpop.orderapp.persistence → لایه‌ی پایداری (دسترسی به داده‌ها)

نکته:
نام‌های کامل این لایه‌ها به‌صورت پکیج در Java، فضای‌نام (namespace) در .NET، یا هر مکانیزم نام‌گذاری‌ای که زبان برنامه‌نویسی انتخابی‌تون استفاده می‌کنه ظاهر می‌شن.

ترجمه‌ی لایه‌ها به کد

وقتی تیم شما پکیج‌های کامپوننت (یا فضای‌نام‌ها) رو ساخت، باید به توسعه‌دهنده‌ها کمک کنین تا معماری رو پیاده‌سازی کنن.

در ادامه، یه مثال شبه‌کد به سبک Python آورده شده تا نشون بده لایه‌ها چطور به کد تبدیل می‌شن.

لایه‌ی رابط کاربری، یا همون لایه‌ی ارائه (Presentation)، بالاترین لایه‌ست.
وظیفه‌ش تعامل با کاربره—و همون نقشی رو ایفا می‌کنه که بخش View در MVC داره.

def UI_layer(request):
    # دریافت درخواست از کاربر
    data = request.get_data()

    # ارسال داده به لایه‌ی منطق تجاری
    return business_logic_layer(data)

لایه‌ی جریان کاری (workflow – که گاهی بهش لایه‌ی قواعد تجاری (business rules) هم گفته می‌شه) مسئول پردازش هر درخواستیه که از لایه‌ی رابط کاربری (UI) میاد—و باید یه پاسخ برگردونه.

# پردازش داده‌ها
def business_logic_layer(data):  # از لایه‌ی UI
    processed_data = process_data(data)

    # ارسال داده‌ی پردازش‌شده به لایه‌ی دسترسی به داده
    return data_access_layer(processed_data)

لایه‌ی پایداری (یا لایه‌ی دسترسی به داده) مسئول دسترسی به داده‌ها از پایگاه داده‌ست—و باید اون داده‌ها رو به لایه‌ی جریان کاری (workflow) برگردونه.

# دسترسی به داده‌ها در پایگاه داده
def data_access_layer(data):
    retrieved_data = retrieve_data(data)

    # بازگرداندن داده‌ی واکشی‌شده به لایه‌ی جریان کاری
    return retrieved_data

توی فصل ۵ گفتین که هر سبک معماری یه دسته‌بندی و یه فلسفه داره. معماری لایه‌ای توی کدوم دسته قرار می‌گیره؟
خوشحالیم که داری به این موضوع فکر می‌کنی. همون‌طور که توی فصل ۵ گفتیم، درک دسته‌بندی‌ها خیلی چیزها رو درباره‌ی ویژگی‌هایی که یه سبک معماری خاص پشتیبانی می‌کنه روشن می‌کنه.
معماری لایه‌ای یه سبک معماری با تفکیک فنی (technically partitioned) محسوب می‌شه، که معمولاً به‌صورت یکپارچه (monolith) پیاده‌سازی می‌شه.
(می‌گیم «معمولاً» چون قراره به‌زودی درباره‌ی چند مدل متفاوت از این سبک صحبت کنیم.)

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

چرا دقیقاً این لایه‌ها—ارائه (presentation)، جریان کاری (workflow)، و پایداری (persistence)؟
این‌ها لایه‌های رایجی هستن، اما الزام‌آور نیستن. بیشتر اپلیکیشن‌ها حداقل بخشی از این جداسازی رو دارن:
مثلاً رابط کاربری معمولاً از منطق اصلی سیستم جداست، و اون هم از توسعه‌ی پایگاه داده جداست.

آیا معماری لایه‌ای از الگوی طراحی Model-View-Controller الهام گرفته؟
احتمالاً برعکسش درسته. معماری‌های لایه‌ای، که از زمانی که مردم شروع به ساخت نرم‌افزار از بخش‌های مختلف کردن وجود داشتن، ممکنه خودشون الهام‌بخش اون الگو بوده باشن.
الگوهای طراحی معمولاً از مشاهده‌ی رخدادهای رایج استخراج می‌شن، و معماری لایه‌ای مدت‌هاست که در شکل‌های مختلف وجود داشته.

دامنه‌ها، کامپوننت‌ها، و لایه‌ها

توی یه سیستم ساده‌ی سفارش غذا مثل Naan & Pop، ممکنه بر اساس دامنه‌ی مسئله با کامپوننت‌های زیر روبه‌رو بشیم:

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

وقتی کامپوننت‌های منطقی رو به جریان‌های کاری (workflow) و موجودیت‌ها (entities) تقسیم کردیم، می‌تونیم این کامپوننت‌ها رو به شکل زیر لایه بندی کنیم.

به نظر می‌رسه هر معماری یه‌سری مزایا داره، اما در عین حال محدودیت‌هایی هم تحمیل می‌کنه.
کاش می‌شد معماری‌ای داشت که دقیقاً با دامنه‌ی مسئله‌م هم‌خوانی داشته باشه، بدون هیچ مصالحه‌ی آزاردهنده‌ای!
اما این فقط یه رویاست…

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

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

محرک‌های انتخاب معماری لایه‌ای

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

تخصص‌گرایی

استفاده از معماری لایه‌ای به سازمان‌ها این امکان رو می‌ده که تیم‌ها رو به متخصص‌هایی تقسیم کنن که قابلیت‌هاشون رو بین پروژه‌های مختلف به اشتراک می‌ذارن.

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

هم‌خوانی با جداسازی فیزیکی

معماری لایه‌ای معمولاً کامپوننت‌های منطقی رو طوری جدا می‌کنه که با جداسازی فیزیکی هم‌خوانی داشته باشه. برای مثال، رایجه که تیم‌ها لایه‌های مختلف رو با تکنولوژی‌های متفاوتی پیاده‌سازی کنن (مثل JavaScript، Java، و MySQL).

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

سهولت در استفاده‌ی مجدد (فنی)

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

نکته
قابلیت استفاده‌ی مجدد از کامپوننت‌ها درون یک لایه یکی از مزایای کلیدی این معماری برای بسیاری از سازمان‌هاست.

دوقلوی مفهومیِ MVC

سادگی و دغدغه‌های مربوط به امکان‌پذیری از عوامل محرک در بسیاری از معماری‌ها هستن.
توسعه‌دهنده‌ها راحت‌تر می‌تونن معماری‌ای رو درک کنن و باهاش کار کنن که با الگوهای طراحی آشنا—مثل MVC—هم‌خوانی داشته باشه.

معماری لایه‌ای در دنیای واقعی: معماری‌های فیزیکی

معماری لایه‌ایِ یکپارچه (layered monolith) یه معماری منطقی رو توصیف می‌کنه،
اما معمارها ممکنه اون معماری منطقی رو در انواع مختلفی از معماری‌های فیزیکی پیاده‌سازی کنن.

معماری دو-لایه (Two-Tier)

در معماری دو-لایه، لایه‌های ارائه‌ (Presentation)، منطق تجاری (Business rules)، و پایداری (Persistence) همگی در یک واحد اجرایی قرار می‌گیرن و از طریق شبکه‌ی محلی با پایگاه داده ارتباط برقرار می‌کنن. این معماری فیزیکی در اپلیکیشن‌های دسکتاپ و کلاینت/سرور رایجه.

مثال: نرم‌افزار حسابداری شرکتی که به‌صورت یک اپلیکیشن دسکتاپ اجرا می‌شه و از یک پایگاه داده‌ی مشترک استفاده می‌کنه

معماری سه-لایه (Three-Tier)

در معماری سه-لایه، هر مسئولیت در یک لایه‌ی فیزیکی جداگانه قرار می‌گیره.
مثال خوب برای این معماری یه اپلیکیشن تحت وبه، با یک سرور اپلیکیشن برای مدیریت لایه‌ی میانی و یک لایه‌ی ارائه که معمولاً با تکنولوژی متفاوتی نوشته می‌شه.

برای مثال، تیم توسعه ممکنه منطق تجاری و پایداری رو با Java بنویسه، در حالی که رابط کاربری با HTML و JavaScript ساخته شده، و همه از یک پایگاه داده رابطه‌ای برای پایداری استفاده می‌کنن

سیستم‌های توکار (Embedded) / موبایل

اغلب به‌ دلیل محدودیت‌های فیزیکی، همه‌ی لایه‌های منطقی در یک واحد اجرایی فیزیکی قرار می‌گیرن. این نوع معماری فیزیکی معمولاً در سیستم‌های توکار (embedded systems) و اپلیکیشن‌های موبایل دیده می‌شه، جایی که اتصال شبکه ممکنه پایدار نباشه یا اصلاً وجود نداشته باشه.

مثال: بازی موبایلی، یا نرم‌افزار دستگاه فروش نوشابه

مصالحه‌های معماری فیزیکی

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

معماری دو-لایه (Two-Tier)

مزایا:

  • رابط کاربری غنی
  • عملکرد بالا
  • ساده (چون همه‌چیز در یک پروژه پیاده‌سازی می‌شود)

معایب:

  • مقیاس‌پذیری متوسط
  • با رشد بیشتر پیچیدگی هم بیشتر میشه
  • در صورت نیاز به قابلیت اطمینان بالا، پیچیده می‌شود

نکته:
قابلیت اطمینان فقط در حد متوسط است چون این معماری برای دسترسی به داده‌ها به شبکه وابسته است.

معماری سه-لایه (Three-Tier)

مزایا:

  • رابط کاربری جداشده
  • مقیاس‌پذیری بیشتر
  • بهره‌مندی از مزایای معماری توزیع‌شده

معایب:

  • پیچیدگی بیشتر (چون اجزای متحرک بیشتری دارد)
  • قابلیت اطمینان کمتر
  • دردسرهای معماری توزیع‌شده

نکته:
معماری‌های توزیع‌شده معمولاً در مقیاس‌پذیری بالا بهتر عمل می‌کنند.

معماری توکار / موبایل (Embedded/Mobile)

مزایا:

  • مستقل و خودکفا
  • استفاده از یک تکنولوژی واحد برای سادگی بیشتر
  • کاملا منطبق با دستگاه‌های سخت‌افزاری

معایب:

  • کمترین مقیاس‌پذیری
  • معمولاً وابسته به پلتفرم اجرایی
  • محدودیت منابع

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

گفتگوی درون تیمی

الکس: آیا «نان و پاپ» به‌قدر کافی عمومی هست که فقط از لایه‌های استاندارد استفاده کنه؟ تیم‌ها چه زمانی لایه اضافه می‌کنن؟

سم: چرا باید به معماری لایه اضافه کنیم؟

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

سم: چه نوع لایه‌هایی؟

الکس: رایجه که یه لایه‌ی خدمات (Services Layer) اضافه بشه، که دسترسی برای یکپارچگی بین کسب‌وکارها (Business-to-Business Integration) رو فراهم می‌کنه، یا لایه‌های یکپارچه‌سازی برای سیستم‌های داخلی دیگه. هر درخواست از هر لایه عبور می‌کنه، پس لایه‌ها باید چیزهایی باشن که برای هر درخواست اتفاق می‌افتن.

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

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

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

سم: پس اگه یه رابط کاربری جدا برای موبایل اضافه کنیم، فقط باید یه لایه رو تغییر بدیم؟

مارا: این یکی از بهترین ویژگی‌های معماری لایه‌ایه!

هشدار نهایی درباره‌ی تغییرات دامنه‌ی مسئله

یکی از مزایای اصلی معماری لایه‌ای اینه که به ما اجازه می‌ده اجزای فنی مشابه رو کنار هم گروه‌بندی کنیم. برای مثال، در اپلیکیشن «نان و پاپ»، جدا کردن رابط کاربری (UI) به‌عنوان مجموعه‌ای مستقل از کامپوننت‌ها باعث می‌شه تیم بتونه انواع جدیدی از رابط کاربری رو اضافه کنه بدون اینکه لایه‌های دیگه تحت تأثیر قرار بگیرن.

اما یه لحظه مکث کنیم و فکر کنیم—اگه تغییر در دامنه‌ی مسئله اتفاق بیفته چی؟
مثلاً اگه «نان و پاپ» بخواد به‌جای فقط ساندویچ، پیتزا هم به منو اضافه کنه، آیا همه‌ی لایه‌ها باید تغییر کنن؟

توانایی تغییر دادن اجزا به‌صورت ایزوله، قدرت ویژه‌ی معماری لایه‌ایه—اما این معماری گلوله‌ی جادویی نیست. مصالحه‌ی بزرگ این سبک معماری اینه که دامنه‌ی مسئله در سراسر لایه‌ها پخش شده. برای مثال، کامپوننت منطقی «ثبت سفارش» در معماری «نان و پاپ» نیاز به رابط کاربری (presentation)، کدی برای پیاده‌سازی جریان کاری (workflow)، و یک شِمای داده (persistence) داره.
یعنی چی؟ یعنی قابلیت‌های فنی به‌راحتی قابل تغییر و ارتقاء هستن، اما تغییرات در دامنه‌ی مسئله می‌تونن اثرات جانبی ایجاد کنن که به تمام لایه‌ها سرایت کنن.

معماری‌های لایه‌ای تغییرات فنی رو تسهیل می‌کنن، اما تغییرات در دامنه‌ی مسئله رو دشوارتر می‌سازن.

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

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

قدرت‌های ویژه‌ی معماری لایه‌ای

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

امکان‌سنجی (Feasibility)

  • اگر زمان و بودجه بسیار مهم باشن، سادگی این معماری جذاب خواهد بود.
  • نکته: اگر کل شرکت بر پایه‌ی سرمایه‌گذاری فعالیت می‌کنه، امکان‌سنجی اهمیت بیشتری پیدا می‌کنه.

تقسیم‌بندی فنی (Technical Partitioning)

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

پردازش داده‌محور (Data-Intensive)

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

کارایی (Performance)

  • مونولیت‌های لایه‌ای که خوب طراحی شده باشن، می‌تونن عملکرد بالایی داشته باشن—بدون تماس‌های شبکه‌ای و با پردازش داده در یک مکان واحد (پایگاه داده‌ی مونولیت)، نیازی به تماس‌های شبکه‌ای که ممکنه عملکرد رو کاهش بدن وجود نداره.

سرعت ساخت (Quick to Build)

  • سادگی به‌علاوه‌ی یک واحد کاری/اجرایی باعث می‌شه تیم‌ها بتونن سیستم‌های کوچک رو خیلی سریع بسازن.

سبک و چابک (Lean and Mean)

  • کوچک نگه‌داشتن این سیستم‌ها کمک می‌کنه از بعضی از نقاط ضعف جدی (مثل پیچیدگی بیش‌ازحد) جلوگیری بشه.

نقاط ضعف معماری لایه‌ای

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

قابلیت استقرار (Deployability)

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

درهم‌تنیدگی (Big Ball of Mud)

  • چون همه‌چیز به همه‌چیز وصله، این معماری بدون نظارت دقیق می‌تونه به یک آشفتگی به‌شدت درهم‌تنیده تبدیل بشه.

مقیاس‌پذیری (Scalability)

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

کشسانی (Elasticity)

  • یک فرآیند واحد به‌سختی می‌تونه با انفجار ناگهانی کاربران کنار بیاد.

قابلیت تست (Testability)

  • اتصال شدید بین اجزا و حجم بالای کد باعث می‌شه تست کردن سیستم به‌مرور زمان سخت‌تر و زمان‌برتر بشه.

رتبه‌بندی معماری لایه‌ای

تیم معماری «نان و پاپ» تصمیم می‌گیره از نمودار رتبه‌بندی‌ای استفاده کنه که در کتاب اصول معماری نرم‌افزار (انتشارات O’Reilly) معرفی شده—نوشته‌ی دو نفر از نویسندگان همین کتاب.
این نمودار، معماری لایه‌ای رو به‌صورت خلاصه و قابل‌فهم توصیف می‌کنه.

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

ویژگی معماریامتیاز
قابلیت پشتیبانی – Maintainability⭐️
قابلیت تست – Testability⭐️⭐️
قابلیت استقرار – Deployability⭐️
سادگی – Simplicity⭐️⭐️⭐️⭐️⭐️
قابلیت تحول پذیری – Evolvability⭐️
کارایی – Performance⭐️⭐️⭐️
قابلیت مقیاس پذیری – Scalability⭐️
کشسانی – Elasticity⭐️
تحمل خطا – Fault Tolerance⭐️
هزینه کلی$

جمع‌بندی

تبریک! تیم «نان و پاپ» چندین سبک معماری مختلف رو بررسی کرد،
اما پس از در نظر گرفتن اولویت‌های کسب‌وکار، معماری لایه‌ای رو انتخاب کردید.
این انتخاب کاملاً نتیجه داد و به کسب‌وکار اجازه داد بدون هیچ مشکلی رشد کنه.

نکات کلیدی

  • معماری لایه‌ای یکپارچه (monolithic) است: کل سیستم (کد و پایگاه داده) به‌صورت یک پکیج واحد مستقر می‌شود.
  • لایه‌ها بر اساس قابلیت‌های فنی از هم جدا می‌شوند. لایه‌های معمول در این معماری شامل ارائه (برای رابط کاربری)، قوانین تجاری (برای جریان کاری و منطق برنامه)، و پایداری (امکاناتی برای پشتیبانی از پایگاه داده در سیستم‌هایی که به داده‌ی پایدار نیاز دارند) هستند.
  • معماری لایه‌ای از نظر امکان‌سنجی پشتیبانی خوبی دارد؛ فهم آن آسان است و اجازه می‌دهد سیستم‌های ساده را سریع بسازید.
  • معماری لایه‌ای جداسازی فنی بسیار خوبی را پشتیبانی می‌کند، که افزودن قابلیت‌های جدید مانند رابط‌های کاربری یا پایگاه‌های داده را آسان می‌سازد.
  • معماری لایه‌ای برخی دغدغه‌های الگوی طراحی Model-View-Controller را تقلید می‌کند، اما آن‌ها را به لایه‌های فیزیکی تبدیل کرده و با محدودیت‌های دنیای واقعی تطبیق می‌دهد.
  • درخواست‌های کاربر از طریق رابط کاربری و از هر لایه عبور می‌کنند تا پاسخ به کاربر بازگردانده شود.
  • هر درخواست در این معماری از هر لایه عبور می‌کند.
  • توانایی‌های معماری لایه‌ای در طول زمان کاهش می‌یابد اگر تیم‌ها به افزودن قابلیت‌ها ادامه دهند—به‌دلیل محدودیت‌های منابع (برای مثال، ظرفیت حافظه).
  • معماری لایه‌ای پشتیبانی عالی برای تخصص‌گرایی فراهم می‌کند (طراحان رابط کاربری، برنامه‌نویسان، متخصصان پایگاه داده و غیره).
  • کامپوننت‌های منطقی نمایانگر دامنه‌ی مسئله هستند، اما لایه‌ها بر قابلیت‌های فنی تمرکز دارند، که نیاز به ترجمه بین دامنه و لایه‌های معماری را ایجاد می‌کند.
  • معماری لایه‌ای ممکن است در چندین معماری فیزیکی ظاهر شود، از جمله دو-لایه (که به‌عنوان کلاینت/سرور نیز شناخته می‌شود)، سه-لایه (وب)، و توکار/موبایل.
  • تغییر و افزودن به قابلیت‌های فنی موجود در لایه‌ها آسان است؛ معماری لایه‌ای این کار را تسهیل می‌کند.
  • تغییر دامنه‌ی مسئله نیازمند هماهنگی بین لایه‌های معماری است، که تغییرات دامنه‌ای را دشوارتر می‌سازد.

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

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