ข้อยกเว้นใน DDD


11

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

public function doIt(UserData $userData)
{
    $user = $this->userRepository->byEmail($userData->email());
    if ($user) {
        throw new UserAlreadyExistsException();
    }

    $this->userRepository->add(
        new User(
            $userData->email(),
            $userData->password()
        )
    );
}

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

วิธีที่ดีที่สุดสำหรับสิ่งนี้คืออะไร?


1
การมีผู้ให้บริการโดเมน (ตัวจัดการ) การยกเว้นเป็นเรื่องปกติ
Andy

คำตอบ:


20

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

แบบจำลองโลหิตจางสามารถสร้างความรู้สึกได้อย่างมากมายสำหรับนักพัฒนาที่เพิ่งเริ่มใช้ DDD คุณสร้างUserแบบจำลองและแบบEmailMustBeUnqiueRuleที่ได้รับการฉีดด้วยข้อมูลที่จำเป็นในการตรวจสอบอีเมล ง่าย สง่า ปัญหาคือว่า "ชนิด" ความคิดนี้เป็นขั้นตอนพื้นฐานในธรรมชาติ ไม่ใช่ DDD สิ่งที่เกิดขึ้นคือคุณถูกทิ้งให้อยู่กับโมดูลที่Rulesห่อหุ้มและห่อหุ้มอย่างเรียบร้อยหลายสิบครั้ง แต่สิ่งเหล่านี้ปราศจากบริบทโดยสิ้นเชิงจนถึงจุดที่ไม่สามารถเปลี่ยนได้อีกต่อไปเพราะมันไม่ชัดเจนเมื่อมีการบังคับใช้ มันสมเหตุสมผลไหม มันอาจจะเห็นได้ชัดว่าตัวเอง EmailMustBeUnqiueRuleจะถูกนำไปใช้ในการสร้างของUserแต่สิ่งที่เกี่ยวกับUserIsInGoodStandingRule? อย่างช้า ๆ แต่แน่นอนการแยกเป็นเมล็ดของRulesจากบริบทของพวกเขาทำให้คุณมีระบบที่เข้าใจยาก (และไม่สามารถเปลี่ยนแปลงได้) ควรห่อหุ้มกฎเฉพาะเมื่อการกระทืบ / การประมวลผลที่แท้จริงเกิดขึ้นจริงดังนั้นรูปแบบของคุณจะเริ่มหลวมโฟกัส

ต่อไปนี้สำหรับคำถามเฉพาะของคุณ: ปัญหาของการมีService/ การCommandHandlerโยนExceptionคือว่าตรรกะทางธุรกิจของคุณเริ่มรั่ว ("ขึ้นไป") ออกจากโดเมนของคุณ ทำไมService/ CommandHandlerจำเป็นต้องรู้อีเมลของคุณจะต้องไม่ซ้ำกัน? เลเยอร์บริการแอปพลิเคชันโดยทั่วไปจะใช้สำหรับการประสานงานมากกว่าการใช้งาน เหตุผลนี้สามารถแสดงให้เห็นได้ง่าย ๆ ถ้าเราเพิ่มChangeEmailเมธอด / คำสั่งลงในระบบของคุณ ตอนนี้ทั้งสองวิธี / ตัวจัดการคำสั่งจะต้องรวมการตรวจสอบที่ไม่ซ้ำกันของคุณ นี่คือที่นักพัฒนาอาจถูกล่อลวงให้ "แยก" EmailMustBeUniqueRuleและ ตามที่อธิบายไว้ข้างต้นเราไม่ต้องการไปเส้นทางนั้น

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

สำหรับกรณีนี้โดยเฉพาะ (และอื่น ๆ อีกมากมายที่เกี่ยวข้องกับการบังคับใช้ค่าคงที่ทั่วทั้งคอลเลกชัน) Repositoryสถานที่ที่ดีที่สุดที่จะใช้ตรรกะนี้จะอยู่ในของคุณ สิ่งนี้สะดวกสบายเป็นพิเศษเพราะRepository"รู้" ของคุณเป็นส่วนเสริมของโครงสร้างพื้นฐานที่จำเป็นในการดำเนินการตรวจสอบความถูกต้อง (แหล่งข้อมูล) ในกรณีของคุณฉันจะทำการตรวจสอบนี้ในaddวิธีการ มันสมเหตุสมผลใช่ไหม? แนวคิดนี้เป็นวิธีการที่เพิ่มUserระบบของคุณอย่างแท้จริง แหล่งข้อมูลเป็นรายละเอียดการนำไปปฏิบัติ


ฉันขอถามคุณหมายถึงMoving rules "upwards" is generally a sign of an anemic modelอะไร ขึ้นไปด้านบนหมายถึงเลเยอร์ของแอปพลิเคชัน? หรือรูปแบบโดเมน?
Anyname Donotcare

1
“ ขึ้นไป” หมายถึงแอพพลิเคชั่นเลเยอร์ของคุณ (คิดว่าเป็นสถาปัตยกรรมแบบเลเยอร์) ฉันได้สัมผัสถึงสิ่งนี้ด้านบน แต่เหตุผลที่ทำให้กฏการเคลื่อนที่สูงขึ้นเป็นสัญญาณของแบบจำลองโลหิตจางคือมันหมายถึงการแยกข้อมูลและพฤติกรรมในแบบที่เอาชนะจุดประสงค์ทั้งหมดของการมีโมเดลโดเมนหลัก หากการตรวจสอบความถูกต้องของอีเมลอยู่ในโมดูลที่แยกจากกันมากกว่าการส่งอีเมลEmailแบบจำลองของคุณจะต้องเป็นถุงข้อมูลที่ตรวจสอบค่าคงที่ก่อนส่ง ดูที่: softwareengineering.stackexchange.com/questions/372338/…
สไลด์ King-side

2
การย้าย Domain Logic ไปยัง Repository ผิด คุณควรบังคับใช้กฎของโดเมนโดยรวมรากในเลเยอร์โดเมน
Arash

1
การตรวจสอบ @Arash Set นั้นยุ่งยากและมักจะเล่นได้ไม่ดีกับ DDD คุณเหลือที่เลือกที่จะตรวจสอบว่ากระบวนการบางอย่างจะทำงานก่อนที่จะลองใช้กระบวนการ (เพิ่ม a User) หรือยอมรับว่าค่าคงที่ประเภทนี้จะไม่ถูกห่อหุ้มอย่างเป็นระเบียบในโดเมนของคุณ อดีตแสดงถึงการแยกของข้อมูลและพฤติกรรมที่เกิดขึ้นในกระบวนทัศน์กระบวนงาน นี่มันน้อยกว่าอุดมคติ การตรวจสอบควรมีอยู่กับข้อมูลที่ไม่รอบข้อมูล นอกจากนี้มีประโยชน์อย่างไรในการสร้างบริการโดเมนที่ให้บริการเพื่อตรวจสอบกฎนี้เท่านั้น เห็นได้ชัดว่าบริการนี้ ...
สไลด์ด้านข้างกษัตริย์

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

0

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

ตัวอย่างเช่นเอนทิตีใช้วิธีการที่แสดงถึงกฎเกณฑ์ทางธุรกิจในขณะที่ use-cases ใช้กฎเฉพาะสำหรับแอปพลิเคชันของคุณ

หากการสร้างบริการอีเมลของคุณเหมือนกับ Gmail อาจให้เหตุผลว่ากฎ "ผู้ใช้ควรมีอีเมลที่ไม่ซ้ำ" เป็นกฎทางธุรกิจ

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

เพื่อความปลอดภัย aditional คุณสามารถบังคับใช้กฎนี้ในที่เก็บของคุณ

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