เมื่อใดก็ตามที่นักพัฒนาถามว่า "อะไรคือจุดประสงค์ของการทำเช่นนี้" สิ่งที่พวกเขาหมายถึงจริงๆคือ "ฉันไม่เห็นกรณีใช้งานที่ทำสิ่งนี้ให้ประโยชน์" ด้วยเหตุนี้ฉันขอยกตัวอย่างให้คุณดู
ตัวอย่างทั้งหมดจะขึ้นอยู่กับรูปแบบข้อมูลที่เรียบง่ายนี้:
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 ไม่สนใจว่ามันจะทำอย่างไรเพราะมันไม่ได้ส่งผลกระทบต่อพวกเขา
วลีที่สำคัญที่นี่เป็นเพราะมันไม่ได้ส่งผลกระทบต่อพวกเขา การดำเนินการแยกความกังวลที่ดีพยายามลดผลกระทบ (และต้องมีส่วนร่วม) บุคคลอื่น ๆ
แน่นอนการเปลี่ยนแปลงที่สำคัญบางอย่างไม่สามารถหลีกเลี่ยงการรวมมากกว่าหนึ่งคนได้เช่นเมื่อมีการเพิ่มเอนทิตีใหม่ทั้งหมดในฐานข้อมูล แต่อย่าดูถูกดูแคลนจำนวนการเปลี่ยนแปลงเล็กน้อยที่คุณต้องทำในระหว่างอายุการใช้งานของแอปพลิเคชัน การเปลี่ยนแปลงที่สำคัญคือจำนวนน้อย
What's the benefit of these conversions?
แยกตัวแบบข้อมูลการคงอยู่จากตัวแบบข้อมูล (การเป็นตัวแทน) ที่เสนอให้กับผู้บริโภค ประโยชน์ของ decoupling ถูกกล่าวถึงอย่างกว้างขวางใน SE อย่างไรก็ตามจุดมุ่งหมายที่อยู่ภายใต้ DTOs คือการรวบรวมในการตอบกลับข้อมูลเดียวเท่าที่จำเป็นสำหรับลูกค้าเพื่อบันทึกการโทรไปยังเซิร์ฟเวอร์ สิ่งที่ทำให้การสื่อสารกับไคลเอนต์เซิร์ฟเวอร์ราบรื่นขึ้น