Clean Architecture
کتاب معماری تمیز رو تموم کردم. یه یادداشت بزارم وقتی خواستم مطالعه کنم مرور بشه. شاید به مرور بهش یه چیزایی بعدا اضافه کنم.
SOLID چیست؟
SOLID مجموعهای از ۵ اصل برای طراحی بهتر کد و نرمافزار است. هدفش این است که کدها قابلفهمتر، قابلتغییرتر و قابلآزمایشتر باشند.
1. اصل تکمسئولیتی — Single Responsibility Principle
توضیح ساده:
هر کلاس یا ماژول باید فقط یک مسئولیت اصلی داشته باشد و فقط به یک دلیل تغییر کند.
مثال بدون کد:
فرض کن در یک فروشگاه اینترنتی، یک بخش همزمان این کارها را انجام میدهد:
- محاسبه مبلغ سفارش
- ذخیره سفارش در پایگاه داده
- ارسال ایمیل به مشتری
- ساخت فاکتور PDF
اگر قالب فاکتور تغییر کند، همان بخش باید تغییر کند. اگر روش ارسال ایمیل عوض شود هم همان بخش تغییر میکند. این یعنی چند مسئولیت با هم ترکیب شدهاند.
طراحی بهتر این است که:
- یک بخش مسئول محاسبه سفارش باشد.
- یک بخش مسئول ذخیره سفارش باشد.
- یک بخش مسئول ارسال ایمیل باشد.
- یک بخش مسئول ساخت فاکتور باشد.
در این حالت، هر بخش دلیل مشخصی برای تغییر دارد.
2. اصل بازبودن برای توسعه و بستهبودن برای تغییر — Open/Closed Principle
توضیح ساده:
کد باید طوری طراحی شود که برای اضافهکردن قابلیت جدید، مجبور نباشیم کدهای قبلی را مدام تغییر دهیم.
مثال بدون کد:
فرض کن سیستم پرداخت فقط از کارت بانکی پشتیبانی میکند. بعداً میخواهی پرداخت با PayPal و کیف پول دیجیتال را هم اضافه کنی.
طراحی نامناسب این است که هر بار وارد بخش اصلی پرداخت شوی و شرطهای جدید اضافه کنی:
- اگر کارت بود، این کار را انجام بده.
- اگر PayPal بود، آن کار را انجام بده.
- اگر کیف پول بود، کار دیگری انجام بده.
طراحی بهتر این است که سیستم پرداخت یک قرارداد کلی داشته باشد و هر روش پرداخت، پیادهسازی خودش را ارائه دهد. در این صورت برای اضافهکردن روش جدید، یک بخش جدید اضافه میکنی، بدون اینکه منطق روشهای قبلی را تغییر دهی.
3. اصل جانشینی لیسکوف — Liskov Substitution Principle
توضیح ساده:
اگر یک نوع فرعی از نوع اصلی باشد، باید بتوان از آن در هر جایی که نوع اصلی انتظار میرود استفاده کرد، بدون اینکه رفتار سیستم خراب شود.
مثال بدون کد:
فرض کن در نرمافزار، مفهوم «پرنده» را داریم و انتظار داریم هر پرنده بتواند پرواز کند. حالا «پنگوئن» را هم از نوع پرنده در نظر میگیریم؛ اما پنگوئن نمیتواند پرواز کند.
اگر سیستم برای همه پرندهها دستور «پرواز کن» صادر کند، با پنگوئن مشکل پیدا میکند. بنابراین مدل اولیه اشتباه بوده است.
طراحی بهتر این است که:
- مفهوم «پرنده» جدا باشد.
- قابلیت «پروازکردن» فقط برای پرندههایی تعریف شود که واقعاً پرواز میکنند.
- پنگوئن همچنان پرنده باشد، اما مجبور نباشد قابلیت پرواز را داشته باشد.
یعنی نوع فرعی نباید رفتاری داشته باشد که با انتظار نوع اصلی تناقض دارد.
4. اصل جداسازی واسطها — Interface Segregation Principle
توضیح ساده:
هیچ بخشی نباید مجبور شود به قابلیتهایی وابسته باشد که به آنها نیاز ندارد.
مثال بدون کد:
فرض کن یک واسط بزرگ برای دستگاههای اداری داریم که این قابلیتها را شامل میشود:
- چاپ
- اسکن
- فکس
- کپی
حالا یک چاپگر ساده فقط قابلیت چاپ دارد. اگر مجبور باشد از کل این واسط استفاده کند، باید برای اسکن و فکس هم قابلیتهایی داشته باشد که واقعاً از آنها استفاده نمیکند.
طراحی بهتر این است که قابلیتها جدا شوند:
- واسط چاپ
- واسط اسکن
- واسط فکس
- واسط کپی
در این صورت چاپگر ساده فقط به واسط چاپ وابسته میشود.
5. اصل وارونگی وابستگی — Dependency Inversion Principle
توضیح ساده:
بخشهای مهم و اصلی برنامه نباید مستقیماً به جزئیات وابسته باشند؛ هر دو باید به抽抽抽抽 abstractions یا قراردادهای عمومی وابسته باشند.
مثال بدون کد:
فرض کن منطق اصلی فروشگاه مستقیماً به یک نوع پایگاه داده خاص وابسته است. اگر بعداً بخواهی پایگاه داده را عوض کنی، باید منطق اصلی فروشگاه را هم تغییر دهی.
طراحی بهتر این است که منطق فروشگاه فقط با یک قرارداد کلی مثل «ذخیره سفارش» کار کند. سپس هر پایگاه دادهای این قرارداد را پیادهسازی کند.
در این حالت:
- منطق اصلی نمیداند سفارش در MySQL ذخیره میشود یا MongoDB.
- تغییر پایگاه داده، منطق کسبوکار را تحت تأثیر قرار نمیدهد.
- تستکردن منطق اصلی هم سادهتر میشود.
Clean Architecture چیست؟
Clean Architecture یک روش برای سازماندهی کل ساختار نرمافزار است. هدف آن این است که منطق اصلی برنامه از جزئیات بیرونی جدا باشد.

جزئیات بیرونی میتوانند شامل این موارد باشند:
- رابط کاربری
- پایگاه داده
- وبسرویسها
- فریمورکها
- سیستم ارسال ایمیل
- سرویس پرداخت
- فایلها و سیستمعامل
ایده اصلی این است:
قوانین کسبوکار نباید به تکنولوژیهای بیرونی وابسته باشند.
در Clean Architecture معمولاً سیستم به چند لایه تقسیم میشود.
بخشهای اصلی Clean Architecture
1. Entities یا موجودیتها
توضیح ساده:
موجودیتها مهمترین قوانین و مفاهیم کسبوکار را در خود دارند. این بخش باید کمترین وابستگی را به محیط بیرون داشته باشد.
موجودیتها معمولاً مفاهیمی هستند که برای کسبوکار اهمیت دارند، مثل:
- کاربر
- سفارش
- محصول
- حساب بانکی
- بلیت
- رزرو
مثال بدون کد:
در سیستم فروشگاه، «سفارش» یک موجودیت است.
قوانین مربوط به سفارش میتواند شامل این موارد باشد:
- سفارش بدون محصول معتبر نیست.
- مبلغ سفارش نباید منفی باشد.
- سفارش پرداختشده نمیتواند دوباره به حالت پرداختنشده برگردد.
- سفارش لغوشده نباید ارسال شود.
این قوانین نباید به نوع پایگاه داده یا نوع رابط کاربری وابسته باشند.
2. Use Cases یا موارد استفاده
توضیح ساده:
Use Caseها مشخص میکنند کاربر یا سیستم چه کاری میتواند انجام دهد. این بخش منطق انجام عملیات اصلی برنامه را در خود دارد.
مثلاً:
- ثبت سفارش
- پرداخت سفارش
- لغو سفارش
- ثبتنام کاربر
- انتقال وجه
- رزرو بلیت
مثال بدون کد:
Use Case مربوط به «ثبت سفارش» میتواند این مراحل را انجام دهد:
- اطلاعات مشتری را دریافت کند.
- محصولات را بررسی کند.
- موجودی را بررسی کند.
- مبلغ نهایی را محاسبه کند.
- سفارش را ایجاد کند.
- سفارش را ذخیره کند.
- نتیجه را به بخش بیرونی اعلام کند.
این Use Case نباید بداند اطلاعات دقیقاً در MySQL ذخیره میشوند یا از طریق چه فریمورکی دریافت شدهاند.
3. Interface Adapters یا مبدلها
توضیح ساده:
این لایه بین منطق برنامه و دنیای بیرون قرار میگیرد. وظیفهاش تبدیل اطلاعات از یک شکل به شکل دیگر است.
مثلاً تبدیل:
- اطلاعات درخواست HTTP به داده قابلاستفاده برای Use Case
- اطلاعات موجودیت به پاسخ API
- داده پایگاه داده به موجودیتهای برنامه
- اطلاعات فرم به مدل داخلی برنامه
مثال بدون کد:
کاربر از طریق یک فرم، این اطلاعات را ارسال میکند:
- شناسه محصول
- تعداد محصول
- آدرس ارسال
بخش مبدل این اطلاعات را از شکل فرم یا درخواست اینترنتی خارج میکند و به شکلی تبدیل میکند که Use Case ثبت سفارش بتواند آن را بفهمد.
بعد از انجام عملیات، نتیجه داخلی برنامه را هم به شکل مناسب برای پاسخ API یا صفحه وب تبدیل میکند.
4. Frameworks and Drivers یا فریمورکها و ابزارهای بیرونی
توضیح ساده:
این بیرونیترین لایه است و شامل تکنولوژیهایی میشود که برنامه با آنها اجرا میشود.
مثل:
- فریمورک وب
- پایگاه داده
- رابط کاربری
- سیستمعامل
- سرویس ایمیل
- سرویس پرداخت
- کتابخانههای خارجی
مثال بدون کد:
فرض کن برای فروشگاه از این ابزارها استفاده میکنی:
- یک فریمورک برای API
- یک پایگاه داده برای ذخیره سفارشها
- یک سرویس پیامک برای اطلاعرسانی
- یک درگاه پرداخت برای پرداخت مشتری
این ابزارها جزئیات بیرونی هستند. اگر درگاه پرداخت یا فریمورک وب عوض شود، نباید قوانین اصلی ثبت سفارش تغییر کنند.
قانون مهم Clean Architecture: جهت وابستگیها
در Clean Architecture وابستگیها باید به سمت داخل باشند.
یعنی:
- لایه بیرونی میتواند به لایه داخلی وابسته باشد.
- لایه داخلی نباید به لایه بیرونی وابسته باشد.
مثلاً:
- بخش API میتواند Use Case ثبت سفارش را صدا بزند.
- Use Case میتواند با یک قرارداد ذخیرهسازی کار کند.
- اما Use Case نباید مستقیماً به یک فریمورک API یا یک پایگاه داده خاص وابسته باشد.
مثال ساده:
منطق ثبت سفارش باید بتواند بدون اجرای وبسایت، بدون اتصال به پایگاه داده واقعی و بدون استفاده از فریمورک خاص هم تست شود.
مثال کامل از Clean Architecture در فروشگاه
فرض کن کاربر میخواهد سفارشی ثبت کند.
جریان کلی
- کاربر اطلاعات سفارش را در سایت وارد میکند.
- بخش وب، اطلاعات را دریافت میکند.
- Adapter اطلاعات را به قالب داخلی تبدیل میکند.
- Use Case ثبت سفارش اجرا میشود.
- قوانین سفارش و محاسبه مبلغ بررسی میشوند.
- Use Case از یک قرارداد برای ذخیره سفارش استفاده میکند.
- یک پیادهسازی بیرونی، سفارش را در پایگاه داده ذخیره میکند.
- نتیجه به Adapter برمیگردد.
- Adapter نتیجه را به پاسخ مناسب برای سایت تبدیل میکند.
در این ساختار، Use Case نمیداند:
- کاربر از سایت استفاده کرده یا اپلیکیشن موبایل.
- پایگاه داده MySQL است یا MongoDB.
- درخواست از طریق REST آمده یا GraphQL.
- پیامک از چه سرویسی ارسال شده است.
این جداسازی باعث میشود تغییر تکنولوژی، کمترین آسیب را به منطق اصلی وارد کند.
تفاوت SOLID و Clean Architecture
SOLID مجموعهای از اصول طراحی است که بیشتر درباره نحوه طراحی کلاسها، ماژولها و وابستگیها صحبت میکند.
Clean Architecture یک الگوی معماری بزرگتر است که کل برنامه را به لایههای مشخص تقسیم میکند.
رابطه آنها اینگونه است:
- SOLID به طراحی درست اجزای داخل سیستم کمک میکند.
- Clean Architecture مشخص میکند این اجزا چگونه در لایههای مختلف قرار بگیرند.
- اصل Dependency Inversion در SOLID، یکی از پایههای مهم Clean Architecture است.
بهصورت خلاصه:
SOLID میگوید هر جزء را چگونه طراحی کنیم؛ Clean Architecture میگوید اجزای کل سیستم را چگونه سازماندهی کنیم.
منابع بیشتر
- ← Previous
Solving Some TypeScript Challenges