จะรักษาความน่าเชื่อถือของการอ้างอิงระหว่างมวลรวมได้อย่างไร?


11

ฉันกำลังดิ้นรนเล็กน้อยกับการอ้างอิงระหว่างมวลรวม สมมติรวมมีการอ้างอิงถึงการรวมCar อ้างอิงนี้จะได้รับการสร้างแบบจำลองโดยมีDriverCar.driverId

ตอนนี้ปัญหาของฉันคือไกลแค่ไหนฉันควรจะไปในการตรวจสอบการสร้างที่รวมอยู่ในCar CarFactoryฉันควรจะเชื่อหรือไม่ว่าการส่งผ่านDriverIdหมายถึงที่มีอยู่ Driverหรือฉันควรตรวจสอบค่าคงที่หรือไม่

สำหรับการตรวจสอบฉันเห็นความเป็นไปได้สองประการ:

  • ฉันสามารถเปลี่ยนลายเซ็นของโรงงานรถยนต์เพื่อยอมรับเอนทิตีของคนขับทั้งหมด จากนั้นโรงงานก็จะเลือกรหัสจากเอนทิตีนั้นและสร้างรถด้วย ที่นี่ค่าคงที่มีการตรวจสอบโดยปริยาย
  • ฉันจะมีการอ้างอิงของDriverRepositoryในและชัดเจนโทรCarFactorydriverRepository.exists(driverId)

แต่ตอนนี้ฉันสงสัยว่าการตรวจสอบไม่แปรปรวนมากเกินไปใช่ไหม ฉันสามารถจินตนาการได้ว่ามวลรวมเหล่านั้นอาจอาศัยอยู่ในบริบทที่มีขอบเขตแยกกันและตอนนี้ฉันจะทำให้มลภาวะทางรถยนต์ของ BC กับการพึ่งพาใน DriverRepository หรือเอนทิตีของผู้ขับขี่ของไดรเวอร์ BC

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

คำตอบ:


6

ฉันสามารถเปลี่ยนลายเซ็นของโรงงานรถยนต์เพื่อยอมรับเอนทิตีของคนขับทั้งหมด จากนั้นโรงงานก็จะเลือกรหัสจากเอนทิตีนั้นและสร้างรถด้วย ที่นี่ค่าคงที่มีการตรวจสอบโดยปริยาย

วิธีนี้น่าสนใจเนื่องจากคุณได้รับเช็คฟรีและสอดคล้องกับภาษาที่แพร่หลาย Carไม่ได้ขับเคลื่อนด้วยแต่โดยdriverIdDriver

วิธีการนี้ในความเป็นจริงที่ใช้โดยจอห์นเวอร์นอนอยู่ในนั้นของเอกลักษณ์และการเข้าถึงบริบทตัวอย่างขอบเขตที่เขาผ่านUserรวมไปGroupรวม แต่ถือลงบนประเภทค่าGroup GroupMemberตามที่คุณเห็นสิ่งนี้ยังช่วยให้เขาสามารถตรวจสอบการเปิดใช้งานของผู้ใช้ (เราทราบดีว่าการตรวจสอบอาจค้างอยู่)

    public void addUser(User aUser) {
        //original code omitted
        this.assertArgumentTrue(aUser.isEnabled(), "User is not enabled.");

        if (this.groupMembers().add(aUser.toGroupMember()) && !this.isInternalGroup()) {
            //original code omitted
        }
    }

อย่างไรก็ตามโดยผ่านDriverอินสแตนซ์คุณยังเปิดตัวเองเพื่อปรับเปลี่ยนDriverภายในโดยไม่Carตั้งใจ การส่งผ่านค่าอ้างอิงช่วยให้เหตุผลเกี่ยวกับการเปลี่ยนแปลงจากมุมมองของโปรแกรมเมอร์ได้ง่ายขึ้น แต่ในเวลาเดียวกัน DDD นั้นเกี่ยวกับภาษา Ubiquitous ดังนั้นอาจคุ้มค่ากับความเสี่ยง

หากคุณสามารถสร้างชื่อที่ดีเพื่อใช้หลักการแยกส่วนต่อประสาน(ISP) ได้คุณอาจใช้อินเทอร์เฟซที่ไม่มีวิธีการเชิงพฤติกรรม คุณอาจจะได้แนวคิดเกี่ยวกับวัตถุค่าที่แสดงถึงการอ้างอิงไดรเวอร์ที่ไม่เปลี่ยนรูปแบบและสามารถสร้างอินสแตนซ์ได้จากไดร์เวอร์ที่มีอยู่เท่านั้น (เช่นDriverDescriptor driver = driver.descriptor())

ฉันสามารถจินตนาการได้ว่ามวลรวมเหล่านั้นอาจอาศัยอยู่ในบริบทที่มีขอบเขตแยกกันและตอนนี้ฉันจะทำให้มลภาวะทางรถยนต์ของ BC กับการพึ่งพาใน DriverRepository หรือเอนทิตีของผู้ขับขี่ของไดรเวอร์ BC

ไม่คุณไม่อยากทำ มีเลเยอร์ต่อต้านการคอร์รัปชั่นอยู่เสมอเพื่อให้แน่ใจว่าแนวคิดของบริบทหนึ่งจะไม่แยกออกไปอีก จริงๆแล้วมันง่ายกว่ามากถ้าคุณมี BC ที่อุทิศให้กับการเชื่อมโยงคนขับรถเพราะคุณสามารถสร้างแบบจำลองแนวคิดที่มีอยู่เช่นCarและDriverโดยเฉพาะสำหรับบริบทนั้น

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

โปรดทราบว่าบริการบนเว็บไม่จำเป็นต้องเป็นวิธีการเรียงลำดับที่ดีที่สุดระหว่าง BC นอกจากนี้คุณยังสามารถพึ่งพาการส่งข้อความที่ตัวอย่างเช่นUserCreatedข้อความจากบริบทการจัดการไดรเวอร์จะถูกใช้ในบริบทระยะไกลซึ่งจะเก็บการเป็นตัวแทนของไดรเวอร์ในฐานข้อมูลของตัวเอง ความDriverLookupServiceสามารถในการใช้ฐานข้อมูลนี้และข้อมูลของคนขับจะได้รับการอัพเดทพร้อมข้อความเพิ่มเติม (เช่นDriverLicenceRevoked)

ฉันไม่สามารถบอกคุณได้ว่าวิธีใดดีกว่าสำหรับโดเมนของคุณ แต่หวังว่าจะช่วยให้คุณมีข้อมูลเชิงลึกที่เพียงพอสำหรับการตัดสินใจ


3

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

อย่างไรก็ตามคุณต้องกังวลว่าไดรเวอร์ที่เกี่ยวข้องกับ driverId จะไม่ถูกลบก่อนที่รถจะถูกลบหรือให้ไดรเวอร์อื่น (และอาจเป็นไปได้ว่าคนขับไม่ได้ถูกมอบหมายให้กับรถคันอื่น เกี่ยวข้องกับรถยนต์หนึ่งคัน))

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

Btw ฉันเห็นด้วยกับ @PriceJones ว่าความสัมพันธ์ระหว่างรถยนต์กับคนขับอาจเป็นความรับผิดชอบที่แยกจากรถหรือคนขับ ความสัมพันธ์ชนิดนี้จะเติบโตในความซับซ้อนเมื่อเวลาผ่านไปเพราะฟังดูเป็นปัญหาการตั้งเวลา (ไดรเวอร์รถยนต์ช่วงเวลา / หน้าต่างทดแทน ฯลฯ ... ) แม้ว่ามันจะเหมือนปัญหาการลงทะเบียนมากขึ้น แต่ก็อาจต้องการประวัติศาสตร์ การลงทะเบียนเช่นเดียวกับการลงทะเบียนปัจจุบัน ดังนั้นมันอาจจะทำบุญ BC ของตัวเองได้เป็นอย่างดี

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

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

การกำหนดเวลารถยนต์และคนขับรถ BC นั้นจะจัดทำปฏิทินของไดรเวอร์ที่เกี่ยวข้องกับรถยนต์เป็นระยะเวลา / ระยะเวลาทั้งในปัจจุบันและอนาคตและแจ้งให้ทราบถึงการจัดสรรคืน BC อื่น ๆ เฉพาะเมื่อมีการใช้รถยนต์หรือไดรเวอร์ตามกำหนดเวลาครั้งสุดท้าย


ในฐานะที่เป็นโซลูชั่นที่รุนแรงกว่านี้คุณสามารถปฏิบัติต่อรถยนต์และคนขับรถ BC ในฐานะโรงงานบันทึกประวัติศาสตร์ที่ต่อท้ายเท่านั้นโดยคงความเป็นเจ้าของไว้ที่กำหนดการเชื่อมโยงรถยนต์ / ไดรเวอร์ รถ BC อาจสร้างรถใหม่พร้อมรายละเอียดทั้งหมดของรถพร้อมกับ VIN กรรมสิทธิ์ในรถยนต์นั้นได้รับการจัดการโดยกำหนดการเชื่อมโยงรถยนต์ / คนขับ แม้ว่าความสัมพันธ์ของรถยนต์ / คนขับจะถูกลบและตัวรถเองก็ถูกทำลายบันทึกของรถยนต์ยังคงมีอยู่ในรถบีซีตามคำจำกัดความและเราสามารถใช้รถบีซีเพื่อค้นหาข้อมูลทางประวัติศาสตร์; ในขณะที่สมาคมรถยนต์ / คนขับ / เจ้าของ (อดีตปัจจุบันและอนาคตที่กำหนด) จะถูกจัดการโดย BC อื่น


2

สมมติว่ารถยนต์รวมมีการอ้างอิงถึงไดรเวอร์รวม การอ้างอิงนี้จะเป็นแบบจำลองโดยมี Car.driverId

ใช่นั่นเป็นวิธีที่ถูกต้องในการรวมกันเป็นคู่

ถ้าฉันจะคุยกับผู้เชี่ยวชาญในโดเมนพวกเขาจะไม่ถามคำถามถึงความถูกต้องของข้อมูลอ้างอิง

ไม่ใช่คำถามที่เหมาะสมที่จะถามผู้เชี่ยวชาญด้านโดเมนของคุณ ลอง "สิ่งที่มีค่าใช้จ่ายสำหรับธุรกิจหากไม่มีไดรเวอร์อยู่?"

ฉันอาจจะไม่ใช้ DriverRepository เพื่อตรวจสอบ driverId ฉันจะใช้บริการโดเมนแทน ฉันคิดว่ามันจะทำงานได้ดีขึ้นในการแสดงเจตนา - ภายใต้หน้าปกบริการโดเมนยังคงตรวจสอบระบบการบันทึก

ดังนั้นสิ่งที่ชอบ

class DriverService {
    private final DriverRepository driverRepository;

    boolean doesDriverExist(DriverId driverId) {
        return driverRepository.exists(driverId);
    }
}

คุณสอบถามโดเมนเกี่ยวกับ driverId ที่จุดต่าง ๆ

  • จากไคลเอ็นต์ก่อนที่จะส่งคำสั่ง
  • ในแอปพลิเคชันก่อนส่งคำสั่งไปยังรุ่น
  • ภายในโมเดลโดเมนระหว่างการประมวลผลคำสั่ง

การตรวจสอบใด ๆ หรือทั้งหมดเหล่านี้สามารถลดข้อผิดพลาดในอินพุตของผู้ใช้ แต่พวกเขาทั้งหมดทำงานจากข้อมูลเก่า มวลรวมอื่น ๆ สามารถเปลี่ยนแปลงได้ทันทีหลังจากที่เราถามคำถาม ดังนั้นจึงมีอันตรายจากการลบ / ผลบวกปลอมอยู่เสมอ

  • ในรายงานข้อยกเว้นเรียกใช้หลังจากคำสั่งเสร็จสมบูรณ์

ที่นี่คุณยังคงทำงานกับข้อมูลเก่า (การรวมอาจเรียกใช้คำสั่งในขณะที่คุณกำลังเรียกใช้รายงานคุณอาจไม่สามารถดูการเขียนล่าสุดไปยังการรวมทั้งหมด) แต่การตรวจสอบระหว่างมวลรวมจะไม่สมบูรณ์แบบ (Car.create (คนขับ: 7) ทำงานในเวลาเดียวกับ Driver.delete (คนขับ: 7)) ดังนั้นสิ่งนี้จะช่วยให้คุณสามารถป้องกันความเสี่ยงได้อีกชั้นหนึ่ง


1
Driver.deleteไม่ควรมีอยู่ ฉันไม่เคยเห็นโดเมนที่จะถูกทำลายมวลรวม ด้วยการทำให้ ARs อยู่รอบตัวคุณจะไม่มีวันจบลงด้วยเด็กกำพร้า
plalx

1

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

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


ฉันคิดถึงความสัมพันธ์ของรถยนต์ / คนขับด้วยเช่นกัน - แต่การแนะนำการรวม DriverAssignment เพียงแค่เคลื่อนย้ายซึ่งการอ้างอิงจำเป็นต้องได้รับการตรวจสอบ
VoiceOfUnreason

1

แต่ตอนนี้ฉันสงสัยว่าการตรวจสอบไม่แปรปรวนมากเกินไปใช่ไหม

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

จากนั้นการออกแบบระดับทำให้ไม่จำเป็นด้วย

  • หากมีข้อกำหนด "รถยนต์ที่จอดอยู่อาจมีหรือไม่มีคนขับ"
  • หากวัตถุไดรเวอร์ต้องการDriverIdและมีการตั้งค่าในตัวสร้าง
  • หากCarความต้องการเท่านั้นที่DriverIdมีDriver.Idทะเยอทะยาน ไม่มีตัวตั้ง

ที่เก็บไม่ใช่ที่สำหรับกฎทางธุรกิจ

  • Carใส่ใจถ้ามันมีDriver(หรือบัตรประจำตัวของเขาอย่างน้อย) ใส่ใจถ้ามันมีDriver กังวลเกี่ยวกับความสมบูรณ์ของข้อมูลและไม่สามารถดูแลน้อยเกี่ยวกับรถยนต์ขับน้อยDriverIdRepository
  • DB จะมีกฎความสมบูรณ์ของข้อมูล คีย์ที่ไม่ใช่ null, ข้อ จำกัด ที่ไม่เป็นโมฆะ ฯลฯ แต่ความถูกต้องของข้อมูลเป็นเรื่องเกี่ยวกับ data / schema ของตารางไม่ใช่กฎทางธุรกิจ เรามีความสัมพันธ์อย่างรุนแรงและมีความสัมพันธ์ทางชีวภาพในกรณีนี้ แต่อย่าผสมทั้งสองอย่าง
  • ความจริงที่ว่าDriverIdเป็นสิ่งที่โดเมนธุรกิจได้รับการจัดการในชั้นเรียนที่เหมาะสม

การแยกความกังวลการละเมิด

... เกิดขึ้นเมื่อRepository.DriverIdExists()ถามคำถาม

สร้างวัตถุโดเมน ถ้าไม่ใช่วัตถุDriverอาจเป็นDriverInfoเพียงแค่DriverIdและNameสมมุติว่า ได้รับการDriverIdตรวจสอบเมื่อมีการก่อสร้าง มันจะต้องมีอยู่จริงและเป็นประเภทที่ถูกต้องและอย่างอื่น จากนั้นเป็นปัญหาการออกแบบคลาสไคลเอ็นต์วิธีจัดการกับ driver / driverId ที่ไม่มีอยู่จริง

บางทีเป็นเรื่องปกติไม่มีคนขับจนกว่าคุณจะเรียกCar Car.Drive()ในกรณีที่Carวัตถุของหลักสูตรมั่นใจสถานะของตนเอง ขับไม่ได้ถ้าไม่มีDriver- ดียังไม่ค่อยดี

การแยกคุณสมบัติออกจากคลาสของมันนั้นไม่ดี

แน่นอนว่ามีCar.DriverIdถ้าคุณต้องการ แต่ควรมีลักษณะเช่นนี้:

public class Car {
    // Non-null driver has a driverId by definition/contract.
    protected DriverInfo myDriver;
    public DriverId {get { return myDriver.Id; }}

    public void Drive() {
       if (myDriver == null ) return errorMessage; // or something
       // ... continue driving
    }
}

ไม่ใช่สิ่งนี้:

public class Car {
    public int DriverId {get; protected set;}
}

ตอนนี้Carต้องจัดการกับDriverIdปัญหาความถูกต้องทั้งหมด- การละเมิดหลักการความรับผิดชอบเดียว; และรหัสซ้ำซ้อนอาจ

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