הקשרים DDD מוגבלים ומודולי Java

1. סקירה כללית

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

יחד עם זאת, עם מערכת המודול Java 9, אנו יכולים ליצור מודולים מכוסים היטב.

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

2. הקשרים עם גבולות DDD

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

2.1. הקשר מוגבל ושפה בכל מקום

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

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

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

2.2. הקשר סדר

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

ה הזמנת לקוח ישות היא שורש מצטבר:

ClassOrder בכיתה ציבורית {int orderId פרטי; תשלום מחרוזת פרטי כתובת מחרוזת פרטית; פרטי רשימת פריטים; צף ציבורי calcTotalPrice () {return orderItems.stream (). map (OrderItem :: getTotalPrice) .reduce (0F, Float :: sum); }}

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

לאחר מכן, בואו ניצור את הזמן פריט מעמד:

סדר ציבורי OrderItem {private int productId; כמות אינטנס פרטית; יחידת צף פרטית מחיר; יחידת צף פרטית משקל; }

הגדרנו ישויות, אך עלינו לחשוף API כלשהו לחלקים אחרים ביישום. בואו ניצור את CustomerOrderService מעמד:

מחלקה ציבורית CustomerOrderService מיישמת את OrderService {public static final String EVENT_ORDER_READY_FOR_SHIPMENT = "OrderReadyForShipmentEvent"; פרטי CustomerOrder מאגר orderRepository; EventBus פרטי EventBus; @Override public void placeOrder (הזמנת CustomerOrder) {this.orderRepository.saveCustomerOrder (order); מטען מטען = HashMap חדש (); payload.put ("order_id", String.valueOf (order.getOrderId ())); אירוע ApplicationEvent = ApplicationEvent חדש (מטען) {@Override ציבורי מחרוזת getType () {return EVENT_ORDER_READY_FOR_SHIPMENT; }}; this.eventBus.publish (אירוע); }}

הנה, יש לנו כמה נקודות חשובות להדגיש. ה מקום הזמנה השיטה אחראית על עיבוד הזמנות הלקוחות. לאחר עיבוד ההזמנה, האירוע מתפרסם ל EventBus. נדון בתקשורת מונחת האירועים בפרקים הבאים. שירות זה מספק את יישום ברירת המחדל עבור ה- OrderService מִמְשָׁק:

ממשק ציבורי OrderService מרחיב את ApplicationService {void placeOrder (הזמנת CustomerOrder); בטל setOrderRepository (CustomerOrderRepository orderRepository); }

יתר על כן, שירות זה מחייב את מאגר לקוחות להחזיק פקודות:

ממשק ציבורי CustomerOrderRepository {void saveCustomerOrder (הזמנת CustomerOrder); }

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

2.3. הקשר משלוח

עכשיו, בואו נגדיר את הקשר ההובלה. זה יהיה גם פשוט ויכיל שלוש ישויות: חֲבִילָה, פריט חבילה, ו ניתן להזמין.

נתחיל עם ה- ניתן להזמין יֵשׁוּת:

מחלקה ציבורית ShippableOrder {int intlInd private; כתובת מחרוזת פרטית; חבילת רשימה פרטית פריטים; }

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

וגם ה חֲבִילָה הישות ספציפית להקשר המשלוח:

חבילה בכיתה ציבורית {int orderId פרטי; כתובת מחרוזת פרטית; מעקב מחרוזת פרטי חבילת רשימה פרטית פריטים; צף ציבורי calcTotalWeight () {return packageItems.stream (). map (PackageItem :: getWeight) .reduce (0F, Float :: sum); } ציבורי בוליאני isTaxable () {return calcnEstimatedValue ()> 100; } צף ציבורי calculateEstimatedValue () {return packageItems.stream (). map (PackageItem :: getWeight) .reduce (0F, Float :: sum); }}

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

לבסוף, בואו נגדיר את ParcelShippingService:

class class ParcelShippingService מיישם ShippingService {public static final String EVENT_ORDER_READY_FOR_SHIPMENT = "OrderReadyForShipmentEvent"; פרטי משלוח הזמנה מאגר הזמנה מאגר; EventBus פרטי EventBus; מפה פרטית shippedParcels = HashMap חדש (); @Override public void shipOrder (int orderId) {סדר אופציונלי = this.orderRepository.findShippableOrder (orderId); order.ifPresent (completeOrder -> {Parcel parcel = new Parcel (completeOrder.getOrderId (), completeOrder.getAddress (), completeOrder.getPackageItems ()); if (parcel.isTaxable ()) {// חישוב מיסים נוספים} // משלוח מנות this.shippedParcels.put (completeOrder.getOrderId (), חבילה);}); } @Override public void listenToOrderEvents () {this.eventBus.subscribe (EVENT_ORDER_READY_FOR_SHIPMENT, EventSubscriber new () {@Override public void onEvent (E event) {shipOrder (Integer.parseInt (event.getPayloadVal); }); } @Override public אופציונלי getParcelByOrderId (int orderId) {החזר Optional.ofNullable (this.shippedParcels.get (orderId)); }}

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

3. מפות הקשר

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

ישנם חמישה סוגים עיקריים של קשרים בין הקשרים מגבילים:

  • שׁוּתָפוּת - קשר בין שני הקשרים שמשתפים פעולה בכדי ליישר בין שתי הצוותים למטרות תלויות
  • גרעין משותף - סוג של קשר כאשר חלקים משותפים ממספר הקשרים מחולצים להקשר / מודול אחר כדי להפחית את שכפול הקוד
  • ספק הלקוח - חיבור בין שני הקשרים, כאשר הקשר אחד (במעלה הזרם) מייצר נתונים, והשני (במורד הזרם) צורך אותו. במערכת יחסים זו שני הצדדים מעוניינים ליצור תקשורת מיטבית
  • קונפורמיסט - למערכת יחסים זו יש גם במעלה ובזרם, אולם במורד הזרם תואם תמיד את ה- API של המעלה
  • שכבה נגד שחיתות - סוג זה של יחסים נמצא בשימוש נרחב למערכות מדור קודם להתאמתן לארכיטקטורה חדשה ולהעביר בהדרגה מבסיס הקוד הקודם. שכבת Anticorruption משמשת כמתאם לתרגום נתונים ממעלה הזרם ולהגנה מפני שינויים לא רצויים

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

לפיכך, מודול SharedKernel לא יכיל שום יישום קונקרטי, אלא ממשקים בלבד.

נתחיל עם ה- EventBus מִמְשָׁק:

ממשק ציבורי EventBus {public void (אירוע E); בטל מנוי (String eventType, EventSubscriber subscriber); בטל ביטול הרישום (String eventType, EventSubscriber subscriber); }

ממשק זה יושם בהמשך במודול התשתית שלנו.

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

ממשק ציבורי ApplicationService {ברירת מחדל void publishEvent (אירוע E) {EventBus eventBus = getEventBus (); אם (eventBus! = null) {eventBus.publish (אירוע); }} מנוי בטל ברירת מחדל (String eventType, EventSubscriber subscriber) {EventBus eventBus = getEventBus (); אם (eventBus! = null) {eventBus.subscribe (eventType, מנוי); }} ברירת מחדל בטל ביטול המנוי (String eventType, EventSubscriber subscriber) {EventBus eventBus = getEventBus (); אם (eventBus! = null) {eventBus.unsubscribe (eventType, מנוי); }} EventBus getEventBus (); בטל setEventBus (EventBus eventBus); }

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

4. Java 9 מודולריות

עכשיו הגיע הזמן לבחון כיצד מערכת ה- Java 9 Module יכולה לתמוך במבנה היישומים שהוגדר.

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

בואו נראה את תרשים המודולים הסופי שלנו:

4.1. מודול SharedKernel

נתחיל במודול SharedKernel, שאין לו תלות במודולים אחרים. אז ה module-info.java נראה כמו:

מודול com.baeldung.dddmodules.sharedkernel {יצוא com.baeldung.dddmodules.sharedkernel.events; מייצאת com.baeldung.dddmodules.sharedkernel.service; }

אנו מייצאים ממשקי מודולים, כך שהם זמינים למודולים אחרים.

4.2. OrderContext מודול

לאחר מכן, נעביר את המיקוד שלנו למודול OrderContext. זה דורש רק ממשקים שהוגדרו במודול SharedKernel:

מודול com.baeldung.dddmodules.ordercontext {דורש com.baeldung.dddmodules.sharedkernel; מייצאת com.baeldung.dddmodules.ordercontext.service; מייצאת com.baeldung.dddmodules.ordercontext.model; יצוא com.baeldung.dddmodules.ordercontext.repository; מספק com.baeldung.dddmodules.ordercontext.service.OrderService עם com.baeldung.dddmodules.ordercontext.service.CustomerOrderService; }

כמו כן, אנו יכולים לראות כי מודול זה מייצא את יישום ברירת המחדל עבור ה- OrderService מִמְשָׁק.

4.3. ShippingContext מודול

בדומה למודול הקודם, בואו ניצור את קובץ ההגדרה של מודול ShippingContext:

מודול com.baeldung.dddmodules.shippingcontext {דורש com.baeldung.dddmodules.sharedkernel; מייצא com.baeldung.dddmodules.shippingcontext.service; מייצאת com.baeldung.dddmodules.shippingcontext.model; יצוא com.baeldung.dddmodules.shippingcontext.repository; מספק com.baeldung.dddmodules.shippingcontext.service.ShippingService עם com.baeldung.dddmodules.shippingcontext.service.ParcelShippingService; }

באותו אופן, אנו מייצאים את יישום ברירת המחדל עבור ה- ShippingService מִמְשָׁק.

4.4. מודול תשתית

עכשיו הגיע הזמן לתאר את מודול התשתיות. מודול זה מכיל את פרטי ההטמעה של הממשקים המוגדרים. נתחיל ביצירת יישום פשוט עבור ה- EventBus מִמְשָׁק:

מחלקה ציבורית SimpleEventBus מיישמת את EventBus {Map final final מנויים = ConcurrentHashMap חדש (); @ עקוב על פרסום חלל ציבורי (אירוע E) {if (subscribers.containsKey (event.getType ())) {subscribers.get (event.getType ()) .forEach (מנוי -> subscriber.onEvent (אירוע)); }} @ מנוי בטל ציבורי @ עקוב (מחרוזת eventType, מנוי EventSubscriber) {הגדר eventSubscribers = subscribers.get (eventType); if (eventSubscribers == null) {eventSubscribers = CopyOnWriteArraySet () חדש; subscribers.put (eventType, eventSubscribers); } eventSubscribers.add (מנוי); } @ ביטול ציבורי מבטל בטל (@ String eventType, EventSubscriber subscriber) {if (subscribers.containsKey (eventType)) {subscribers.get (eventType) .remove (subscriber); }}}

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

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

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

מחלקה סטטית ציבורית PersistenceOrder {public int orderId; תשלום מחרוזת ציבורי שיטה; כתובת מחרוזת ציבורית; ציבורי רשימת ציבורים מחלקה סטטית ציבורית OrderItem {public int productId; יחידה לצוף ציבורי מחיר; פריט צף ציבורי משקל; כמות אינטנסיבית ציבורית; }}

אנו יכולים לראות כי מחלקה זו מכילה את כל השדות משניהם הזמנת לקוח ו ניתן להזמין ישויות.

כדי לשמור על דברים פשוטים, בואו לדמות מסד נתונים בזיכרון:

מחלקה ציבורית InMemoryOrderStore מיישם את CustomerOrderRepository, ShippingOrderRepository {private orders ordersDb = HashMap חדש (); @Override public void saveCustomerOrder (CustomerOrder order) {this.ordersDb.put (order.getOrderId (), PersistenceOrder חדש (order.getOrderId (), order.getPaymentMethod (), order.getAddress (), סדר .getOrderItems (). () .map (orderItem -> PersistenceOrder.OrderItem חדש (orderItem.getProductId (), orderItem.getQuantity (), orderItem.getUnitWeight (), orderItem.getUnitPrice ())) .collect (Collectors.toList ()))) } @Override public אופציונלי findShippableOrder (int orderId) {אם (! This.ordersDb.containsKey (orderId)) להחזיר Optional.empty (); PersistenceOrder orderRecord = this.ordersDb.get (orderId); להחזיר Optional.of (ShippableOrder חדש (orderRecord.orderId, orderRecord.orderItems .stream (). מפה (orderItem -> PackageItem חדש (orderItem.productId, orderItem.itemWeight, orderItem.quantity * orderItem.unitPrice)). למנות()))); }}

כאן אנו ממשיכים ומשיגים סוגים שונים של ישויות על ידי המרת מודלים קבועים לסוג מתאים או ממנו.

לבסוף, ניצור את הגדרת המודול:

מודול com.baeldung.dddmodules.infrastructure {דורש com.baeldung.dddmodules.sharedkernel מעבר. דורש com.baeldung.dddmodules.ordercontext מעבר. דורש com.baeldung.dddmodules.shippingcontext מעבר. מספק com.baeldung.dddmodules.sharedkernel.events.EventBus עם com.baeldung.dddmodules.infrastructure.events.SimpleEventBus; מספק com.baeldung.dddmodules.ordercontext.repository.CustomerOrderRepository עם com.baeldung.dddmodules.infrastructure.db.InMemoryOrderStore; מספק com.baeldung.dddmodules.shippingcontext.repository.ShippingOrderRepository עם com.baeldung.dddmodules.infrastructure.db.InMemoryOrderStore; }

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

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

4.5. מודול ראשי

לסיום, נגדיר מודול שיהווה נקודת הכניסה ליישום שלנו:

מודול com.baeldung.dddmodules.mainapp {משתמש com.baeldung.dddmodules.sharedkernel.events.EventBus; משתמש com.baeldung.dddmodules.ordercontext.service.OrderService; משתמש ב com.baeldung.dddmodules.ordercontext.repository.CustomerOrderRepository; משתמש com.baeldung.dddmodules.shippingcontext.repository.ShippingOrderRepository; משתמש com.baeldung.dddmodules.shippingcontext.service.ShippingService; דורש תשתית com.baeldung.dddmodules.infrastructure; }

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

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

5. הפעלת האפליקציה

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

5.1. מבנה הפרויקט

הפרויקט שלנו מכיל חמישה מודולים ואת מודול האב. בואו נסתכל על מבנה הפרויקט שלנו:

מודולי ddd (ספריית הבסיס) pom.xml | - תשתית | - src | - ראשי | - java module-info.java | - com.baeldung.dddmodules.infrastructure pom.xml | - mainapp | - src | - main | - java module-info.java | - com.baeldung.dddmodules.mainapp pom.xml | - ordercontext | - src | - main | - java module-info.java | --com.baeldung.dddmodules.ordercontext pom.xml | - sharedkernel | - src | - main | - java module-info.java | - com.baeldung.dddmodules.sharedkernel pom.xml | - shippingcontext | - src | - main | - java module-info.java | - com.baeldung.dddmodules.shippingcontext pom.xml

5.2. יישום ראשי

נכון לעכשיו, יש לנו הכל למעט היישום הראשי, אז בואו נגדיר את שלנו רָאשִׁי שיטה:

ראשי סטטי ציבורי ריק (מחרוזת טוענת []) {מפה מיכל = createContainer (); OrderService orderService = (OrderService) container.get (OrderService.class); ShippingService shippingService = (ShippingService) container.get (ShippingService.class); shippingService.listenToOrderEvents (); CustomerOrder customerOrder = חדש CustomerOrder (); int orderId = 1; customerOrder.setOrderId (orderId); רשימת orderItems = ArrayList חדש (); orderItems.add (חדש OrderItem (1, 2, 3, 1)); orderItems.add (OrderItem חדש (2, 1, 1, 1)); orderItems.add (OrderItem חדש (3, 4, 11, 21)); customerOrder.setOrderItems (orderItems); customerOrder.setPaymentMethod ("PayPal"); customerOrder.setAddress ("הכתובת המלאה כאן"); orderService.placeOrder (customerOrder); אם (orderId == shippingService.getParcelByOrderId (orderId) .get (). getOrderId ()) {System.out.println ("ההזמנה עובדה ונשלחה בהצלחה"); }}

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

אבל איך הגענו לכל התלות ולמה createContainer החזרת שיטה מַפָּה<> אובייקט>? בואו נסתכל מקרוב על שיטה זו.

5.3. הזרקת תלות באמצעות ServiceLoader

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

אנו יכולים להשיג מופע מטעין על ידי הפעלת אחד מהסטטי לִטעוֹן שיטות של ServiceLoader מעמד. ה לִטעוֹן השיטה מחזירה את ניתן לנידון הקלד כדי שנוכל לחזור על יישומים שהתגלו.

עכשיו, בואו נמרח את המטעין לפתרון התלות שלנו:

מפה סטטית ציבורית createContainer () {EventBus eventBus = ServiceLoader.load (EventBus.class) .findFirst (). get (); CustomerOrderRepository customerOrderRepository = ServiceLoader.load (CustomerOrderRepository.class) .findFirst (). Get (); ShippingOrderRepository shippingOrderRepository = ServiceLoader.load (ShippingOrderRepository.class) .findFirst (). Get (); ShippingService shippingService = ServiceLoader.load (ShippingService.class) .findFirst (). Get (); shippingService.setEventBus (eventBus); shippingService.setOrderRepository (shippingOrderRepository); OrderService orderService = ServiceLoader.load (OrderService.class) .findFirst (). Get (); orderService.setEventBus (eventBus); orderService.setOrderRepository (customerOrderRepository); מפת גיבוב מיכל = HashMap חדש (); container.put (OrderService.class, orderService); container.put (ShippingService.class, shippingService); מיכל החזרה; }

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

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

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

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

מחלקה ציבורית InMemoryOrderStore מיישם CustomerOrderRepository, ShippingOrderRepository {פרטי נדיפים פרטיים סטטיים InMemoryOrderStore = חדש InMemoryOrderStore (); ספק סטטי ציבורי InMemoryOrderStore () {מופע החזרה; }}

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

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

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

לבסוף, אנו יכולים להריץ את היישום.

6. מסקנה

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

לאחר מכן ראינו כיצד להשתמש במערכת Java 9 Module יחד עם Contexted Context ליצירת מודולים מכוסים היטב.

יתר על כן, כיסינו את ברירת המחדל ServiceLoader מנגנון לגילוי תלות.

קוד המקור המלא של הפרויקט זמין באתר GitHub.


$config[zx-auto] not found$config[zx-overlay] not found