เป็นการดีที่จะใช้วัตถุเอนทิตีเป็นวัตถุการถ่ายโอนข้อมูลหรือไม่


29

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

ฉันใช้วัตถุเอนทิตีที่ฉันสร้างขึ้นด้วยกรอบงานเอนทิตี


3
ฉันคิดว่านี่เป็นคำถามที่ดี แต่ฉันไม่สามารถบอกได้จริงๆเพราะอ่านยากฉันไม่แน่ใจว่าจะแก้ไขได้อย่างไร โปรดแก้ไขคำถามของคุณ
candied_orange

1
@CandiedOrange +1 และมันทำให้ทุกอย่างน่ากลัวยิ่งขึ้นว่านี่มี upvotes มากมาย
guillaume31

ที่เกี่ยวข้อง: softwareengineering.stackexchange.com/questions/373284/…
Dherik

คำตอบ:


23

มันขึ้นอยู่กับคุณ.

คนส่วนใหญ่จะบอกคุณว่ามันไม่ใช่วิธีปฏิบัติที่ดี แต่คุณสามารถหลีกเลี่ยงได้ในบางกรณี

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

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

ตัวอย่างเช่นสมมติว่าคุณมีคลาสที่ถูกเรียกPersonพร้อมกับคำจำกัดความต่อไปนี้:

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; get; }

    // plus a bunch of other properties relevant about a person
}

สมมติว่าคุณต้องการที่จะแสดงรายชื่อของพนักงานบางแห่งก็อาจจะเป็นในทางปฏิบัติที่จะส่งเพียงId, และFirstName LastNameแต่คุณจะต้องส่งมอบคุณสมบัติที่ไม่เกี่ยวข้องอื่น ๆ ทั้งหมด ไม่ใช่เรื่องใหญ่หากคุณไม่สนใจขนาดของการตอบกลับ แต่แนวคิดทั่วไปคือการส่งข้อมูลที่เกี่ยวข้อง ในทางกลับกันคุณอาจออกแบบ API ที่ส่งคืนรายชื่อบุคคลและในกรณีนั้นการส่งคุณสมบัติทั้งหมดอาจจำเป็นต้องใช้ดังนั้นจึงควรทำให้ซีเรียลไลซ์และส่งเอนทิตี ในกรณีนี้การสร้างคลาส DTO นั้นเป็นที่ถกเถียงกัน บางคนชอบผสมเอนทิตีและ DTO บางคนทำไม่ได้

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


1
สรุปโดยย่อ อะไรคือความแตกต่างระหว่าง DTO และ POCO พวกเขาไม่เพียงแค่เก็บข้อมูลหรือไม่
Laiv

1
@ guillaume31 คุณไม่สามารถมีรูปแบบโดเมนที่แท้จริงได้โดยไม่ต้องคำนึงถึงเรื่องนี้ อะไรคืออินเทอร์เฟซสาธารณะที่ดีถ้าฉันถูกบังคับให้ใช้การไตร่ตรอง (ไม่ต้องพูดถึงแฮ็กประเภทอื่น ๆ โดยมีเงื่อนไขว่าทรัพย์สินสาธารณะของฉันได้รับการสนับสนุนโดยเขตข้อมูลส่วนตัวที่ชื่อแตกต่างกัน) สำหรับการโหลดมวลรวมของฉัน หาก EF ไม่ได้ให้โครงสร้างพื้นฐานแก่ฉันในการซ่อนการรวมตัวของฉันโดยใช้ส่วนต่อประสานสาธารณะของพวกเขาฉันควรจะรักษาตรรกะทางธุรกิจของฉันไว้ที่อื่นและให้มีโดเมนโลหิตจางแทนการเขียนแผ่นข้อมูลเพิ่มเติมสำหรับการโหลดจากฐานข้อมูล
devnull

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

1
@ Konrad จากที่นี่ : As of EF Core 2.1, only services known by EF Core can be injected. Support for injecting application services is being considered for a future release.ฉันชอบที่จะให้บริการแอปพลิเคชันฉีดเข้าไปในเอนทิตีของฉัน
devnull

1
@ Konrad ฉันจะหยุดใช้เอนทิตีโดเมนของฉันเป็น DTO (ถ้าฉันจะใช้พวกเขาด้วยวิธีนี้) DTOs นั้นมีประโยชน์โดยไม่คำนึงถึงการสนับสนุนของ EF สำหรับการให้บริการฉีด
devnull

8

ไม่มันไม่ใช่.

ตามหลักการแล้ว DTO จะตรงกับที่เก็บข้อมูลของคุณ (เช่นตารางฐานข้อมูลของคุณ)

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

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


คำสั่งวัตถุเป็น DTO ว่าชั้นธุรกิจควรรู้อะไรเกี่ยวกับ
devnull

ฉันคิดว่าเลเยอร์ธุรกิจอาจเรียกวัตถุคำสั่งทางอ้อมผ่านอินเตอร์เฟส และฉันหมายถึง DTO ในวัตถุง่าย ๆ ที่มีข้อมูลโดยไม่มีฟังก์ชั่นเลย ฉันไม่แน่ใจว่าวัตถุคำสั่งเป็น DTO ( martinfowler.com/eaaCatalog/dataTransferObject.html ) หรือไม่
Juan Carlos Eduardo Romaina Ac

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

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

4

จริงๆแล้วมันเป็นความคิดที่แย่มาก ๆ ฟาวเลอร์มาร์ตินมีบทความเกี่ยวกับ DTOs

เรื่องสั้นที่มีความยาวDTOรูปแบบถูกใช้สำหรับการถ่ายโอนข้อมูลนอกกระบวนการตัวอย่างเช่นบนเส้นลวดและไม่ใช่ระหว่างเลเยอร์ภายในกระบวนการเดียวกัน


เช่นเดียวกับวิธีการเขียนโปรแกรมทั้งหมดไม่มีขนาดที่เหมาะกับทุกอย่างมิฉะนั้นเราจะไม่อภิปรายและอภิปรายรูปแบบและวิธีการที่แตกต่างกันในการสร้างแอปทุกครั้งที่เราสร้างเรามักจะใช้สิ่งเดียวกันเสมอ DTO นั้นเป็นเพียงแค่ POPO เท่านั้นพวกมันถูกตั้งชื่อเพื่อแสดงเจตนาบางอย่าง ไม่มีอะไรที่ใช้ DTO เพื่อ "ถ่ายโอนข้อมูล" ระหว่างบริการ, คอนโทรลเลอร์, และมุมมอง ไม่มีสิ่งใดในชื่อหรือโครงสร้างของคลาสที่ระบุหรือ จำกัด การถ่ายโอนจากที่ไหนและไปยังข้อมูล
James

4

ไม่มันเป็นการปฏิบัติที่ไม่ดี

เหตุผลบางอย่าง:

  1. สาขาองค์กรใหม่จะอยู่ใน Dto โดยค่าเริ่มต้น ใช้เอนทิตีหมายความว่าทุกข้อมูลจะพร้อมใช้งานโดยค่าเริ่มต้น นี่อาจทำให้คุณเปิดเผยข้อมูลที่สมเหตุสมผลหรืออย่างน้อยก็ทำให้สัญญา API ของคุณสูงเกินจริงโดยมีข้อมูลจำนวนมากที่ไม่ได้ใช้สำหรับผู้ที่ใช้งาน API แน่นอนคุณสามารถละเว้นฟิลด์โดยใช้คำอธิบายประกอบบางอย่าง (เช่น@JsonIgnoreจากโลกของ Java) แต่สิ่งนี้นำไปสู่ปัญหาต่อไป ...
  2. หมายเหตุประกอบ / เงื่อนไข / การควบคุมบนเอนทิตีจำนวนมาก คุณต้องควบคุมสิ่งที่คุณต้องการส่งใน Dto เมื่อมีการเปลี่ยนชื่อคุณลักษณะบางอย่าง (การทำเช่นนี้จะเป็นการผิดสัญญา) เพิ่มคุณสมบัติบางอย่างที่ไม่ได้มาจากเอนทิตีลำดับของแอททริบิว ฯลฯ ในไม่ช้าคุณจะเห็น เอนทิตีที่มีคำอธิบายประกอบจำนวนมากฟิลด์พิเศษและทุกครั้งจะยากที่จะเข้าใจว่าเกิดอะไรขึ้น
  3. มีความยืดหยุ่นน้อย ด้วย Dto คุณมีอิสระที่จะทำลายข้อมูลในคลาสเพิ่มเติมเปลี่ยนชื่อแอ็ตทริบิวต์เพิ่มแอ็ตทริบิวต์ใหม่ ฯลฯ คุณไม่สามารถทำสิ่งนี้ได้ง่ายด้วยเอนทิตีเป็น Dto
  4. ไม่เหมาะ เอนทิตีของคุณจะใหญ่กว่า Dto ง่าย ๆ เสมอ ดังนั้นคุณจะมีข้อมูลเพิ่มเติมที่จะถูกละเว้น / ต่อเนื่องและอาจมีการส่งข้อมูลที่ไม่จำเป็นเพิ่มเติม
  5. ปัญหาขี้เกียจ การใช้เอนทิตีของเฟรมเวิร์กบางตัว (เช่น Hibernate) หากคุณพยายามดึงข้อมูลบางอย่างที่ไม่ได้ขี้เกียจโหลดภายในทรานแซคชันฐานข้อมูลมาก่อนพร็อกซี ORM จะไม่ถูกแนบกับธุรกรรมและคุณจะได้รับ เพียงเพื่อเรียกgetวิธีการของเอนทิตี

ดังนั้นจึงง่ายขึ้นและปลอดภัยใช้เครื่องมือ mapper บางชนิดเพื่อช่วยคุณในงานนี้การแมปเขตข้อมูลเอนทิตีกับ Dto


1

เพื่อให้สิ่งที่ @Dherik พูดถึงเสร็จสมบูรณ์ปัญหาหลักของการใช้วัตถุเอนทิตีเป็นวัตถุการถ่ายโอนข้อมูลคือ:

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

  2. ขนาดของข้อมูลที่คุณแชร์ระหว่างไคลเอนต์และเซิร์ฟเวอร์: บางครั้งคุณไม่ต้องการส่งเนื้อหาทั้งหมดของเอนทิตีไปยังไคลเอนต์เพื่อลดขนาดของการตอบสนองของคำขอ การแยก DTO ออกจากเอนทิตีนั้นมีความยืดหยุ่นมากขึ้นเพื่อที่จะเชี่ยวชาญข้อมูลที่คุณต้องการส่งในกรณีใช้งานบางอย่าง

  3. ทัศนวิสัยและการบำรุงรักษา: คุณต้องจัดการคำอธิบายประกอบ jpa / hibernate ในช่องของเอนทิตีของคุณและเก็บรักษาคำอธิบายประกอบแจ็คสันเพื่อทำให้เป็นอนุกรมใน json ที่สถานที่เดียวกัน (แม้ว่าคุณสามารถแยกพวกเขาออกจากการใช้งานเอนทิตี นิติบุคคล) จากนั้นหากคุณเปลี่ยนเนื้อหา DTO ของคุณในการเพิ่มฟิลด์ใหม่บุคคลอื่นอาจคิดว่าเป็นฟิลด์ของเอนทิตีดังนั้นฟิลด์ของตารางที่เกี่ยวข้องกับฐานข้อมูลของคุณ (แม้ว่าคุณจะสามารถใช้@Transientคำอธิบายประกอบในฟิลด์ DTO ทั้งหมดของคุณ สำหรับกรณี .. !)

ในความคิดของฉันมันจะสร้างเสียงรบกวนเมื่อคุณอ่านเอนทิตี แต่ความเห็นของฉันเป็นแบบอัตนัย

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