مروری بر کتاب Clean Architecture

کتاب CLEAN ARCHITECTUREبخش پنجمفصل پانزدهم------------------------------------------------------------------------------------------------------------------- ?What Is Architectureنکته اصلی نویسنده این است که معماری نرم افزار نقش مهمی در توسعه(development)، استقرار(deployment)، عملیات (operation) و نگهداری سیستم های نرم افزاری(maintenance of software systems) ایفا می کند. برای بیان این نکته، نویسنده ابتدا این افسانه را که معماران نرم افزار از کد عقب نشینی می کنند تا روی مسائل سطح بالاتر تمرکز کنند را رد میکند.در واقع، نویسنده استدلال می‌کند که معماران نرم‌افزار بهترین برنامه‌نویسان هستند و به انجام وظایف برنامه‌نویسی خود ادامه می‌ دهند، همچنین بقیه اعضای تیم را به سمت طراحی هدایت می‌کنند که بهره‌وری را به حداکثر می‌رساند. نویسنده سپس به بحث در مورد نقش خاص معماری نرم افزار در هر یک از چهار مرحله چرخه عمر توسعه نرم افزار می پردازد:توسعه(development): یک معماری خوب نرم افزار، توسعه سیستم را برای تیم هایی که آن را توسعه می دهند آسان می کند. این امر به ویژه برای سیستم های بزرگی که توسط چندین تیم در حال توسعه هستند بسیار مهم است.استقرار(deployment): هدف یک معمار نرم افزار باید ساختن سیستمی باشد که با یک اقدام به راحتی مستقر شود. متأسفانه، استراتژی استقرار به ندرت در چرخه عمر توسعه نرم افزار در نظر گرفته می شود، که می تواند منجر به معماری هایی شود که به سختی استقرار می یابند.عملیات(operation): تأثیر معماری بر عملکرد سیستم نسبت به تأثیر معماری بر توسعه، استقرار و نگهداری کمتر دیده میشود. با این حال، یک معماری نرم‌افزار خوب نیازهای عملیاتی سیستم را به توسعه‌دهندگان منتقل می‌کند که می‌تواند کارکرد سیستم را آسان‌تر کند.به روز رسانی و نگهداری(maintenance): سیستم های نرم افزاری که به خوبی طراحی شده اند نگهداری و به روز رسانی آسان تری خواهند داشت . این به این دلیل است که معماری ساختار واضحی را برای سیستم فراهم می‌کند و درک نحوه تعامل اجزای مختلف با یکدیگر را آسان‌تر می‌کند. به طور کلی، نویسنده یک مورد قانع کننده برای اهمیت معماری نرم افزار بیان می کند. با در نظر گرفتن نیازهای معماری یک سیستم، توسعه دهندگان می توانند سیستم هایی ایجاد کنند که توسعه، استقرار، بهره برداری و نگهداری شان آسان تر باشد.فصل شانزدهم------------------------------------------------------------------------------------------------------------------------------Independenceنویسنده در مورد اهمیت معماری نرم افزار در پشتیبانی از موارد استفاده (Use cases) و عملیات(operation) سیستم ، و همچنین توسعه(development) و استقرار(deployment) بحث می کند.موارد استفاده: معماری سیستم باید از هدف سیستم، همانطور که توسط موارد استفاده تعریف شده است، پشتیبانی کند.این بدان معناست که معماری باید اجرای رفتار(behavior) مطلوب سیستم را واضح و آسان کند.عملیات: معماری سیستم باید نیازهای عملیاتی سیستم مانند توان عملیاتی(throughput)، زمان پاسخگویی(response time) و مقیاس پذیری(scalability) را نیز پشتیبانی کند.توسعه: معماری سیستم باید محیط توسعه را نیز با تقسیم کردن سیستم به اجزای جدا شده و مستقل قابل توسعه(developable components)، پشتیبانی کند. این کار باعث میشود کار توسعه دهندگان آسان تر شود تا روی قسمت های مختلف سیستم بدون تداخل با یکدیگر کار کنند.استقرار: معماری سیستم نیز باید استقرار سیستم را آسان کند. این ممکن است شامل اجتناب از اسکریپت های پیکربندی پیچیده و ترفندهای فایل ویژگی باشد.نویسنده استدلال می کند که یک معماری نرم افزار خوب گزینه هایی را برای همه این نگرانی ها باز می گذارد تا با آن گزینه ها نگرانی ها را بتوان حل کرد. این به این دلیل است که اغلب دشوار است که همه الزامات یک سیستم مانند محدودیت های عملیاتی(operational constraint)، ساختار تیم و الزامات استقرار(deployment requirements) را از قبل بدانید.با باز گذاشتن گزینه‌ها، یک معماری خوب تطبیق سیستم را با نیازهای متغیر در طول زمان آسان‌تر می‌کند.نویسنده همچنین دو اصل خاص را که می تواند برای طراحی یک معماری خوب مورد استفاده قرار گیرد، مورد بحث قرار می دهد:اصل مسئولیت واحد: یک کلاس باید تنها یک دلیل برای تغییر داشته باشد.اصل بسته شدن مشترک: کلاس هایی که با هم تغییر می کنند باید با هم گروه بندی شوند.از این اصول می توان برای پارتیشن بندی سیستم به اجزای ایزوله شده که به راحتی قابل تغییر و نگهداری هستند استفاده کرد. فصل هفدهم --------------------------------------------------------------------------------------------------------- Boundaries: Drawing Lines نویسنده استدلال می‌کند که معماران نرم‌افزار باید از تصمیم‌گیری زودهنگام اجتناب کنند، به‌ویژه زمانی که صحبت از چارچوب‌ها(frameworks)، پایگاه‌های داده(databases)، سرورهای وب(web servers)، کتابخانه‌های ابزار(utility libraries)، تزریق وابستگی(dependency injection) و مواردی از این دست باشد. این تصمیمات باید فرعی و قابل تعویق باشند تا در آخرین لحظه ممکن و بدون تأثیر قابل توجه اتخاذ شوند.نویسنده داستان شرکت P را بیان می کند که تصمیم زودهنگام اتخاذ یک معماری سه لایه را برای برنامه وب(web application) خود گرفت. این تصمیم منجر به افزایش قابل توجهی صرف انرژی برای توسعه دادن شد، زیرا برنامه نویسان مجبور بودند یک سیستم پیچیده از ارتباطات بین پردازنده(interprocessor) و بین لایه ای(inter-tier communications) را پیاده سازی کنند.نویسنده همچنین داستان یک معمار نرم افزار را روایت می کند که یک مدل دامنه عظیم از تمام "اشیاء" مختلف در تجارت ایجاد کرد و مجموعه ای از خدمات را برای مدیریت این اشیاء دامنه، طراحی کرد. این امر اجرای ویژگی های ساده را برای برنامه نویسان بسیار دشوار می کرد، زیرا آنها مجبور بودند با چندین سرویس تعامل داشته باشند و داده های زیادی را منتقل کنند. نویسنده با این استدلال نتیجه می گیرد که معماران نرم افزار باید بر ترسیم مرزهایی تمرکز کنند که جفت شدن(coupling) را به حداقل می رساند و حداکثر انعطاف را فراهم می کند. این کار تطبیق سیستم را با نیازهای در حال تغییر در طول زمان آسان تر می کند. به طور کلی، برای معماران نرم افزار مهم است که از تصمیم گیری های زودهنگام خودداری کنند.معماران با تمرکز بر ترسیم مرزهایی که اتصال را به حداقل می‌رسانند و حداکثر انعطاف‌پذیری را فراهم می‌کنند، می‌توانند سیستم‌هایی ایجاد کنند که راحت‌تر با نیازهای در حال تغییر در طول زمان سازگار شوند و احتمال شکست خوردن آنها کمتر شود.فصل هجدهم ---------------------------------------------------------------------------------------------------------------------Boundary Anatomyانواع مختلف مرزهای معماری در سیستم های نرم افزاری:یکپارچه(Monolith):ساده ترین نوع مرزهای معماری، بدون نمایش فیزیکی دقیق میباشد. توابع و داده ها در یک پردازنده و فضای آدرس جدا می شوند. ارتباط بین مؤلفه ها سریع و ارزان است، اما مؤلفه ها معمولاً به عنوان کد منبع ارائه می شوند و باید قبل از استقرار کامپایل(compiled) و به صورت ایستا پیوند (statically linked) شوند.جزء استقرار(Deployment component): یک واحد قابل استقرار که معمولاً به صورت باینری ارائه می شود. ارتباط بین مؤلفه‌های استقرار نیز سریع و ارزان است، اما مؤلفه‌ها باید برای پلتفرم خاصی که در آن مستقر می‌شوند، کامپایل شوند.فرآیند محلی(Local process): فرآیندی که در فضای آدرس خود اجرا می شود و از طریق سوکت ها یا سایر امکانات ارتباطی سیستم عامل با سایر فرآیندها ارتباط برقرار می کند. ارتباط بین فرآیندهای محلی نسبتاً گران است، بنابراین گفتگو باید با دقت محدود شود.سرویس(Service): فرآیندی که به موقعیت فیزیکی آن بستگی ندارد و با سرویس های دیگر از طریق شبکه ارتباط برقرار می کند. ارتباط بین سرویس ها بسیار کند است، بنابراین باید مراقب باشید که از چت کردن خودداری کنید.نویسنده همچنین خاطرنشان می کند که اکثر سیستم ها از بیش از یک استراتژی مرزی استفاده می کنند. به عنوان مثال، سیستمی که از مرزهای سرویس استفاده می کند ممکن است دارای برخی از مرزهای فرآیند محلی نیز باشد.برخی از ملاحظات اضافی برای هر نوع مرز معماری:مونولیت ها: توسعه، آزمایش و استقرار مونولیت ها به خصوص برای سیستم های بزرگ و پیچیده می تواند دشوار باشد. با این حال، آنها می توانند عملکرد و مقیاس پذیری خوبی ارائه دهند و درک و نگهداری از آنها نسبتاً آسان است.اجزای استقرار: توسعه، آزمایش و استقرار مؤلفه‌های استقرار می‌تواند آسان‌تر از یکپارچه‌ها باشد، و آنها می‌توانند مقیاس سیستم را با استقرار نمونه‌های اضافی مؤلفه‌ها آسان‌تر کنند. با این حال، ارتباط بین اجزای استقرار می‌تواند پیچیده‌تر و گران‌تر از ارتباطات درون یکپارچه باشد.فرآیندهای محلی: فرآیندهای محلی می توانند تعادل خوبی بین عملکرد، مقیاس پذیری و سهولت توسعه ایجاد کنند. با این حال، ارتباط بین فرآیندهای محلی می‌تواند پیچیده‌تر و گران‌تر از ارتباطات درون یکپارچه یا بین اجزای استقرار باشد.سرویس ها : سرویس ها برای سیستم هایی که نیاز به مقیاس پذیری بالایی دارند و در چندین سرور توزیع می شوند، انتخاب خوبی هستند. با این حال، ارتباط بین سرویس‌ها می‌تواند کند و پرهزینه باشد، بنابراین طراحی سیستم به دقت برای به حداقل رساندن چت بسیار مهم است.بهترین نوع مرز معماری برای یک سیستم خاص به نیازهای خاص سیستم مانند عملکرد، مقیاس پذیری، سهولت توسعه و نگهداری بستگی دارد.فصل نوزدهم ----------------------------------------------------------------------------------------------------------------------------Policy and Levelاین فصل در مورد اهمیت جداسازی سیاست ها در معماری نرم افزار است.سیاست ها به عنوان قوانینی تعریف می شوند که بر رفتار یک سیستم حاکم هستند. آنها می توانند به هر چیزی از امنیت(security) گرفته تا عملکرد(performance) و تجربه کاربری(user experience) مرتبط باشند.نویسنده استدلال می کند که سیاست ها باید بر اساس روشی که تغییر می کنند با هم گروه بندی شوند. سیاست هایی که به دلایل یکسان و در زمان های مشابه تغییر می کنند باید در یک جزء گروه بندی شوند. سیاست هایی که به دلایل مختلف یا در زمان های مختلف تغییر می کنند باید به اجزای مختلف تفکیک شوند. نویسنده همچنین استدلال می کند که جهت وابستگی های کد منبع(source code) باید بر اساس سطح اجزایی باشد که آنها به هم متصل می شوند.هرچه یک خط مشی(policy) از هر دو ورودی و خروجی سیستم دورتر باشد، سطح آن بالاتر است.نویسنده نمونه ای از یک برنامه رمزگذاری ساده را ارائه می دهد. بالاترین سطح در این سیستم translate component است، زیرا مولفه ای است که از ورودی ها و خروجی ها دورتر است.نویسنده سپس اهمیت جدا کردن خط مشی رمزگذاری سطح بالا از سیاست های ورودی/خروجی سطح پایین را مورد بحث قرار می دهد.این را می توان با گروه بندی خط مشی رمزگذاری در یک جزء مجزا از سیاست های ورودی/خروجی انجام داد.نویسنده با بیان اینکه جدا نگه داشتن خط‌مشی‌ها، با همه وابستگی‌های کد منبع در جهت سیاست‌های سطح بالاتر، تأثیر تغییر را کاهش می‌دهد، نتیجه‌گیری می‌کند.نکات کلیدی این فصل: سیاست ها باید بر اساس روشی که تغییر می کنند با هم گروه بندی شوند. جهت وابستگی های کد منبع باید بر اساس سطح اجزایی باشد که آنها به هم متصل می شوند. اجزای سطح بالاتر در یک سیستم باید نسبت به اجزای سطح پایین کمتر مستعد تغییر باشند. جدا نگه داشتن سیاست ها تأثیر تغییر را کاهش می دهد.توصیه نویسنده مهم است زیرا به ایجاد معماری های نرم افزاری که قوی تر، سازگارتر و قابل نگهداری هستند کمک می کند.فصل بیستم------------------------------------------------------------------------------------------------------------------------------- Business Rulesاین فصل دو نوع مختلف از قوانین تجاری را شرح می دهد:قوانین حیاتی کسب و کار(Critical Business Rules):قوانینی هستند که باعث ایجاد ارزش یا صرفه جویی در هزینه کسب و کار می شوند، حتی اگر سیستمی برای خودکارسازی آنها وجود نداشته باشد. آنها معمولاً به طور جدایی ناپذیری به داده های تجاری حیاتی وابسته هستند و می توان آنها را به عنوان موجودیت در نظر گرفت.موارد استفاده(Use Cases):اینها قوانینی هستند که نحوه عملکرد یک سیستم خودکار را تعریف و محدود می کنند. آنها مختص یک برنامه واحد هستند و حرکات موجودات را کنترل می کنند.نویسنده استدلال می‌کند که قوانین تجاری باید جدا از سایر نگرانی‌های موجود در سیستم، مانند رابط کاربری(user interface) یا پایگاه داده(database)، نگهداری شوند.این را می توان با گروه بندی قوانین تجاری به موجودیت ها و موارد استفاده، و با استفاده از وارونگی وابستگی برای اطمینان از اینکه وابستگی ها در جهت مفاهیم سطح بالاتر اشاره می کنند، به دست آورد.توصیه نویسنده مهم است زیرا به ایجاد سیستم های نرم افزاری که قوی تر، سازگارتر و قابل نگهداری هستند کمک می کند.فصل بیست و یکم---------------------------------------------------------------------------------------------------------Screaming Architectureاین فصل در مورد اهمیت طراحی معماری نرم افزار در اطراف موارد استفاده(use cases)، به جای چارچوب ها(frameworks) است.نویسنده استدلال می‌کند که چارچوب‌ها ابزار هستند، نه معماری، و باید برای پشتیبانی از موارد استفاده(use cases) استفاده شوند، نه برعکس. نویسنده همچنین استدلال می‌کند که معماری‌ها باید قابل آزمایش باشند، و همه موارد استفاده باید بدون هیچ‌کدام از چارچوب‌ها قابل آزمایش واحد باشند. این را می توان با طراحی موجودیت ها و استفاده از موارد مستقل از چارچوب ها و سایر پیچیدگی ها به دست آورد.نویسنده با بیان اینکه معماری شما باید در مورد سیستم به خوانندگان بگوید، نه در مورد چارچوب هایی که در سیستم خود استفاده کرده اید، نتیجه گیری می کند. برنامه نویسان جدید باید بتوانند تمام موارد استفاده سیستم را یاد بگیرند، اما هنوز نمی دانند سیستم چگونه ارائه می شود.فصل بیست و دوم---------------------------------------------------------------------------------------------------------The Clean Architectureاین فصل معماری شش ضلعی(hexagonal architecture) (همچنین به عنوان پورت ها و آداپتورها شناخته می شود) را توصیف می کند که یک الگوی طراحی نرم افزاری است که می تواند برای ایجاد سیستم هایی مستقل از چارچوب ها، قابل آزمایش و تغییر آسان استفاده شود.معماری شش ضلعی دارای سه لایه اصلی است:موجودیت ها (Entities): موجودیت ها قوانین حیاتی کسب و کار را در سراسر سازمان دربر می گیرند. آنها عمومی ترین و سطح بالای قوانین در سیستم هستند و نباید تحت تأثیر تغییرات خارجی مانند پایگاه داده، UI یا چارچوب ها قرار گیرند.موارد استفاده(Use Cases): لایه Use Case ها شامل قوانین تجاری خاص برنامه است. این Use Case ها جریان داده‌ها را با موجودیت‌ها هماهنگ می‌کنند و آن موجودیت ها را هدایت می‌کنند تا از قوانین تجاری حیاتی خود برای دستیابی به اهداف Use Case ها استفاده کنند.آداپتورهای رابط(Interface Adapters): لایه آداپتورهای رابط مجموعه‌ای از آداپتورها است که داده‌ها را از فرمت مناسب برای Use Case ها و موجودیت‌ها به فرمت مناسب برای برخی از آژانس‌های خارجی مانند پایگاه داده یا وب تبدیل می‌کند.معماری شش ضلعی توسط قانون وابستگی اعمال می شود، که بیان می کند که وابستگی های کد منبع باید فقط به سمت داخل، به سمت سیاست های سطح بالاتر اشاره کنند. این بدان معناست که لایه‌های موجودیت‌ها و Use Case ها نباید هیچ گونه وابستگی به لایه آداپتورهای رابط یا لایه چارچوب‌ها و درایورها داشته باشند.استقلال فریم ورک ها: معماری شش ضلعی به شما این امکان را می دهد که چارچوب ها و درایورهای استفاده شده توسط سیستم خود را بدون تأثیر بر منطق تجاری تغییر دهید. این سیستم شما را انعطاف پذیرتر و سازگارتر می کند.آزمایش پذیری: معماری شش ضلعی آزمایش سیستم شما را آسان تر می کند زیرا منطق تجاری از عوامل خارجی جدا شده است. شما می توانید لایه های Use Case ها و موجودیت ها را unit test کنید بدون اینکه نگران پایگاه داده، UI یا چارچوب ها باشید.قابلیت تغییر: معماری شش ضلعی تغییر سیستم شما را آسان تر می کند زیرا منطق کسب و کار در لایه ها سازماندهی شده است. اگر نیاز به تغییر یک قانون تجاری دارید، فقط باید لایه مناسب را تغییر دهید. این باعث می شود سیستم شما قابل نگهداری و تکامل باشد.به طور کلی، معماری شش ضلعی انتخاب خوبی برای توسعه سیستم‌های نرم‌افزاری است که مستقل از چارچوب‌ها، قابل آزمایش و تغییر آسان هستند.فصل بیست و سوم------------------------------------------------------------------------------------------Presenters and Humble Objectsاین فصل الگوی شیء فروتن(Humble Object pattern) را توصیف می‌کند، که یک الگوی طراحی نرم‌افزاری است که می‌تواند برای جدا کردن رفتارهای آزمایش کردن سخت(hard-to-test behaviors) یک سیستم از رفتارهای آزمایش کردن آسان(easy-to-test behaviors) استفاده شود.این الگو می تواند برای بهبود تست پذیری و قابلیت نگهداری یک سیستم مفید باشد.یکی از نمونه های الگوی Humble Object، الگوی Presenter است.در الگوی Presenter، رابط کاربری گرافیکی(GUI) به دو جزء تقسیم می شود:View و Presenterویو (View)یک شی فروتن است که مسئول نمایش داده ها به کاربر است، در حالی که Presenter مسئول فرمت بندی داده ها و ارسال آن به View است.این تفکیک نگرانی‌ها، آزمایش Presenter را آسان‌تر می‌کند، زیرا نیازی به وابستگی به رابط کاربری گرافیکی(GUI) ندارد.نمونه دیگری از الگوی Humble Object دروازه پایگاه داده (database gateway) است. دروازه پایگاه داده یک رابط چند شکلی است که شامل روش هایی برای هر عملیات ایجاد، خواندن، به روز رسانی یا حذف است که می تواند در پایگاه داده انجام شود. این رابط توسط یک کلاس بتن(concrete class) پیاده سازی می شود که از درایور پایگاه داده خاص برای دسترسی به داده ها استفاده می کند. دروازه پایگاه داده را می توان با یک خرد(stub) یا ساختگی(mock) در آزمایش ها جایگزین کرد، که آزمایش Use Case ها که با پایگاه داده در تعامل هستند را آسان تر می کند.الگوی Humble Object همچنین می تواند برای ایجاد مرز بین سرویس ها استفاده شود. به عنوان مثال، اگر برنامه شما نیاز به ارتباط با سرویس دیگری دارد، می توانید از الگوی Humble Object برای ایجاد مرز سرویس بین دو برنامه استفاده کنید. این مرز می تواند توسط کلاسی پیاده سازی شود که یک رابط ساده را در معرض سرویس دیگر قرار می دهد. سپس کلاس می تواند از یک پیاده سازی مشخص از سرویس برای دسترسی به داده ها استفاده کند. این تفکیک نگرانی ها، آزمایش این دو برنامه را به طور مستقل آسان تر می کند.به طور کلی، الگوی Humble Object ابزار مفیدی برای بهبود تست پذیری و قابلیت نگهداری سیستم های نرم افزاری است. با جدا کردن رفتارهای آزمون سخت از رفتارهای آزمون آسان، الگوی Object Humble می‌تواند نوشتن تست‌ها و تغییر سیستم را بدون شکستن آن آسان‌تر کند.فصل بیست و چهارم--------------------------------------------------------------------------------------------------------------Partial Boundariesاین فصل مرزهای جزئی را توصیف می کند، که راهی برای پیاده سازی مرزهای معماری بدون هزینه تمام یک مرز کامل است. مرزهای جزئی می تواند برای نگه داری مکانی برای یک مرز آینده(future boundary) یا برای اجرای یک مرز (implementing a boundary) در شرایطی که هزینه یک مرز کامل بسیار زیاد است مفید باشد.سه نوع اصلی از مرزهای جزئی وجود دارد:از آخرین مرحله بگذرید(Skip the last step): این نوع مرز جزئی شامل ایجاد تمام کارهای لازم برای یک مرز کامل است، اما سپس اجزاء(components) را با هم در یک جزء(component) نگه می دارد. این گران ترین نوع مرز جزئی است، اما همچنین انعطاف پذیرترین نوع آن است.مرزهای یک بعدی(One-dimensional boundaries): این نوع مرز جزئی از رابط مرزی متقابل برای حفظ ایزوله در یک جهت استفاده می کند. این هزینه کمتر از مرز رد شدن از آخرین مرحله است، اما انعطاف پذیری کمتری نیز دارد.نماها(Facades): این نوع مرز جزئی از یک کلاس نما(facade class) برای تعیین مرز استفاده می کند. این کم هزینه ترین نوع مرز جزئی است، اما در عین حال کمترین انعطاف را نیز دارد.همچنین به اهمیت مرزهای معماری و نقش معمار در تصمیم گیری در مورد مکان و زمان اجرای آنها باید پرداخت.هزینه کاهش یافته: مرزهای جزئی نسبت به مرزهای کامل هزینه کمتری دارند.انعطاف پذیری: مرزهای جزئی می توانند انعطاف پذیرتر از مرزهای کامل باشند، زیرا در آینده به راحتی می توان آنها را به یک مرز کامل گسترش داد.نگه داشتن یک مکان برای یک مرز آینده: از مرزهای جزئی می توان برای نگه داشتن مکانی برای یک مرز آینده استفاده کرد، حتی اگر مرز فورا مورد نیاز نباشد.به طور کلی، مرزهای جزئی ابزار مفیدی برای معماران است که در جعبه ابزار خود داشته باشند. آنها می توانند برای اجرای مرزها در شرایطی استفاده شوند(situations) که هزینه یک مرز کامل بسیار زیاد است، یا جایی که معمار می خواهد مکانی را برای یک مرز آینده نگه دارد.فصل بیست و پنجم ------------------------------------------------------------------------------------------------------- Layers and Boundariesدر این فصل نوبسنده در زمینه بازی مبتنی بر متن اشاره می کند بر Hunt the Wumpusمعماری از سه جزء اصلی تشکیل شده است: UI، قوانین بازی و ذخیره سازی داده. مؤلفه UI ورودی کاربر را کنترل می کند و از طریق یک API مستقل از زبان با قوانین بازی ارتباط برقرار می کند. مولفه قوانین بازی وضعیت بازی را ذخیره می کند و از طریق API با مؤلفه UI تعامل دارد. مولفه ذخیره سازی داده مسئول حفظ داده های بازی است و از طریق API دیگری از قوانین بازی تشکیل می شود.با جدا کردن رابط کاربری از قوانین بازی، می توانید به راحتی از زبان های مختلف در بازارهای مختلف پشتیبانی کنید. UI زبان مستقل API را به زبان انسانی مناسب ترجمه می‌کند و امکان انعطاف‌پذیری زبان را بدون تأثیرگذاری بر قوانین بازی فراهم می‌کند.طبق اصول کتاب Clean Architecture، هر تعداد از مؤلفه‌های UI می‌توانند از همان قوانین بازی دوباره استفاده کنند. جزء قواعد بازی(rules component) مستقل از UI باقی می ماند و نیازی به اطلاع از پیاده سازی های UI خاص یا زبان های مورد استفاده ندارد. برای جداسازی بیشتر نگرانی‌ها(concerns)، یک API بین قوانین بازی و مؤلفه ذخیره‌سازی داده ایجاد می‌شود. قوانین بازی از طریق این API با ذخیره سازی داده ارتباط برقرار می کند و قوانین بازی را از جزئیات اجرای ذخیره سازی داده جدا می کند. وابستگی‌های بین مؤلفه‌ها به‌درستی مطابق قانون وابستگی هدایت می‌شوند.نمودار های 25 در کتاب ، جریان اطلاعات بین اجزا را نشان می دهد. زبان API توسط اجزای مختلف زبان، مانند انگلیسی و اسپانیایی پیاده سازی می شود.?مؤلفه قوانین بازی با مؤلفه Language از طریق API تعریف شده توسط قوانین بازی و پیاده سازی شده توسط مؤلفه Language ارتباط برقرار می کند.?مؤلفه Language به نوبه خود با استفاده از API دیگری که توسط مؤلفه Language تعریف شده و توسط مؤلفه TextDelivery پیاده سازی شده است، با مؤلفه TextDelivery ارتباط برقرار می کند.?این نمودار همچنین مالکیت API ها را توسط کاربر به جای پیاده‌کننده نشان می‌دهد. این بدان معناست که تعاریف API متعلق به مؤلفه‌های سطح بالاتر، مانند قوانین بازی، و پیاده‌سازی مؤلفه‌های سطح پایین‌تر، مانند مؤلفه‌های خاص زبان است.با توجه به جریان داده، از نمودار دو جریان پیشنهاد می کند:یکی برای ارتباط کاربر و دیگری برای تداوم داده.این جریان ها در مؤلفه قوانین بازی که به عنوان پردازنده مرکزی داده ها عمل می کند، ملاقات می کنند. جریان ارتباط کاربر شامل ترجمه ورودی کاربر از طریق مؤلفه Language و ارائه زبان مناسب به کاربر از طریق مؤلفه TextDelivery است. جریان پایداری داده شامل ارسال داده ها به مؤلفه DataStorage برای ذخیره سازی است.توجه به این نکته مهم است که اصول معماری پاک یک دستورالعمل کلی برای طراحی سیستم ارائه می دهد و می تواند با زمینه ها و الزامات خاص سازگار شود.فصل بیست و ششم---------------------------------------------------------------------------------------------------------The Main Componentاین فصل جزء اصلی(Main component) بازی Hunt the Wumpus را توصیف می کند. کامپوننت Main پایین ترین خط مشی(lowest-level policy) بازی است و وظیفه ایجاد بازی و نقشه و تفسیر دستورات ورودی بازیکن را بر عهده دارد. جزء اصلی از HtwFactory برای ایجاد بازی استفاده می کند. HtwFactory یک کلاس کارخانه ای است که وظیفه ایجاد تمام اجزای مختلف بازی مانند بازیکن، Wumpus و خفاش ها را بر عهده دارد. هنگامی که بازی ساخته شد، کامپوننت اصلی وارد یک حلقه می شود که در آن دستور ورودی بازیکن را می خواند و سپس روش مناسب را برای اجرای دستور روی بازی فراخوانی می کند. کامپوننت اصلی همچنین نقاط ضربه بازیکن و وضعیت نقشه بازی را پیگیری می کند.مسئولیت های مؤلفه اصلی: بازی و نقشه را بسازد. دستورات ورودی بازیکن را تفسیر کند. برای اجرای دستورات بازیکن، متدهای مناسب روی بازی را فراخوانی کند. امتیازات ضربه بازیکن و وضعیت نقشه بازی را پیگیری کند.کامپوننت Main جزء بسیار مهم بازی Hunt the Wumpus است و بسیاری از عملکردهای اصلی بازی را بر عهده دارد.فصل بیست و هفتم----------------------------------------------------------------------------------------------------Services: Great and Smallاین فصل در مورد اشتباه توسعه و استقرار مستقل بحث می کند.نویسنده استدلال می کند که خدمات لزوماً از نظر معماری مهم نیستند و می توانند به شدت با یکدیگر پیوند شوند. نویسنده همچنین استدلال می‌کند که سرویس‌ها همیشه نمی‌توانند به طور مستقل توسعه، مستقر و عملیاتی شوند.نویسنده از مثال یک سیستم جمع‌آوری تاکسی برای نشان دادن اشتباه توسعه و استقرار مستقل استفاده می‌کند. سیستم جمع‌آوری تاکسی از چندین سرویس از جمله سرویس TaxiUI، سرویس TaxiFinder، سرویس TaxiSelector و سرویس TaxiDispatcher تشکیل شده است. این خدمات با داده ها و رفتار با یکدیگر جفت می شوند. به عنوان مثال، سرویس TaxiSelector برای انتخاب تاکسی مناسب برای کاربر، باید در مورد توانایی های تامین کنندگان مختلف تاکسی اطلاعات داشته باشد. نویسنده استدلال می کند که توسعه، استقرار و بهره برداری مستقل از این خدمات دشوار خواهد بود. به عنوان مثال، اگر سرویس TaxiFinder رابط خود را تغییر دهد، سرویس TaxiSelector باید متناسب با آن به روز شود. علاوه بر این، اگر سرویس TaxiSelector معیارهای خود را برای انتخاب تاکسی تغییر دهد، سرویس TaxiDispatcher باید بر این اساس به روز شود. نویسنده با بیان این که خدمات تنها گزینه برای ساخت سیستم های مقیاس پذیر نیستند، نتیجه گیری می کند. او استدلال می‌کند که سیستم‌های یکپارچه و مبتنی بر مؤلفه نیز می‌توانند مقیاس‌پذیر باشند، و توسعه و نگهداری آن‌ها ممکن است آسان‌تر از سیستم‌های مبتنی بر خدمات باشد.نکات اصلی نویسنده: خدمات لزوماً از نظر معماری مهم نیستند. خدمات را می توان به شدت با یکدیگر پیوند داد. سرویس ها همیشه نمی توانند به طور مستقل توسعه، مستقر و بهره برداری شوند. سیستم‌های یکپارچه و مبتنی بر مولفه نیز می‌توانند مقیاس‌پذیر باشند، و ممکن است توسعه و نگهداری آن‌ها آسان‌تر از سیستم‌های مبتنی بر خدمات باشد.استدلال های نویسنده از این جهت مهم است که تفکر مرسوم در مورد خدمات را به چالش می کشد. بسیاری از مردم بر این باورند که خدمات بهترین راه برای ساخت سیستم های مقیاس پذیر است. با این حال، نویسنده استدلال می کند که خدمات همیشه بهترین گزینه نیستند، و توسعه و نگهداری آنها می تواند دشوار باشد.فصل بیست و هشتم--------------------------------------------------------------------------------------------------------------The Test Boundaryاین فصل اهمیت طراحی سیستم ها برای آزمایش پذیری را مورد بحث قرار می دهد. نویسنده استدلال می کند که آزمایش ها باید در طراحی سیستم ادغام شوند و آنها باید از کد تولید(production code) جدا شوند. نویسنده توصیه می کند یک API آزمایشی ایجاد کنید که می تواند توسط آزمایش ها برای تأیید قوانین تجاری سیستم استفاده شود. این API باید دارای ابرقدرت‌هایی(superpowers) باشد که به آزمایش‌ها اجازه می‌دهد از محدودیت‌های امنیتی اجتناب کنند، منابع گران قیمت را دور بزنند و سیستم را به حالت‌های قابل آزمایش خاصی وادار کنند.هدف از تست API جدا کردن تست ها از برنامه است.این جداسازی چندین مزیت دارد: با تغییر کد تولید، تست‌ها قوی‌تر می‌شوند و احتمال شکستگی کمتری دارند. کد تولید انعطاف پذیرتر می شود و تغییر آن آسان تر می شود .آزمایش‌ها را می‌توان به روش‌هایی تغییر داد و تکامل داد که بر کد تولید تأثیری نداشته باشد.نویسنده همچنین اهمیت جدا نگه داشتن API آزمایشی از کد تولید را مورد بحث قرار می دهد. این به این دلیل است که API آزمایشی دارای قدرت های فوق العاده ای است که در صورت استقرار در سیستم های تولید می تواند خطرناک باشد.نویسنده با بیان این که طراحی سیستم هایی برای آزمایش پذیری به چند دلیل مهم است، نتیجه گیری می کند: این سیستم ها را قوی تر می کند و احتمال شکست را کمتر می کند. این سیستم ها را منعطف تر می کند و تغییر را آسان تر می کند. نگهداری و درک سیستم ها را آسان تر می کند.به طور کلی، این فصل بینش های ارزشمندی در مورد اهمیت طراحی سیستم ها برای آزمایش پذیری ارائه می دهد.فصل بیست و نهم-----------------------------------------------------------------------------------------------Clean Embedded Architectureدر این فصل، نویسنده استدلال می‌کند که تعریف سنتی سیستم‌افزار(firmware) اشتباه است، و سیستم باید به‌عنوان کدی تعریف شود که تغییر آن با تکامل سخت‌افزار(hardware) دشوار است. نویسنده همچنین استدلال می‌کند که ما باید سیستم‌افزار(firmware) کمتر و نرم‌افزار(software) بیشتری بنویسیم، و باید کد تعبیه‌شده خود را به گونه‌ای ساختار دهیم که تکامل آن با تغییرات سخت‌افزاری آسان شود.نویسنده دلایل زیادی برای تبدیل شدن نرم افزارهای جاسازی شده بالقوه به سیستم عامل ارائه می دهد، از جمله:تاکید بر روی کارکرد کد:نویسنده استدلال می‌کند که بیشترین تاکید در توسعه سیستم‌های جاسازی شده بر روی کارکرد کد است .وسواس تست App-titude : نویسنده استدلال می کند که بسیاری از برنامه نویسان به هدف "سریع بسازید" وسواس دارند و در هر فرصتی بهینه سازی های خرد را اضافه می کنند. این می‌تواند درک و نگهداری کد را دشوار کند و تغییر کد را با تکامل سخت‌افزار دشوار کند.نویسنده نکاتی را برای تمیز نگه داشتن معماری نرم افزار تعبیه شده ارائه می دهد، از جمله:کد را دوباره فاکتور کنید(Refactor the code) : نویسنده توصیه می کند که کد را مجدداً فاکتور کنید تا درک و نگهداری آن آسان باشد. این کار تغییر کد را با تکامل سخت افزار آسان تر می کند.جداسازی نرم افزار از سخت افزار: نویسنده توصیه می کند نرم افزار را از سخت افزار جدا کنید. این کار تغییر نرم افزار را بدون نیاز به تغییر سخت افزار آسان تر می کند.از API ها استفاده کنید: نویسنده استفاده از API ها را برای تعامل با سخت افزار توصیه می کند. این باعث می شود نرم افزار قابل حمل تر و استفاده مجدد آن آسان تر شود.به طور کلی، نویسنده بینش‌های ارزشمندی در مورد اهمیت نوشتن نرم‌افزار تعبیه‌شده ارائه می‌کند که به راحتی با تغییرات سخت‌افزاری تکامل می‌یابد.این مطلب، بخشی از تمرینهای درس معماری نرم‌افزار در دانشگاه شهیدبهشتی است. https://gamma.app/docs/Clean-Architecture-A-Guide-to-Software-Development-rx9yji6xv0xe004 همچنین برای مطالعه خلاصه کوتاه تر کتاب Clean Architecture می توانید از گامای من دیدن فرمایید . ☝️