“ การกลับกันของการควบคุม” ส่งเสริม“ รูปแบบโดเมนโลหิตจาง” หรือไม่?


32

เมื่อฉันใช้ IoC Container ในโปรเจ็กต์สุดท้ายของฉันฉันได้จบด้วยหน่วยงานโลหิตจางและตรรกะทางธุรกิจส่วนใหญ่ของฉันในบริการไร้สัญชาติ

ฉันเคยเห็นโปรเจ็กต์ที่เขียนโดยนักพัฒนาคนอื่น ๆ ที่ใช้ "Inversion of Control" และพวกเขามักจะเป็น "Anemic"

เนื่องจาก "Anemic Domain Model" เป็นรูปแบบต่อต้านจึงเป็นไปได้หรือไม่ที่จะใช้ IoC และ Rich Domain เป็นตัวอย่างที่ดีของพวกเขาโครงการโอเพนซอร์สที่ทำเช่นนั้น?


ฉันคิดว่าเราต้องการเห็นตัวอย่างเฉพาะของกรณีของคุณเพื่อช่วย
Martijn Verburg

1
ขออภัยฉันหมายถึงโค้ด :)
Martijn Verburg

คำตอบ:


11

สำหรับผู้เริ่ม: DI และ IoC ไม่ใช่คำพ้องความหมาย ฉันขอโทษ แต่ฉันต้องชี้ให้เห็น (ดูเหมือนว่าคุณคิดว่าพวกเขาเป็น)

สำหรับคำถามของคุณ ... การพึ่งพาการฉีดเป็นเพียงเครื่องมือ วิธีที่คุณจะใช้เครื่องมือนี้เป็นสิ่งที่แยกจากกันอย่างสมบูรณ์ นอกจากนี้ยังมีเครื่องมืออื่น ๆ (รูปแบบการออกแบบ) ที่สามารถรวมถึงปัญหา ตัวอย่างเช่นฉันรู้สึกว่าการยอมรับอย่างกว้างขวางของรูปแบบ MVC เป็นหนึ่งในองค์ประกอบสำคัญในการสร้างรูปแบบการต่อต้าน Anemic Domain Model: ตัวควบคุม (ในแอพพลิเคชั่นที่ง่ายกว่าในรูปแบบที่ซับซ้อนมากขึ้นซึ่งจะเป็นชั้นบริการเพิ่มเติม) บังคับให้พวกเขารวมถึงการเปลี่ยนเอนทิตี DB ให้เป็นสิ่งที่มีประโยชน์ในขณะที่ Business Layer เปลี่ยนเป็นเลเยอร์การเข้าถึงข้อมูลอย่างง่ายที่เป็น ORM ธรรมดาที่มีการแมปแบบหนึ่งต่อหนึ่งกับการแม็พแบบหนึ่งต่อหนึ่ง

แน่นอนว่ามันเป็นวิธีที่คุณออกแบบแอปพลิเคชันของคุณ - คุณสามารถสร้างรูปแบบโดเมนที่ถูกต้องหากคุณต้องการและ IoC, DI, MVC เหล่านี้ทั้งหมดไม่ได้หยุดคุณ สิ่งที่สามารถหยุดคุณได้คือทีมของคุณ คุณต้องโน้มน้าวให้พวกเขาใช้เส้นทางที่ถูกต้องและอาจเป็นเรื่องยากเนื่องจากผู้พัฒนาซอฟต์แวร์จำนวนมากไม่มีภูมิหลังทางสถาปัตยกรรมที่แข็งแกร่ง


ฉันจะเพิ่มนี้ที่บางทีคุณอาจจะมองวิธี DDD โดย Eric Evans และคณะ
Martijn Verburg

1
ฉันอ่านหนังสือของ Eric Evans มันเป็นสิ่งที่ดีสำหรับวิธีการทั่วไปและภาษาที่แพร่หลาย แต่ค่อนข้างขาดในตัวอย่างโลกแห่งความจริง
Mag20

ขอบคุณสำหรับการชี้ให้เห็นความแตกต่างระหว่าง DI และ IoC ฉันคิดว่าปัญหาเกี่ยวข้องกับ IoC มากกว่า DI เปลี่ยนคำถามเพื่อสะท้อนว่า
Mag20

จากประสบการณ์ของฉันกับเฟรมเวิร์ก / คอนเทนเนอร์ DI (Spring DI, CDI, Unity) พวกเขาจะหยุดคุณจากการสร้าง "รูปแบบโดเมนที่ถูกต้อง" ซึ่งสำหรับฉันหมายความว่านักพัฒนาไม่ควรถูก จำกัด จากการใช้วัตถุจริง (เช่น stateful) . แต่ DI ไม่ได้สนับสนุนสิ่งนั้นจริงๆ
Rogério

8

แอปพลิเคชั่นส่วนใหญ่ (ถ้าไม่ใช่ทั้งหมด) เป็นการผสมผสานระหว่างโครงสร้างพื้นฐานและความกังวลเกี่ยวกับโดเมน เมื่อคุณมีความซับซ้อนในระดับหนึ่งคุณจะสามารถจัดการได้ง่ายขึ้นหากโดเมนถูกแยกออกจากโครงสร้างพื้นฐานเพื่อให้ง่ายต่อการให้เหตุผลและสามารถพัฒนาได้อย่างอิสระ

แน่นอนว่ารูปแบบโดเมนยังคงต้องการสื่อสารกับส่วนที่เหลือของระบบและโดยปกติจะเป็นบริการไร้สัญชาติ (ซึ่งเป็นส่วนหนึ่งของโดเมน) ที่มีปัญหาด้านโครงสร้างพื้นฐาน (เช่นการเข้าถึงฐานข้อมูล) ที่ถูกแทรกเข้าไป การใช้คอนเทนเนอร์ IoC ไม่ได้เป็นการลบการอ้างอิงนี้ แต่จะย้ายการกำหนดค่าไปยังพื้นที่อื่น - ทำให้ง่ายขึ้นในการให้เหตุผลและดูแลรักษา

เอนทิตีกำลังจัดเก็บสถานะและควรรับผิดชอบกฎธุรกิจ หากบริการของคุณบังคับใช้ค่าคงที่และกฎเกณฑ์ทางธุรกิจอื่น ๆ ก็อาจเป็นไปได้ว่าตรรกะนั้นผิดที่

ตอนนี้ถ้าคุณมีเหตุผลในสถานที่ที่ถูกต้องและยังคงจบลงด้วยการให้บริการที่ไม่เพียงแค่ห่อหุ้มโครงสร้างพื้นฐานสิ่งต่าง ๆ และเอนทิตีที่เป็นเพียงแค่ถุงทรัพย์สินก็เป็นไปได้มากที่โดเมนจะไม่ซับซ้อนพอที่จะพิสูจน์ ค่าใช้จ่ายของแบบจำลองของตัวเอง ทุกสิ่งที่คุณจะได้อ่านเกี่ยวกับ DDD จะมีข้อจำกัดความรับผิดชอบที่มีไว้สำหรับโดเมนที่ซับซ้อนเท่านั้น แต่ดูเหมือนว่ามันจะถูกลืมบ่อยเกินไป


7

ไปที่แหล่งที่มา เริ่มต้นด้วยชิ้นฟาวเลอร์ในรุ่นโลหิตจางโดเมน เขาอ้างถึงการออกแบบการขับเคลื่อนโดเมนของ Eric Evan เป็นตัวอย่างของการปฏิบัติที่ดี source code สำหรับที่อยู่ที่นี่ ดาวน์โหลดได้.

สังเกตว่ามันใช้ Inversion of Control (ค้นหา @Autowired) และมีคลาสบริการ (BookingService) และคลาส "กระบวนการทางธุรกิจ" (เช่น ItineraryUpdater)

บทความต้นฉบับของพรานล่าสัตว์เริ่มต้นเส้นทางไปยังตัวอย่างที่คุณกำลังมองหา


ในความเป็นจริงตัวอย่างแอปนั้นไม่เป็นไปตาม DDD ตามที่อธิบายไว้ในหนังสือ ข้อขัดแย้งเฉพาะกับหนังสือเล่มนี้คือมันละเมิดแนวคิดของ "โครงสร้างพื้นฐาน" อย่างเต็มที่โดยอนุญาตให้มีรหัสเฉพาะโดเมน ตัวอย่างเช่นVoyageRepositoryHibernateคลาสซึ่งวางในเลเยอร์โครงสร้างพื้นฐาน แต่จริง ๆ แล้วขึ้นอยู่กับเลเยอร์โดเมน
Rogério

ใช่หนังสือเล่มนี้บอกว่าในหน้า 73 ว่าเลเยอร์โครงสร้างพื้นฐานคือ "ด้านล่าง" เลเยอร์โดเมนและ "ไม่ควรมีความรู้พิเศษเกี่ยวกับโดเมนที่ให้บริการ" มันไม่สมเหตุสมผลเลยสำหรับฉัน พิจารณาโครงการที่มีการใช้ VoyageRepository สองรายการ: VoyageRepositoryHibernate และคลาส VoyageRepositoryJDBC การใช้งานของพวกเขาแตกต่างกันมากและเทคโนโลยีเฉพาะ สิ่งเหล่านี้อยู่ในเลเยอร์โดเมนหรือไม่ หรือเลเยอร์โครงสร้างพื้นฐาน? ในรหัสของเราตามหนังสือเราทำย้อนหลัง: เลเยอร์โครงสร้างพื้นฐานสามารถอ้างอิงเลเยอร์โดเมน แต่ไม่ใช่ในทางกลับกัน
jamie

พวกเขาอยู่ในโดเมนเลเยอร์ใช่ การประยุกต์ใช้ JDBC จะประกอบด้วยรหัส SQL ที่เชื่อมโยงกับตารางและคอลัมน์ในฐานข้อมูลแอปพลิเคชันซึ่งเฉพาะกับโดเมน การใส่รหัสเฉพาะโดเมนหรือแอปพลิเคชันลงในเลเยอร์โครงสร้างพื้นฐานนั้นผิดเนื่องจาก "รหัสโครงสร้างพื้นฐาน" ควรใช้เพื่อแก้ปัญหาด้านเทคนิคเท่านั้นและควร (ควร) นำมาใช้ซ้ำได้อย่างสมบูรณ์ระหว่างแอปพลิเคชันและโดเมนที่ต่างกัน วิธีแก้ปัญหาในการมีรหัส "ระดับต่ำ" (เช่น SQL) ในเลเยอร์โดเมนไม่ได้เป็นการย้ายออกโดยสิ้นเชิง แต่จะนำไปใช้กับโครงสร้างพื้นฐานที่ดีกว่าเช่น ORM
Rogério

สำหรับฉันแล้วการใช้งานการบันทึก (MyDomainObject foo) เป็นปัญหาด้านเทคนิคอย่างแท้จริง YMMV
jamie

หากมันไม่ได้นำคุณไปสู่การละเมิดกฎพื้นฐานของสถาปัตยกรรมแบบเลเยอร์: เลเยอร์ที่ต่ำกว่าไม่สามารถขึ้นอยู่กับเลเยอร์ที่สูงขึ้นได้ ดังนั้นหากคุณใช้งานsave(foo)กับรหัสที่อาจมีการเปลี่ยนแปลงเมื่อมีการเปลี่ยนแปลงรูปแบบโดเมน (ตัวอย่างเช่นหากมีการเพิ่มแอตทริบิวต์ใหม่ลงไปMyDomainObject) จะต้อง (ตามคำนิยาม) เป็นของเลเยอร์โดเมน ไม่เช่นนั้นคุณจะไม่สามารถพูดถึงการมี "เลเยอร์" อีกต่อไป
Rogério

7

เป็นไปได้หรือไม่ที่จะใช้ IoC และ Rich Domain เป็นตัวอย่างที่ดีของพวกเขาโครงการโอเพนซอร์สที่ทำเช่นนั้น?

ฉันถือว่าคุณหมายถึง DI แทน IoC และโครงการที่คุณทำงานใช้ DI container เช่น Spring IoC มีสองรสชาติหลัก: รูปแบบ DI และตัวระบุตำแหน่ง ฉันไม่เห็นว่าทำไมรูปแบบตัวระบุตำแหน่งควรเป็นปัญหาดังนั้นให้มุ่งเน้นที่ DI

ฉันไม่คิดว่ามันเป็นไปได้หรืออย่างน้อยก็อาจเป็นสิ่งที่ทำไม่ได้ ลักษณะสำคัญของภาชนะ DI คือพวกมันควบคุมการสร้างวัตถุเมื่อพวกมันฉีดเข้าไปในสิ่งอื่น ("วัตถุที่ได้รับการจัดการ") ชุดของวัตถุที่ได้รับการจัดการที่ยังมีชีวิตอยู่เมื่อโครงการทำงานเป็นอิสระจากรายการโดเมนที่มีอยู่ในโครงการของคุณ แต่ขึ้นอยู่กับว่าวัตถุมีสายและขอบเขตใด (singleton, prototype) ที่กำหนดให้กับพวกเขา

นี่คือเหตุผลที่คุณไม่ต้องการให้ DI container จัดการวัตถุโดเมนของคุณ แต่ถ้าคุณสร้างวัตถุด้วยตนเอง (ใหม่) คุณจะไม่สามารถฉีดวัตถุอื่น ๆ ไปยังวัตถุโดเมนของคุณได้ (การทิ้งงานที่เป็นไปได้ด้วยการเดินสายไฟด้วยตนเอง) เนื่องจากคุณต้องการการฉีดเหล่านี้เพื่อแทนที่การใช้งานกับผู้อื่นคุณจึงไม่สามารถแทนที่การทำงานของวัตถุโดเมนที่หลากหลายโดยใช้ DI ดังนั้นคุณจะไม่ต้องการวางฟังก์ชันการทำงานลงในวัตถุโดเมนหรือคุณจะสูญเสียคุณสมบัติของ DI

ฉันไม่เห็นว่าคอนเทนเนอร์ DI สมมุติสามารถทำงานได้ที่ไม่จัดการวัตถุของคุณและการใช้งานที่มีอยู่ไม่อนุญาต ดังนั้นจึงยุติธรรมที่จะอ้างว่า DI อาศัยการจัดการวัตถุ ดังนั้นจะเป็นการล่อลวงให้คุณแบ่งวัตถุ Rich Domain ที่มีศักยภาพออกเป็นคลาส anemic หนึ่งคลาสและหนึ่งหรือหลายคลาสสคริปต์ธุรกรรม


คำตอบนี้กระทบเล็บจริง ๆ บนหัวเมื่อมันมาถึงความตึงเครียดระหว่าง Rich Domain Model และ Dependency Injection
jrahhali
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.