สถาปัตยกรรมสะอาดแนะนำให้ให้โต้ตอบด้วยกรณีใช้เรียกใช้งานจริงของพรีเซนเตอร์ (ซึ่งถูกฉีดต่อไปนี้กรมทรัพย์สินทางปัญญา) เพื่อจัดการการตอบสนอง / จอแสดงผล อย่างไรก็ตามฉันเห็นคนที่ใช้สถาปัตยกรรมนี้ส่งคืนข้อมูลเอาต์พุตจากผู้โต้ตอบจากนั้นปล่อยให้คอนโทรลเลอร์ (ในชั้นอะแดปเตอร์) ตัดสินใจเลือกวิธีจัดการกับมัน โซลูชันที่สองรั่วไหลความรับผิดชอบของแอปพลิเคชันออกจากเลเยอร์แอปพลิเคชันนอกเหนือจากการกำหนดอินพุตและเอาต์พุตพอร์ตให้กับผู้โต้ตอบไม่ชัดเจนหรือไม่
พอร์ตอินพุตและเอาต์พุต
เมื่อพิจารณาคำจำกัดความของ Clean Architectureและโดยเฉพาะอย่างยิ่งแผนภาพการไหลขนาดเล็กที่อธิบายความสัมพันธ์ระหว่างตัวควบคุมผู้ใช้เคสผู้โต้ตอบและผู้นำเสนอฉันไม่แน่ใจว่าฉันเข้าใจถูกต้องหรือไม่ว่าควรใช้ "พอร์ตเคสเอาต์พุต" อย่างไร
สถาปัตยกรรมแบบคลีนเช่นสถาปัตยกรรมหกเหลี่ยมแยกความแตกต่างระหว่างพอร์ตหลัก (เมธอด) และพอร์ตรอง (อินเทอร์เฟซที่จะใช้งานโดยอะแดปเตอร์) ตามกระแสการสื่อสารฉันคาดหวังว่า "ใช้ Case Input Port" เป็นพอร์ตหลัก (ดังนั้นเป็นเพียงวิธีการ) และ "Use Case Output Port" เป็นอินเตอร์เฟสที่จะใช้งานอาจเป็นอาร์กิวเมนต์ตัวสร้างที่ใช้อะแดปเตอร์จริง เพื่อให้ผู้โต้ตอบสามารถใช้งานได้
ตัวอย่างรหัส
ในการสร้างตัวอย่างโค้ดนี่อาจเป็นรหัสคอนโทรลเลอร์:
Presenter presenter = new Presenter();
Repository repository = new Repository();
UseCase useCase = new UseCase(presenter, repository);
useCase->doSomething();
ส่วนต่อประสานผู้นำเสนอ:
// Use Case Output Port
interface Presenter
{
public void present(Data data);
}
ในที่สุดผู้โต้ตอบเอง:
class UseCase
{
private Repository repository;
private Presenter presenter;
public UseCase(Repository repository, Presenter presenter)
{
this.repository = repository;
this.presenter = presenter;
}
// Use Case Input Port
public void doSomething()
{
Data data = this.repository.getData();
this.presenter.present(data);
}
}
ในการโต้ตอบเรียกผู้นำเสนอ
การตีความก่อนหน้านี้ดูเหมือนจะได้รับการยืนยันจากไดอะแกรมข้างต้นเองซึ่งความสัมพันธ์ระหว่างคอนโทรลเลอร์และพอร์ตอินพุตถูกแทนด้วยลูกศรทึบที่มีหัว "คม" (UML สำหรับ "การเชื่อมโยง" หมายถึง "มี") คอนโทรลเลอร์ "มี" กรณีใช้งาน "ในขณะที่ความสัมพันธ์ระหว่างผู้นำเสนอและพอร์ตเอาท์พุทจะแสดงด้วยลูกศรทึบที่มีหัว" สีขาว "(UML สำหรับ" การสืบทอด "ซึ่งไม่ได้เป็นหนึ่งสำหรับ" การนำไปใช้ "แต่อาจเป็นไปได้ นั่นคือความหมายต่อไป)
นอกจากนี้ในคำตอบสำหรับคำถามอื่นโรเบิร์ตมาร์ตินอธิบายกรณีการใช้งานที่ผู้โต้ตอบเรียกพรีเซนเตอร์ตามคำขอการอ่าน:
การคลิกบนแผนที่จะทำให้ placePinController ถูกเรียกใช้ มันรวบรวมตำแหน่งของการคลิกและข้อมูลบริบทอื่น ๆ สร้างโครงสร้างข้อมูล placePinRequest และส่งผ่านไปยัง PlacePinInteractor ซึ่งตรวจสอบตำแหน่งของหมุดตรวจสอบความถูกต้องถ้าจำเป็นสร้างเอนทิตี Place เพื่อบันทึกพินสร้าง EditPlaceReponse วัตถุและส่งผ่านไปยัง EditPlacePresenter ซึ่งแสดงหน้าจอตัวแก้ไขสถานที่
เพื่อให้การเล่นนี้ทำได้ดีกับ MVC ฉันคิดว่าตรรกะของแอปพลิเคชั่นที่จะเข้าไปในคอนโทรลเลอร์นั้นถูกย้ายไปที่ตัวโต้ตอบเนื่องจากเราไม่ต้องการให้ตรรกะของแอปพลิเคชันรั่วไหลออกไปนอกเลเยอร์แอปพลิเคชัน คอนโทรลเลอร์ในเลเยอร์อะแดปเตอร์จะเรียกผู้โต้ตอบและอาจทำการแปลงรูปแบบข้อมูลเล็กน้อยในกระบวนการ:
ซอฟต์แวร์ในเลเยอร์นี้เป็นชุดของอะแดปเตอร์ที่แปลงข้อมูลจากรูปแบบที่สะดวกที่สุดสำหรับกรณีการใช้งานและเอนทิตีไปเป็นรูปแบบที่สะดวกที่สุดสำหรับเอเจนซี่ภายนอกบางแห่งเช่นฐานข้อมูลหรือเว็บ
จากบทความต้นฉบับพูดคุยเกี่ยวกับ Interface Adapters
บนข้อมูลที่ส่งคืนผู้โต้ตอบ
อย่างไรก็ตามปัญหาของฉันกับวิธีนี้คือกรณีการใช้งานต้องดูแลงานนำเสนอด้วยตัวเอง ตอนนี้ฉันเห็นว่าจุดประสงค์ของPresenter
อินเทอร์เฟซนั้นเป็นนามธรรมพอที่จะเป็นตัวแทนของผู้นำเสนอหลายประเภท (GUI, Web, CLI, ฯลฯ ) และมันก็หมายถึง "เอาท์พุท" ซึ่งเป็นสิ่งที่กรณีการใช้งานอาจ เป็นอย่างดีมี แต่ฉันยังไม่มั่นใจกับมันทั้งหมด
ตอนนี้เมื่อมองไปรอบ ๆ เว็บเพื่อหาแอพพลิเคชั่นของสถาปัตยกรรมที่สะอาดฉันดูเหมือนจะพบว่ามีคนตีความพอร์ตเอาท์พุทเป็นวิธีการส่งคืน DTO บางอย่าง นี่จะเป็นสิ่งที่ชอบ:
Repository repository = new Repository();
UseCase useCase = new UseCase(repository);
Data data = useCase.getData();
Presenter presenter = new Presenter();
presenter.present(data);
// I'm omitting the changes to the classes, which are fairly obvious
สิ่งนี้น่าสนใจเพราะเรากำลังเปลี่ยนความรับผิดชอบในการ "เรียก" การนำเสนอออกจากกรณีการใช้งานดังนั้นกรณีการใช้งานจึงไม่เกี่ยวข้องกับการรู้ว่าต้องทำอะไรกับข้อมูลอีกต่อไปแทนที่จะเพียงแค่ให้ข้อมูล นอกจากนี้ในกรณีนี้เรายังไม่ละเมิดกฎการพึ่งพาเนื่องจากกรณีการใช้ยังไม่ทราบอะไรเกี่ยวกับเลเยอร์ด้านนอก
อย่างไรก็ตามกรณีการใช้งานไม่ได้ควบคุมช่วงเวลาที่การนำเสนอจริงดำเนินการอีกต่อไป (ซึ่งอาจมีประโยชน์เช่นเพื่อทำสิ่งเพิ่มเติม ณ จุดนั้นเช่นการบันทึกหรือเพื่อยกเลิกพร้อมกันหากจำเป็น) นอกจากนี้โปรดสังเกตว่าเราสูญเสีย Use Case Input Port เนื่องจากตอนนี้คอนโทรลเลอร์ใช้getData()
วิธีการเท่านั้น(ซึ่งเป็นพอร์ตเอาต์พุตใหม่ของเรา) ยิ่งไปกว่านั้นฉันคิดว่าเรากำลังทำลายหลักการ "บอกอย่าถาม" ที่นี่เพราะเรากำลังขอให้ผู้โต้ตอบให้ข้อมูลบางอย่างทำอะไรกับมันแทนที่จะบอกให้ทำสิ่งจริงใน ที่แรก.
ตรงประเด็น
ดังนั้นทางเลือกใดในสองทางเลือกนี้คือการตีความ "ถูกต้อง" ของพอร์ตเคสเอาต์พุตการใช้งานตามสถาปัตยกรรมแบบคลีน? พวกเขาทั้งสองทำงานได้?