مهندسی نرمافزار فقط درباره نوشتن کد نیست. هرچه یک محصول بزرگتر میشود، تصمیمهایی که درباره ساختار سیستم، ارتباط اجزا، دادهها، امنیت، مقیاسپذیری و نحوه توسعه آن میگیریم، اهمیت بیشتری پیدا میکنند.
اینجاست که معماری نرمافزار (Software Architecture) وارد میشود.
یک کد ممکن است امروز کاملاً درست کار کند، اما اگر ساختار مناسبی نداشته باشد، با رشد محصول هزینه تغییر، توسعه و نگهداری آن بهشدت افزایش پیدا میکند. معماری نرمافزار تلاش میکند پیش از آنکه این هزینهها به یک مشکل جدی تبدیل شوند، درباره ساختار و تصمیمهای مهم سیستم فکر کند.
معماری نرمافزار را میتوان مجموعهای از تصمیمهای کلیدی درباره ساختار یک سیستم نرمافزاری دانست.
این تصمیمها مشخص میکنند:
بنابراین معماری فقط یک Diagram زیبا نیست.
یک نمودار که چند مستطیل را با فلش به هم وصل کرده، بهتنهایی معماری نیست. هرچند انسانها علاقه عجیبی به کشیدن همین مستطیلها دارند.
معماری در اصل درباره تصمیمها و Trade-offها است.
یکی از سؤالهای رایج این است که معماری نرمافزار چه تفاوتی با طراحی دارد.
مرز این دو همیشه کاملاً مشخص نیست، اما میتوان یک تفکیک کاربردی در نظر گرفت.
Software Architecture بیشتر با تصمیمهای سطح بالاتر سروکار دارد:
در مقابل، Software Design بیشتر روی نحوه پیادهسازی اجزای سیستم تمرکز میکند:
البته این دو کاملاً جدا از یکدیگر نیستند. یک تصمیم معماری میتواند روی طراحی داخلی سیستم اثر بگذارد و برعکس.
وقتی یک پروژه کوچک است، بسیاری از مشکلات معماری خودشان را نشان نمیدهند.
ممکن است همهچیز در یک Repository باشد، چند API ساده داشته باشیم و یک Database مرکزی استفاده کنیم.
اما با رشد محصول:
در این مرحله، تصمیمهایی که ماهها یا سالها قبل گرفتهایم شروع به اثرگذاری میکنند.
معماری خوب قرار نیست تمام مشکلات آینده را پیشبینی کند.
هدف معماری این است که سیستم را طوری طراحی کنیم که تغییر دادن آن در آینده بیش از حد پرهزینه نباشد.
یک تصور اشتباه این است که معماری خوب الزاماً یعنی:
Microservices + Kubernetes + Event Bus + چندین Database
این ترکیب شاید در بعضی سیستمها کاملاً منطقی باشد، اما برای بسیاری از پروژهها فقط مقدار زیادی پیچیدگی اضافه ایجاد میکند.
معماری خوب معمولاً معماریای است که با نیازهای واقعی سیستم تناسب داشته باشد.
برای مثال، یک محصول کوچک ممکن است با یک Monolith تمیز و ساختاریافته سالها بهخوبی کار کند.
در چنین شرایطی تبدیل آن به دهها سرویس مستقل لزوماً پیشرفت معماری نیست.
گاهی بهترین تصمیم معماری این است که چیزی را پیچیده نکنیم.
تقریباً هیچ تصمیم معماری کاملاً رایگان نیست.
فرض کنید میخواهیم سیستم را Highly Available کنیم.
این تصمیم ممکن است باعث شود:
یا اگر Microservices را انتخاب کنیم، استقلال تیمها و Deployment ممکن است بهتر شود، اما در مقابل:
بنابراین سؤال معماری معمولاً این نیست:
«کدام معماری بهترین است؟»
سؤال بهتر این است:
«کدام Trade-offها برای مسئلهای که داریم قابل قبول هستند؟»
این یکی از مهمترین تغییرات ذهنی در مسیر تبدیل شدن از Developer به Software Architect است.
وقتی درباره معماری صحبت میکنیم، فقط Functionality مهم نیست.
سیستم باید علاوه بر انجام دادن کار موردنظر، ویژگیهای دیگری نیز داشته باشد.
برخی از مهمترین Quality Attributeها عبارتاند از:
سیستم با چه سرعتی پاسخ میدهد؟
با افزایش کاربران یا دادهها، سیستم چگونه رشد میکند؟
در صورت بروز خطا، سیستم تا چه اندازه قابل اعتماد باقی میماند؟
چگونه از دادهها و منابع سیستم محافظت میکنیم؟
تغییر دادن و نگهداری سیستم چقدر آسان است؟
سیستم چه میزان از زمان باید در دسترس باشد؟
این ویژگیها معمولاً روی معماری تأثیر مستقیم دارند.
برای مثال، اگر یک سیستم باید میلیونها Request در ثانیه پردازش کند، معماری آن احتمالاً با یک سیستم داخلی کوچک کاملاً متفاوت خواهد بود.
یکی از اشتباهات رایج، Overengineering است.
توسعهدهنده قبل از اینکه نیاز واقعی ایجاد شود، برای مشکلات احتمالی آینده راهحل میسازد.
مثلاً:
«شاید یک روز ده میلیون کاربر داشته باشیم، پس از همین الان باید سیستم را Microservice کنیم.»
مشکل اینجاست که آینده را نمیتوان با قطعیت پیشبینی کرد.
معماری باید بر اساس نیازهای شناختهشده و ریسکهای واقعی شکل بگیرد، نه سناریوهای خیالی.
این به معنی نادیده گرفتن آینده نیست.
بلکه یعنی سیستم را طوری طراحی کنیم که در صورت تغییر نیازها، امکان تکامل آن وجود داشته باشد.
یکی از روشهای مفید برای مدیریت تصمیمهای معماری، استفاده از Architecture Decision Record یا ADR است.
در یک ADR میتوان ثبت کرد:
Context
چه مسئلهای داشتیم؟
Decision
چه تصمیمی گرفتیم؟
Alternatives
چه گزینههای دیگری بررسی شدند؟
Consequences
این تصمیم چه مزایا و هزینههایی دارد؟
این مستندات کمک میکنند چند ماه بعد تیم مجبور نباشد دوباره بپرسد:
«چرا این سیستم را اینطوری ساختیم؟»
چون حافظه تیم، برخلاف چیزی که در جلسات Planning تصور میشود، یک Database قابل اعتماد نیست.
Software Architect الزاماً فردی نیست که فقط Diagram طراحی کند.
نقش معماری بیشتر به تصمیمگیری فنی در سطح سیستم مربوط است.
یک معمار نرمافزار باید بتواند:
به همین دلیل معماری نرمافزار فقط یک مهارت تکنیکی نیست.
Communication، تحلیل و درک Business نیز بخشی از آن هستند.
برای یادگیری معماری، صرفاً خواندن چند Design Pattern کافی نیست.
یک مسیر منطقی میتواند شامل این موضوعات باشد:
اما مهمتر از تعداد موضوعاتی که میخوانید، توانایی ارتباط دادن این مفاهیم به مسائل واقعی است.
خواندن درباره معماری نقطه شروع خوبی است، اما معماری یک مهارت تصمیمگیری است.
مثلاً به جای اینکه فقط درباره Caching مطالعه کنیم، یک مسئله واقعی مطرح کنیم:
یک API داریم که اطلاعات محصول را برمیگرداند و میلیونها بار در روز فراخوانی میشود. آیا Cache اضافه کنیم؟
بعد سؤالهای بیشتری مطرح میشود:
اینجاست که دانش معماری از یک مفهوم تئوری به تصمیم مهندسی تبدیل میشود.
مهندسی معماری نرمافزار درباره پیدا کردن یک معماری «کامل» نیست.
چنین چیزی معمولاً وجود ندارد.
معماری درباره این است که با توجه به نیازهای محصول، محدودیتهای فنی، هزینه، تیم و آینده احتمالی سیستم، تصمیمهای آگاهانه بگیریم.
یک Software Architect خوب کسی نیست که پیچیدهترین معماری را طراحی کند.
بلکه کسی است که بتواند بفهمد:
چه چیزی باید پیچیده باشد، چه چیزی نباید پیچیده شود و چرا.
و شاید همین سؤال، یکی از مهمترین تفاوتهای میان نوشتن نرمافزار و مهندسی سیستمهای نرمافزاری باشد.