REST API - DTOs หรือไม่? [ปิด]


154

ขณะนี้ฉันกำลังสร้าง REST-API สำหรับโครงการและได้อ่านบทความตามบทความเกี่ยวกับแนวทางปฏิบัติที่ดีที่สุด ดูเหมือนว่าหลายคนจะต่อต้าน DTO และเพียงแค่เปิดเผยรูปแบบโดเมนในขณะที่คนอื่น ๆ คิดว่า DTOs (หรือโมเดลผู้ใช้หรือสิ่งที่คุณต้องการเรียกใช้) เป็นแนวปฏิบัติที่ไม่ดี ส่วนตัวแล้วฉันคิดว่าบทความนี้มีเหตุผลมากมาย

อย่างไรก็ตามฉันยังเข้าใจข้อเสียของ DTOs ด้วยรหัสการแมปพิเศษทั้งหมดโมเดลโดเมนที่อาจเหมือน 100% กับ DTO-counterpart และอื่น ๆ

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

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

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


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

2
ลิงก์บทความ ( ibm.com/developerworks/community/blogs/barcia/entry/ ...... ) เสีย
pinkpanther

7
@pinkpanther เป็นบทความที่ยอดเยี่ยมและน่าเสียดายที่ไม่มีให้บริการอีกต่อไป นี่คือเว็บรุ่นเอกสารเก่าที่เก็บไว้ชั่วคราว
cassiomolin

คำตอบ:


252

ทำไมคุณควรใช้ DTO ใน REST API ของคุณ

DTOย่อมาจากD ATA T ransfer O bject

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

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

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


เพียงกล่าวถึงประโยชน์เล็กน้อยของการเปิดเผย DTOs แทนที่จะเป็นแบบจำลองการคงอยู่:

  • Decouple persistence models จากโมเดล API

  • DTO สามารถปรับให้เหมาะกับความต้องการของคุณและเป็นสิ่งที่ยอดเยี่ยมเมื่อเปิดเผยชุดของคุณลักษณะของเอนทิตีที่มีอยู่ของคุณเท่านั้น คุณไม่จำเป็นต้องมีคำอธิบายประกอบเช่น@XmlTransientและ@JsonIgnoreเพื่อหลีกเลี่ยงการทำให้เป็นอนุกรมของคุณลักษณะบางอย่าง

  • ด้วยการใช้ DTO คุณจะหลีกเลี่ยงคำอธิบายประกอบในหน่วยงานที่มีอยู่ของคุณนั่นคือหน่วยงานด้านการคงอยู่ของคุณจะไม่ได้รับการเปิดเผยข้อมูลที่เกี่ยวข้อง

  • คุณจะสามารถควบคุมคุณสมบัติทั้งหมดที่คุณได้รับเมื่อสร้างหรืออัปเดตทรัพยากร

  • หากคุณใช้Swaggerคุณสามารถใช้@ApiModelและเพิ่มความคิดเห็น@ApiModelPropertyเพื่อจัดทำเอกสารโมเดล API ของคุณโดยไม่ทำให้เอนทิตีการคงอยู่ของคุณ

  • คุณสามารถมี DTO ที่แตกต่างกันสำหรับ API แต่ละเวอร์ชันของคุณ

  • คุณจะมีความยืดหยุ่นมากขึ้นเมื่อทำแผนที่ความสัมพันธ์

  • คุณสามารถมี DTO ที่แตกต่างกันสำหรับประเภทสื่อที่แตกต่างกัน

  • DTOs ของคุณสามารถมีรายชื่อของการเชื่อมโยงสำหรับHATEOAS นั่นคือประเภทของสิ่งที่ไม่ควรถูกเพิ่มเข้าไปในวัตถุคงอยู่ เมื่อใช้Spring HATEOASคุณสามารถขยายคลาส DTO ของคุณRepresentationModel(ซึ่งรู้จักกันในชื่อเดิมResourceSupport) หรือล้อมรอบด้วยEntityModel(เคยเป็นที่รู้จักResource<T>)

การจัดการกับรหัสสำเร็จรูป

คุณไม่จำเป็นต้องแมปเอนทิตีการคงอยู่ของคุณกับDTOและในทางกลับกันด้วยตนเอง มีกรอบการแมปมากมายที่คุณสามารถใช้เพื่อทำมัน ตัวอย่างเช่นดูที่MapStructซึ่งเป็นคำอธิบายประกอบและทำงานเป็น Maven Annotation Processor มันทำงานได้ดีทั้งในแอพพลิเคชั่น CDI และ Spring

นอกจากนี้คุณยังอาจต้องการที่จะต้องพิจารณาลอมบอกเพื่อสร้าง getters, setters, equals(), hashcode()และtoString()วิธีการสำหรับคุณ


ที่เกี่ยวข้อง:เพื่อให้ชื่อที่ดีขึ้นสำหรับคลาส DTO ของคุณอ้างอิงคำตอบนี้


2
ถ้าฉันไปทาง DTO คุณจะแมปวัตถุโดเมนทั้งหมดกับ DTO หรือเฉพาะที่ไม่เหมือนกันหรือไม่ นอกจากนี้คุณจะแก้ไขปัญหาของการเปิดเผยข้อมูลตามสถานการณ์ / บริบทที่แตกต่างกันอย่างไร หลาย DTO ต่อวัตถุโดเมน?
benbjo

6
@benbjo มันขึ้นอยู่กับคุณ ฉันมักจะแมปเอนทิตีที่ซับซ้อนที่สุดกับ DTO เท่านั้นเอนทิตีที่ฉันไม่ต้องการให้เปิดเผยคุณลักษณะทั้งหมดและเอนทิตีที่มีความสัมพันธ์มากมาย DTO ให้ความยืดหยุ่นแก่ฉันในการมีรายการลิงก์ที่จะใช้ใน HATEOAS นั่นคือสิ่งที่ฉันจะไม่เพิ่มเข้าไปในวัตถุติดตาของฉัน
cassiomolin

2
@molin ขอบคุณมากสำหรับข้อมูลและคำแนะนำ ฉันจะตรวจสอบ MapStruct อย่างแน่นอน ได้อย่างรวดเร็วดูเหมือนว่าจะเหมาะกับความต้องการของฉันเป็นอย่างดี
benbjo

6
เรียนผู้ลงคะแนนต่ำอย่างน้อยคุณสามารถอธิบายเหตุผลของการลงคะแนนเสียงของคุณได้หรือไม่?
cassiomolin

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

25

เมื่อ API ของคุณเป็นแบบสาธารณะและคุณต้องรองรับหลายเวอร์ชันคุณต้องไปที่ DTO

ในทางกลับกันถ้าเป็น API ส่วนตัวและคุณสามารถควบคุมทั้งไคลเอนต์และเซิร์ฟเวอร์ฉันมักจะข้าม DTOs และเปิดเผยโมเดลโดเมนโดยตรง


ฉันเห็นด้วยกับคุณในส่วนสุดท้ายและฉันมักจะทำเช่นนี้ แต่นี่เป็น API สาธารณะครั้งแรกของฉัน ฉันจะพิจารณาสิ่งที่คุณพูดเกี่ยวกับการใช้ DTO เพื่อส่วนสาธารณะ บางทีส่วนที่เป็นส่วนตัวและสาธารณะของ API ควรแยกจากกันแม้ว่า "กิน dogfood ของคุณเอง" เป็นหลักการที่ดี
benbjo

11

ฉันมักจะใช้ DTO

ฉันไม่ชอบข้อเสีย แต่ดูเหมือนว่าตัวเลือกอื่น ๆ จะยิ่งแย่กว่านี้:

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

ในอีกด้านหนึ่งข้อเสียของวิธีการ DTO สามารถลดลงได้ด้วยสิ่งต่าง ๆ เช่นการทำแผนที่วัตถุกับวัตถุและลอมบอกสำหรับหม้อไอน้ำที่น้อยกว่า


9

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

นี่เป็นเรื่องจริงสำหรับด้านการตอบสนองของ json / rest api ฉันยังเขียน addon แจ็คสันเพื่อหลีกเลี่ยงการเขียนจำนวนการดู / ตัวกรอง json จำนวนมากสำหรับกรณีเหล่านี้: https://github.com/Antibrumm/jackson-antpathfilter

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


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