לדלג לתוכן

ארכיטקטורה נקייה מוסברת בפשטות

"ארכיטקטורה נקייה" נשמע כמו עוד באזוורד שמנהל טכני זרק בישיבה כדי להרשים. אבל זה בעצם רעיון פשוט מאוד ברגע שמורידים ממנו את המעטפת התיאורטית, ומהניסיון שלי זה אחד הרעיונות שהכי משנים איך פרויקט מתנהג אחרי שנה-שנתיים של פיתוח, לא ביום הראשון.

הבעיה שארכיטקטורה נקייה פותרת

תארו לעצמכם קוד שבו הלוגיקה העסקית שלכם - חישוב מחירים, חוקי הרשמה, כללי אישור הזמנה - מפוזרת בתוך קונטרולרים של Django, ומעורבבת עם קריאות ל-SQL ישירות. עכשיו תבינו את הבעיה - אם יום אחד תרצו להחליף מסד נתונים, או להוסיף ממשק שורת פקודה בנוסף לאתר, או פשוט לכתוב בדיקות (טסטים) ללוגיקה העסקית בלי להרים שרת שלם - אתם תקועים. הכל שזור יחד.

ארכיטקטורה נקייה, רעיון שרוברט מרטין ניסח, פותרת בדיוק את זה. הרעיון המרכזי - הלוגיקה העסקית של המערכת שלכם צריכה להיות עצמאית לחלוטין מפרטים טכניים כמו מסד נתונים, פריימוורק ווב, או ממשק משתמש.

המבנה בעיגולים

ארכיטקטורה נקייה בדרך כלל מצוירת כעיגולים מקוננים, כשהעיגול הפנימי ביותר הוא הכי חשוב:

  • ישויות (Entities). הליבה העסקית הטהורה שלכם - כללים שנכונים תמיד, לא משנה איזו טכנולוגיה תשתמשו. למשל, "הזמנה לא יכולה להיות עם כמות שלילית".
  • מקרי שימוש (Use Cases). הלוגיקה שמתארת מה המערכת עושה - "לבצע הזמנה", "לבטל מנוי". כאן נמצא התהליך העסקי עצמו.
  • מתאמי ממשק (Interface Adapters). השכבה שמתרגמת בין העולם החיצוני לעולם הפנימי - קונטרולרים, מציגי נתונים (presenters), רכיבים שממירים בקשת HTTP לקריאה למקרה שימוש.
  • מסגרות וכלים (Frameworks and Drivers). השכבה החיצונית ביותר - מסד הנתונים בפועל, פריימוורק הווב, ממשק המשתמש. הדברים הכי נוטים להשתנות.

כלל התלויות - החלק החשוב באמת

זה הלב האמיתי של הרעיון, ואם תזכרו רק דבר אחד מהפוסט הזה, תזכרו את זה - תלויות תמיד מצביעות פנימה, לעולם לא החוצה.

זה אומר שהלוגיקה העסקית שלכם (העיגול הפנימי) לא יודעת בכלל שקיים Postgres, שקיים Flask, או שקיים React. היא מגדירה ממשקים (interfaces) שהיא צריכה - למשל "אני צריך משהו שיודע לשמור הזמנה" - והשכבות החיצוניות הן אלה שמממשות את הממשק הזה בפועל, בין אם עם מסד נתונים אמיתי או עם מסד נתונים מדומה לבדיקות.

התוצאה - אתם יכולים להחליף את מסד הנתונים, להחליף את הפריימוורק, אפילו להחליף בין REST ל-GraphQL, בלי לגעת בלוגיקה העסקית בכלל. וחשוב לא פחות - אתם יכולים לבדוק את הלוגיקה העסקית בטסטים מהירים בלי להרים שרת אמיתי או מסד נתונים אמיתי.

דוגמה קצרה שממחישה את זה

נניח שיש לכם מקרה שימוש "לבצע הזמנה". בגישה מבולגנת, הקוד הזה יושב ישר בתוך קונטרולר של Flask וקורא ל-SQLAlchemy ישירות. בארכיטקטורה נקייה, יש לכם מחלקה PlaceOrderUseCase שמקבלת בקונסטרוקטור שלה אובייקט מטיפוס OrderRepository - זה ממשק, לא מימוש. ה-PlaceOrderUseCase בכלל לא יודע אם OrderRepository שומר ל-Postgres, ל-MongoDB, או לקובץ טקסט. הוא רק יודע לקרוא לו ולבקש "שמור את ההזמנה הזאת".

בקוד האמיתי, כשמריצים את האפליקציה, מזריקים מימוש קונקרטי כמו PostgresOrderRepository. בטסטים, מזריקים FakeOrderRepository שרק שומר ברשימה בזיכרון. הלוגיקה העסקית לא זזה, לא השתנתה, ולא ידעה בכלל שהיה חילוף.

מתי זה שווה את המאמץ, ומתי לא

חשוב לומר בכנות - ארכיטקטורה נקייה מוסיפה שכבות, קבצים, וממשקים. לפרויקט קטן, סקריפט אישי, או MVP שרוצים לבדוק תוך שבוע, זו לרוב הגזמה שמאטה אתכם בלי סיבה טובה - זה בדיוק המקום שבו עקרון YAGNI צריך לדבר בראש שלכם.

היא משתלמת כשיש לכם מערכת שאמורה לחיות שנים, עם לוגיקה עסקית מורכבת שמשתנה לעתים רחוקות יותר מהטכנולוגיה סביבה, ועם צוות שצריך לתחזק ולבדוק את הקוד הזה לאורך זמן. במקרים כאלה, ההשקעה בהפרדה הזאת חוזרת אליכם עשרות מונים בכל פעם שאתם צריכים לשנות משהו ולא לפחד ששברתם חצי מערכת.

זה בדיוק סוג ההחלטות שאנחנו מלמדים לקחת נכון בקורס ארכיטקטורת תוכנה - לא רק את התיאוריה, אלא מתי היא באמת משתלמת ומתי היא רק מסבכת אתכם.

יש לכם שאלה על פרויקט ספציפי שלכם ואיך לחלק אותו לשכבות? בואו לשאול בדיסקורד, זה בדיוק סוג הדיון שמתנהל שם.

לסיכום

ארכיטקטורה נקייה זה לא עוד תבנית לזרוק על כל פרויקט. זה כלל פשוט אחד - הלוגיקה העסקית שלכם לא צריכה לדעת שום דבר על הטכנולוגיה שמריצה אותה. תעצבו כך שהתלויות תמיד מצביעות פנימה, ותרוויחו קוד שאפשר לבדוק, לשנות, ולהחליף בו חלקים בלי לפחד. השאלה תמיד היא אם המערכת שלכם באמת צריכה את זה, ולא אם זה נשמע מרשים.