การใช้ DTO คืออะไรแทนที่จะใช้ Entity


18

ฉันกำลังทำงานกับแอปพลิเคชัน RCP ฉันยังใหม่กับแอปพลิเคชันนี้

Spring beans ใช้เพื่อเขียนตรรกะทางธุรกิจเพื่อบันทึก / ดึงเอนทิตี

แต่แทนที่จะส่งเอนทิตีโดยตรงไปยังไคลเอนต์เรากำลังแปลงเป็น DTOsและเติมลูกค้า ในขณะที่ประหยัดเรากำลังแปลง DTO เป็นเอนทิตีและประหยัดอีกครั้ง

ประโยชน์ของการแปลงเหล่านี้คืออะไร มีคนอธิบายได้ไหม


What's the benefit of these conversions?แยกตัวแบบข้อมูลการคงอยู่จากตัวแบบข้อมูล (การเป็นตัวแทน) ที่เสนอให้กับผู้บริโภค ประโยชน์ของ decoupling ถูกกล่าวถึงอย่างกว้างขวางใน SE อย่างไรก็ตามจุดมุ่งหมายที่อยู่ภายใต้ DTOs คือการรวบรวมในการตอบกลับข้อมูลเดียวเท่าที่จำเป็นสำหรับลูกค้าเพื่อบันทึกการโทรไปยังเซิร์ฟเวอร์ สิ่งที่ทำให้การสื่อสารกับไคลเอนต์เซิร์ฟเวอร์ราบรื่นขึ้น
Laiv


ตัวอย่างของคุณดี เมื่อคุณเป็นลูกค้า (มุมมอง ... ) เจ็บปวดต่อการเปลี่ยนแปลง แต่ปัญหาที่ใหญ่ที่สุดคือเมื่อระบบมีการรวมกลุ่มของบุคคลที่สามอยู่แล้วนั่นเป็นไปไม่ได้ที่จะเปลี่ยนแปลง (สัญญาค่าธรรมเนียม) ... หากระบบของคุณจะมีการรวมบุคคลที่สามให้ใช้ DTO
Lucas Gonçalves

คำตอบ:


45

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


ตัวอย่างทั้งหมดจะขึ้นอยู่กับรูปแบบข้อมูลที่เรียบง่ายนี้:

Personนิติบุคคลที่มีห้าคุณสมบัติ:Id, FirstName, LastName, Age, CityId

และคุณสามารถสันนิษฐานได้ว่าแอปพลิเคชันใช้ข้อมูลนี้ในหลากหลายวิธี (รายงาน, แบบฟอร์ม, ป๊อปอัป, ... )

แอปพลิเคชันทั้งหมดมีอยู่แล้ว ทุกสิ่งที่ฉันพูดถึงคือการเปลี่ยนแปลงรหัสฐานที่มีอยู่ นี่เป็นสิ่งสำคัญที่ต้องจำ


ตัวอย่างที่ 1 - การเปลี่ยนแปลงโครงสร้างข้อมูลพื้นฐาน - ไม่มี DTO

ข้อกำหนดมีการเปลี่ยนแปลง อายุของบุคคลนั้นต้องได้รับการดึงจากฐานข้อมูลของรัฐบาลแบบไดนามิก (ลองสมมติตามชื่อและนามสกุล)

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

แต่ส่วนหน้าของคุณยังคงแสดงอายุ มุมมองทั้งหมดได้รับการตั้งค่าให้ใช้Person.Ageคุณสมบัติซึ่งตอนนี้ไม่มีอยู่อีกต่อไป นำเสนอปัญหาของตัวเอง: มุมมองทั้งหมดที่อ้างถึงAgeของความต้องการคนที่จะได้รับการแก้ไข


ตัวอย่างที่ 2 - การเปลี่ยนแปลงโครงสร้างข้อมูลพื้นฐาน - ด้วย DTO

ในระบบเก่านอกจากนี้ยังมีกิจการที่เดียวกับที่ห้าคุณสมบัติ:PersonDTO Id, FirstName, LastName, Age, CityIdหลังจากเรียก a Personแล้วเลเยอร์บริการจะแปลงเป็น a PersonDTOและจากนั้นคืนค่า

แต่ตอนนี้ความต้องการมีการเปลี่ยนแปลง อายุของบุคคลนั้นต้องได้รับการดึงจากฐานข้อมูลของรัฐบาลแบบไดนามิก (ลองสมมติตามชื่อและนามสกุล)

เนื่องจากคุณไม่จำเป็นต้องเก็บAgeค่าไว้ในเครื่องอีกต่อไปดังนั้นจึงต้องลบออกจากPersonเอนทิตี สิ่งสำคัญคือที่นี่เพื่อทราบว่าเอนทิตีแสดงข้อมูลฐานข้อมูลและไม่มีอะไรเพิ่มเติม ถ้ามันไม่ได้อยู่ในฐานข้อมูลมันไม่ได้อยู่ในเอนทิตี

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

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

นี่คือสิ่งที่เราหมายถึงเมื่อเราใช้การแยกวลีของข้อกังวล : มีสองข้อกังวลที่แตกต่างกัน (การเก็บข้อมูลในฐานข้อมูลการนำเสนอข้อมูลไปยังส่วนหน้า) และพวกเขาต้องการชนิดข้อมูลที่แตกต่างกัน แม้ว่าข้อมูลทั้งสองประเภทนั้นจะมีข้อมูลเดียวกันอยู่ในขณะนี้ แต่อาจมีการเปลี่ยนแปลงในอนาคต
ในตัวอย่างที่ระบุAgeความแตกต่างระหว่างสองชนิดข้อมูล: Person(เอนทิตีฐานข้อมูล) ไม่จำเป็นต้องมีAgeแต่PersonDTO((ชนิดข้อมูลส่วนหน้า) ไม่จำเป็นต้องใช้
ด้วยการแยกข้อกังวล (= การสร้างชนิดข้อมูลที่แยกต่างหาก) จากจุดเริ่มต้นทำให้ codebase มีความยืดหยุ่นมากขึ้นต่อการเปลี่ยนแปลงที่เกิดขึ้นกับตัวแบบข้อมูล

คุณอาจยืนยันว่าการมีวัตถุ DTO เมื่อมีการเพิ่มคอลัมน์ใหม่ลงในฐานข้อมูลหมายความว่าคุณต้องทำงานซ้ำซ้อนเพิ่มคุณสมบัติในเอนทิตีและ DTO นั่นคือเทคนิคที่ถูกต้อง ต้องใช้ความพยายามพิเศษเล็กน้อยในการรักษาสองคลาสแทนหนึ่ง

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


ฉันสามารถยกตัวอย่างให้คุณได้มากกว่า แต่หลักการจะเหมือนกันเสมอ

เพื่อสรุป

  • ความรับผิดชอบแยกต่างหาก (ข้อกังวล) จำเป็นต้องทำงานแยกจากกัน พวกเขาไม่ควรแบ่งปันทรัพยากรใด ๆ เช่นคลาสข้อมูล (เช่นPerson)
  • เพียงเพราะเอนทิตีและ DTO มีคุณสมบัติเหมือนกันไม่ได้หมายความว่าคุณต้องรวมมันเข้ากับเอนทิตีเดียวกัน อย่าตัดมุม
    • เป็นตัวอย่างที่ชัดเจนมากขึ้นสมมติว่าฐานข้อมูลของเรามีประเทศเพลงและผู้คน Nameทั้งหมดของหน่วยงานเหล่านี้มี แต่เพียงเพราะพวกเขาทุกคนมีNameคุณสมบัติไม่ได้หมายความว่าเราควรทำให้พวกเขาสืบทอดจากEntityWithNameชั้นฐานที่ใช้ร่วมกัน Nameคุณสมบัติที่แตกต่างไม่มีความสัมพันธ์ที่มีความหมาย
    • หนึ่งควรของคุณสมบัติที่เคยเปลี่ยนแปลง (เช่นเพลงที่Nameได้รับการเปลี่ยนชื่อTitleหรือบุคคลที่ได้รับFirstNameและLastName) พวกคุณจะต้องใช้ความพยายามมากขึ้นในการยกเลิกมรดกที่คุณไม่จำเป็นต้องแม้จะอยู่ในสถานที่แรก
    • แม้ว่าจะไม่ดังเกินไปการโต้แย้งของคุณว่าคุณไม่ต้องการ DTO เมื่อคุณมีเอนทิตีเหมือนกัน คุณกำลังดูอยู่ตอนนี้แต่คุณไม่ได้เตรียมพร้อมสำหรับการเปลี่ยนแปลงในอนาคต หากเอนทิตีและ DTO เหมือนกันทุกประการและหากคุณรับประกันได้ว่าจะไม่มีการเปลี่ยนแปลงใด ๆ กับตัวแบบข้อมูล จากนั้นคุณถูกต้องที่คุณสามารถละเว้น DTO แต่สิ่งหนึ่งคือคุณไม่สามารถรับประกันได้ว่ารูปแบบข้อมูลจะไม่เปลี่ยนแปลง
  • แนวปฏิบัติที่ดีไม่ได้จ่ายทันทีเสมอ มันอาจเริ่มชำระในอนาคตเมื่อคุณต้องการกลับไปที่แอปพลิเคชันเก่า
  • นักฆ่าหลักของฐานรหัสที่มีอยู่กำลังปล่อยให้คุณภาพของรหัสลดลงทำให้การรักษา codebase นั้นยากขึ้นเรื่อย ๆ จนกว่ามันจะกลายเป็นความยุ่งเหยิงของรหัสสปาเก็ตตี้ที่ไร้ประโยชน์
  • แนวปฏิบัติที่ดีเช่นการแยกข้อกังวลจากจุดเริ่มต้นไปยังจุดมุ่งหมายเพื่อหลีกเลี่ยงความลาดชันที่ลื่นไหลของการบำรุงรักษาที่ไม่ดีเพื่อรักษา codebase ให้คงอยู่ได้นานที่สุด

เป็นกฎง่ายๆที่จะพิจารณาแยกข้อกังวลคิดในลักษณะนี้:

สมมติว่าทุกข้อกังวล (UI, ฐานข้อมูล, ตรรกะ) ได้รับการจัดการโดยบุคคลที่แตกต่างกันในตำแหน่งที่ตั้งอื่น พวกเขาสามารถสื่อสารทางอีเมลเท่านั้น

ใน codebase ที่แยกออกจากกันการเปลี่ยนแปลงข้อกังวลเฉพาะจะต้องได้รับการจัดการโดยบุคคลเดียวเท่านั้น:

  • การเปลี่ยนอินเทอร์เฟซผู้ใช้เกี่ยวข้องกับ UI dev เท่านั้น
  • การเปลี่ยนวิธีการจัดเก็บข้อมูลเกี่ยวข้องกับ dev ฐานข้อมูลเท่านั้น
  • การเปลี่ยนตรรกะทางธุรกิจจะเกี่ยวข้องกับการพัฒนาธุรกิจเท่านั้น

หากนักพัฒนาเหล่านี้ใช้Personเอนทิตีเดียวกันและมีการเปลี่ยนแปลงเล็กน้อยกับเอนทิตีทุกคนจะต้องมีส่วนร่วมในกระบวนการ

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

  • ตราบใดที่ฐานข้อมูล dev สามารถส่งคืนPersonDTOวัตถุที่ถูกต้องได้ธุรกิจและ UI dev ไม่สนใจว่าเขาจะเปลี่ยนแปลงวิธีการจัดเก็บ / ดึงข้อมูล
  • ตราบใดที่ธุรกิจ dev เก็บข้อมูลในฐานข้อมูลและให้ข้อมูลที่จำเป็นแก่ส่วนหน้าฐานข้อมูลและ UI devs ไม่สนใจว่าเขาตัดสินใจที่จะทำใหม่กฎทางธุรกิจของเขา
  • ตราบใดที่ UI สามารถออกแบบได้ตาม `PersonViewModel 'UI ก็จะสามารถสร้าง UI ได้ตามที่พวกเขาต้องการ ฐานข้อมูลและธุรกิจ devs ไม่สนใจว่ามันจะทำอย่างไรเพราะมันไม่ได้ส่งผลกระทบต่อพวกเขา

วลีที่สำคัญที่นี่เป็นเพราะมันไม่ได้ส่งผลกระทบต่อพวกเขา การดำเนินการแยกความกังวลที่ดีพยายามลดผลกระทบ (และต้องมีส่วนร่วม) บุคคลอื่น ๆ

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


คำตอบที่ครอบคลุมขอบคุณ ฉันมีคำถาม ขอบคุณถ้าคุณตอบ: 1 - แก้ไขให้ฉันถ้าฉันผิด วัตถุธุรกิจหรือดูวัตถุสำหรับการถ่ายโอนข้อมูลระหว่างการนำเสนอชั้นและชั้นธุรกิจและEntity วัตถุสำหรับการถ่ายโอนข้อมูลระหว่างชั้นธุรกิจและชั้นการเข้าถึงข้อมูล และ DTO สามารถใช้เป็น BO แบบโง่ได้ 2 - สมมติว่าสองมุมมองต้องการข้อมูลที่แตกต่างกันของ บริษัท แล้วเราต้องการสอง บริษัท ที่แตกต่างกัน DTO?
Arash

1
@Arash (1) "DTO" เป็นคำจำกัดความ catch-all สำหรับคลาสข้อมูลใด ๆ ที่ใช้สำหรับการแลกเปลี่ยนระหว่างสองเลเยอร์ วัตถุธุรกิจและวัตถุมุมมองเป็นทั้ง DTOs (2) นั้นขึ้นอยู่กับหลาย ๆ อย่าง การสร้าง dto ใหม่สำหรับทุกคอลเลกชันของฟิลด์ที่คุณต้องการนั้นเป็นงานที่ยุ่งยาก ไม่มีอะไรผิดปกติที่เกิดขึ้นเพียงแค่ส่งคืน บริษัท DTO เต็มรูปแบบ (ในกรณีที่เหมาะสม) จากนั้นให้มุมมองเลือกสาขาที่ตนสนใจมันเป็นเรื่องของการค้นหาความสมดุลระหว่างการแยกความกังวลอย่างเพียงพอและหลีกเลี่ยงการทำซ้ำซ้อน
Flater

ตอนนี้ที่เหมาะสมกับฉัน ขอบคุณมาก. Flater
Arash

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

@Arash: ความแตกต่างระหว่างสิ่งที่คุณเรียกว่า "วัตถุธุรกิจ" และ "มุมมองวัตถุ" เป็นบริบท สำหรับพวกเราแล้วความแตกต่างนั้นสำคัญที่จะต้องเข้าใจสิ่งต่าง ๆ แต่คอมไพเลอร์ (และโดยการขยายภาษาเอง) ไม่เห็นความแตกต่างทางเทคนิคระหว่างพวกเขา เมื่อฉันบอกว่าพวกเขาเหมือนกันฉันหมายความว่าจากมุมมองทางเทคนิค ทั้งสองเป็นเพียงคลาสที่มีคุณสมบัติเพื่อเก็บข้อมูลและส่งต่อไป ในเรื่องนั้นไม่มีความแตกต่างระหว่างพวกเขา
Flater
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.