Onion Architecture PopUpService
สมมติว่าสถาปัตยกรรมประเภทนี้ไม่ว่าคุณจะเรียกมันว่าสะอาด ddd หัวหอมหรือหกเหลี่ยมโดยที่การอ้างอิงชี้เข้าด้านในและตรรกะของแอปพลิเคชันนั้นหมายถึงการแยกออกจาก UI / การนำเสนออย่างมาก
ฉันจะจัดการบางอย่างเช่นการแสดงป๊อปอัปได้อย่างไร ป๊อปอัปจะต้องถูกนำไปใช้จริงในเลเยอร์การนำเสนออย่างชัดเจน แต่คลาส / กรณีการใช้งาน / บริการ / ตรรกะจริงที่จะต้องแสดงป๊อปอัปและดำเนินการตามข้อมูลที่ผู้ใช้ป้อนนั้นอาศัยอยู่ในเลเยอร์แอปพลิเคชัน
ดังนั้นฉันจึงสร้างอินเตอร์เฟส IPopUpService ในเลเยอร์แอปพลิเคชัน ทันใดนั้นฉันสามารถเข้าถึงมันได้ในเลเยอร์การคงอยู่ ฉันสามารถใส่ป๊อปอัปลงในแบบสอบถามฐานข้อมูลได้อย่างแท้จริง นี่ดูเหมือนจะเป็นข้อบกพร่องใหญ่ทีเดียว
โปรดทราบว่าเหตุใดตัวอย่างของสถาปัตยกรรมนี้จึงถูกระบุเป็น web apis เท่านั้น ในการพูดคุยหรือในตัวอย่างการแก้ปัญหาบน github มันควรจะเป็นผู้ไม่เชื่อเรื่องพระเจ้าส่วนหน้า แต่คนมักจะลงเอยด้วยสิ่งที่ง่ายที่สุด
แอปพลิเคชันอนุภาคของฉันคือ wpf mvvm หนึ่ง Mvvm เป็นรูปแบบสถาปัตยกรรมอื่นที่ควรแยกตรรกะออกจาก UI แต่ฉันพบปัญหาเดียวกัน viewmodels ของฉันอยู่ในโปรเจ็กต์ที่ไม่ใช่ wpf โดยใช้ IPopUpService อย่างไรก็ตามสิ่งนี้ช่วยให้ที่เก็บของฉันใช้ด้วย ฉันไม่ได้ทำ แต่ฉันมีตัวเลือกซึ่งดูเหมือนจะผิดสำหรับฉัน
"ใส่ทุกอย่างในแอปพลิเคชันผ่านอินเทอร์เฟซ" ให้ความรู้สึกคล้ายกับการมีเพียงเลเยอร์เดียวโครงการขนาดใหญ่โครงการเดียว ฉันเข้าใจว่าการพึ่งพา IPopUpService ไม่เหมือนกับการพึ่งพา PopUpBox ของ wpf แต่ก็รู้สึกใกล้เคียงมากพอ ฉันคิดผิดที่อื่นหรือเปล่า? การ จำกัด การเข้าถึงไม่ใช่การพิจารณาหรือไม่?
คำตอบ
โดยปกติคุณจะไม่ใส่บริการ "ป๊อปอัป" ทั่วไปไว้ที่ใดก็ได้เนื่องจากป๊อปอัปเป็นแนวคิดที่เกี่ยวข้องกับการใช้งาน UI ที่กำหนด
ในการหาทางเลือกที่เหมาะสมคุณต้องวิเคราะห์กรณีที่เป็นรูปธรรม
- หากคุณต้องการข้อมูลเพิ่มเติมเพื่อทำการสืบค้นฐานข้อมูลในเลเยอร์การคงอยู่ของคุณกรณีการใช้งานควรส่งข้อมูลนี้ไปยังการเรียกการคงอยู่แล้ว ดังนั้นกรณีการใช้งาน - ซึ่งโดยปกติจะมีอินพุตและเอาต์พุต UI บางประเภท - ควร "รู้" แล้วว่าข้อมูลใดที่จำเป็นและส่งข้อมูลนี้ไปยังการโทรครั้งแรก
- หากโดเมนหลักของคุณต้องการถ่ายทอดข้อมูลไปยังผู้สนใจให้ใช้บริการข้อความบางประเภท ข้อความนี้อาจถูกหยิบขึ้นมาโดย ui เพื่อแสดงป๊อปอัปข้อความนี้อาจหยิบขึ้นมาโดยคนตัดไม้หรือบริการอีเมลหรือการใช้งานอื่นใดก็ได้
นี่เป็นเพียงสองตัวอย่าง แต่ฉันหวังว่าพวกเขาจะแสดงให้เห็นว่าคุณต้องเปลี่ยนวิธีการวางแผนแอปพลิเคชันอย่างไรให้ห่างจากคำถาม "สิ่งที่เกิดขึ้นใน UI" เป็น "สิ่งที่เกิดขึ้นในแอปพลิเคชัน"
แนวคิดทั้งหมดคือคุณสามารถใช้โดเมนหลักและใช้บริการในบริบทเดียว (UI) ได้น้อยกว่า เช่นองค์ประกอบการคงอยู่ควรได้รับการออกแบบมาเพื่อให้สามารถใช้จากชุดงานหรือจากการโทรที่เหลือได้ สิ่งนี้ไม่รวมถึงความสามารถในการขอข้อมูลเพิ่มเติมระหว่างการดำเนินการ
คุณสะดุดเข้ากับบางสิ่งที่ชัดเจนมากจนคนส่วนใหญ่มองไม่เห็น: สถาปัตยกรรมเหล่านี้ไม่ได้แยกส่วนอะไรเลย
ในการทำสิ่งที่คุณต้องการในสถาปัตยกรรมประเภทนี้คุณจะต้องส่งออก / เผยแพร่ข้อมูลทั้งหมดที่เกี่ยวข้องกับป๊อปอัปนี้และให้ UI ใช้ข้อมูลนี้เพื่อแสดงป๊อปอัป เห็นได้ชัดว่านี่คือสิ่งที่ตรงกันข้ามกับการแยกส่วน คุณต้องการสิ่งใหม่ ๆ ในด้านหนึ่งคุณมักจะต้องแก้ไขอีกด้านหนึ่งด้วย
วิธีที่ฉันใช้ในการจัดการกับความไม่สอดคล้องกันของการรับรู้จากสิ่งนี้คือฉันหาเหตุผลเข้าข้างตนเองว่าฉันแค่เผยแพร่ข้อมูลบางส่วนในแกนกลาง มันไม่ได้เกี่ยวกับป๊อปอัพ แต่สามารถใช้กับอย่างอื่นได้เช่นกัน แน่นอนว่าแทบจะไม่เกิดขึ้นเลยหากเคยเป็นเช่นนั้น
แน่นอนว่านี่เป็นเหตุผลที่ว่าตัวอย่างทั้งหมดนี้เป็นแบบเว็บและมักจะไม่สำคัญมาก
คุณได้ค้นพบสิ่งที่เป็นจริง ขุดต่อไปอย่าไว้ใจใคร (มีเนื้อหาที่ไม่ดีมากมายบางส่วนจากนักเขียนชื่อดัง) :) ลองทำสิ่งต่างๆด้วยตัวคุณเองเสมอ
สิ่งที่ต้องการจะทำงานนี้?
Something.Ui.WpfApplication
+ Services
- PopupService
Something.Ui
+ Contracts
- IPopupService
+ ViewModels
Something.Data
+ Repositories
Something.Core
+ Contracts
+ Domain
สิ่งนี้จะแยกวัตถุมุมมองของคุณไปยังเลเยอร์มุมมอง (การนำเสนอ) ของคุณ
สัญญาหลัก / โดเมนล้วนเป็นการดำเนินธุรกิจที่สำคัญ แต่ afaik แต่ละชั้นสามารถมีสัญญาของตัวเองได้เช่นกัน
เลเยอร์ที่สูงขึ้นใช้งานจากเลเยอร์ด้านล่าง
โดยทั่วไปแล้วสัญญาจะใช้แทนกันได้ดังนั้นบางทีคุณอาจลงเอยด้วยสิ่งนี้
Something.Ui.WpfApplication
+ Services
- RedPopUpService
Something.Ui.WinFormsApp
+ Services
- BluePopUpService
Something.Ui
+ Contracts
- IPopUpService
+ Services
- PopUpSharedLogic
+ ViewModels
"ดังนั้นฉันจึงสร้างอินเทอร์เฟซ IPopUpService ในเลเยอร์แอปพลิเคชันทันใดนั้นฉันก็สามารถเข้าถึงได้ในเลเยอร์การคงอยู่"
ความจริงที่ว่าคุณสามารถอ้างอิงบางสิ่งบางอย่างไม่ได้หมายความว่าคุณควร ; คุณต้องการควบคุมจำนวนการพึ่งพาระหว่างกันอย่างรอบคอบ (เพื่อลดความซับซ้อนให้น้อยที่สุดคุณไม่ต้องการให้เว็บพันกัน)
นอกจากนี้เลเยอร์การคงอยู่เองยังใช้ abstractions ที่เกี่ยวข้องกับการคงอยู่จำนวนหนึ่งที่กำหนดโดย (และภายใน) ชั้นแอปพลิเคชัน (เช่นหนึ่งหรือหลายอินเทอร์เฟซ) ดังนั้นคุณสามารถพูดในสิ่งเดียวกันกับสิ่งเหล่านั้นได้ (คุณสามารถอ้างอิงได้จากส่วนที่ไม่คงอยู่ของชั้นนอก - ไม่ได้หมายความว่าคุณควร) ดังนั้นจึงมีการสลายตัวของสิ่งต่างๆที่นั่นแม้ว่ามันจะบอกเป็นนัย ๆ เท่านั้น: ชั้นเองไม่ได้เป็นเสาหิน การขึ้นต่อกันข้ามเลเยอร์โดยทั่วไปแล้วจะ จำกัด เฉพาะบางส่วนของชั้นในเท่านั้นพวกเขาไม่ได้ครอบคลุมทั้งสิ่ง
เลเยอร์แอปพลิเคชันจะถูกย่อยสลายเองในบางวิธี (ตามคุณสมบัติกรณีการใช้งานระบบย่อยอะไรก็ตาม ... ); อีกครั้งเป้าหมายคือการควบคุมความซับซ้อนโดยการลดการอ้างอิง การสลายตัวนี้ส่วนใหญ่อาจเป็นตรรกะ แต่ในบางจุดคุณสามารถตัดสินใจที่จะแบ่งเลเยอร์ออกเป็นหลาย ๆ DLL ได้เช่นกัน ยังคงเป็นเลเยอร์เดียวทั้งหมด แต่ส่วนประกอบจากชั้นนอกจะอ้างอิงเฉพาะ DLL ที่ต้องการเท่านั้นไม่ใช่ทั้งหมด คุณไม่จำเป็นต้องทำการเปลี่ยนแปลงการออกแบบครั้งใหญ่ในแอปพลิเคชันของคุณเพื่อให้สามารถแยกย่อยออกเป็น DLL เช่นนั้น
ตอนนี้เมื่อคุณพัฒนาระบบของคุณและทำความเข้าใจกับโดเมนของปัญหามากขึ้นแนวคิดบางอย่างจะเปลี่ยนไปคนอื่น ๆ จะปรากฏขึ้นและคนอื่น ๆ จะถูกทิ้งไป ของคุณIPopUpServiceเป็นอินเทอร์เฟซเฉพาะทางที่แคบตามแนวคิด Robert Martin เรียกว่าพอร์ตเอาต์พุต สิ่งที่เป็นนามธรรมที่ช่วยให้คุณสามารถเรียกใช้บริการระดับล่างได้โดยไม่ต้องพึ่งพาบริการนั้น สิ่งที่เป็นนามธรรมโดยเฉพาะนี้ไม่ใช่นามธรรมทั้งหมด นี่ไม่ใช่สิ่งเลวร้ายในตัวมันเองโดยเฉพาะอย่างยิ่งในขั้นตอนนี้ แต่หมายความว่าสมมติฐานมาตรฐานคือจะมี GUI บางประเภทที่เกี่ยวข้อง
ตอนนี้ถ้ามีการใช้คลาสในเลเยอร์การIPopUpServiceคงอยู่มันจะค่อนข้างแปลกฉันเห็นด้วย เพียงเพื่อชี้แจงบางอย่างก่อนที่ฉันจะดำเนินการต่อ คุณเคยพูดว่า:
"ทันใดนั้นฉันก็สามารถเข้าถึง [IPopUpService] ในเลเยอร์การคงอยู่"
ไม่ใช่ว่าคุณสามารถเข้าถึงได้ แต่คุณมีการพึ่งพาและคุณกำลังจัดเตรียมการใช้งานสำหรับเลเยอร์แอปพลิเคชันที่จะใช้ ปัญหาคือคุณใช้อินเทอร์เฟซเฉพาะทางแคบ ๆ และกำหนดจุดประสงค์ที่กว้างขึ้นโดยไม่ต้องเปลี่ยนชื่อและสร้างใหม่
หากคุณลืมเกี่ยวกับแง่มุมของ GUI ในขณะนี้สิ่งที่IPopUpServiceเกิดขึ้นจริงก็คือบางสิ่งตามบรรทัดเหล่านี้: มันส่งสัญญาณไปยังชั้นนอกเพื่อระบุผลลัพธ์ที่เกี่ยวข้องกับตรรกะทางธุรกิจ (ความสำเร็จความล้มเหลว ฯลฯ ) และฉันหมายถึง "ส่งสัญญาณ" เนื่องจากชั้นแอปพลิเคชันเป็นเพียงวิธีการเรียกใช้ตัวแปรชนิดนามธรรม ผลข้างเคียง (ป๊อปอัปกำลังแสดง) เกิดขึ้นในเลเยอร์การนำเสนอ
แต่สัญญาณเหล่านี้อาจมีความเกี่ยวข้องทางธุรกิจของตัวเองดังนั้นคุณอาจต้องการให้ส่งผลมากกว่านั้นเพียงแค่แสดงป๊อปอัป เช่นคุณอาจต้องการสร้างบันทึกเหตุการณ์และจัดเก็บไว้ในไฟล์และ / หรือในตารางฐานข้อมูล ดังนั้นคุณอาจจบลงด้วยการสรุปอินเทอร์เฟซนี้ (เช่นIOperationResultConsumer1 ) เพื่อให้สามารถใช้งานได้ในทุกสถานการณ์โดยการนำเสนอฐานข้อมูลและเลเยอร์โครงสร้างพื้นฐานใช้เวอร์ชันของตนเองแต่ละเวอร์ชัน (หนึ่งเพื่อแสดงป๊อปอัป สองเพื่อจัดเก็บเหตุการณ์หรือรายการบันทึก)
หรือคุณอาจตัดสินใจโดยพิจารณาจากปัจจัยและข้อ จำกัด อื่น ๆ ว่าควรมีอินเทอร์เฟซแยกต่างหากสำหรับสิ่งเหล่านี้ เช่นอาจมีความแตกต่างเล็กน้อยในตรรกะที่ไม่สามารถสรุปได้ง่ายทำให้วิธีการเชื่อมต่อแบบรวมมีความซับซ้อนมากขึ้น
สรุปได้ว่าประเด็นหลักสองประการของฉันคือ (1) มุมมองแบบเลเยอร์ของระบบเป็นมุมมองระดับสูงมากและยิ่งไปกว่านั้นคุณต้องคำนึงถึงวิธีที่คุณจัดระเบียบโค้ดภายในเลเยอร์และพิจารณาเกี่ยวกับ การพึ่งพาและ (2) นามธรรมที่คุณคิดขึ้นในตอนแรกไม่จำเป็นต้องเป็นสิ่งที่คุณจะต้องจบลง - จะต้องมีการปรับแต่ง (หรือควรมี) จำนวนหนึ่งเมื่อเวลาผ่านไป
1คิดไม่ออกว่า ATM ชื่อดีกว่านี้