ฉันควรใช้ที่เก็บข้อมูลใน Domain Object หรือผลักดัน Domain Object กลับไปที่ Service Layer?


10

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

คลาสเซอร์วิสชื่อ OrganisationService ซึ่งอินเตอร์เฟสมีเมธอดสำหรับการดึงและบันทึกอินสแตนซ์ของอ็อบเจ็กต์โดเมนองค์กร องค์กรเป็นรูทรวมและมีข้อมูลอื่น ๆ ที่เกี่ยวข้อง: สมาชิกและใบอนุญาต ฐานข้อมูล EF6 ก่อน DBContext จะใช้ภายใน OrganisationService เพื่อดึงหน่วยงาน OrganisationDB และหน่วยงาน MemberDB และ LicenseDB ที่เกี่ยวข้อง สิ่งเหล่านี้ทั้งหมดได้รับการแปรสภาพเป็นคลาสอ็อบเจ็กต์โดเมนเทียบเท่าเมื่อดึงข้อมูลโดย OrganisationService และโหลดลงในวัตถุโดเมนองค์กร วัตถุนี้ดูเหมือนว่า:

public class Organisation
{
    public IList<Member> Members { get; set; }
    public IList<License> Licenses { get; set; }
}

ฉันไม่ได้ใช้รูปแบบพื้นที่เก็บข้อมูลใน OrganisationService ... ฉันใช้ EF เป็นพื้นที่เก็บข้อมูลเนื่องจากดูเหมือนว่า EF6 ได้ทำให้พื้นที่เก็บข้อมูลซ้ำซ้อนในขณะนี้

ณ จุดนี้ในการออกแบบวัตถุโดเมนองค์กรเป็นโลหิตจางดูเหมือนว่าระดับองค์กร EF POCO คลาส OrganisationService มีลักษณะเหมือน Repository มาก!

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

นี่คือที่ฉันไม่แน่ใจในสิ่งที่ฉันควรทำ: ฉันจะต้องยืนยันข้อมูลนี้กลับไปยังฐานข้อมูลเป็นส่วนหนึ่งของตรรกะ หมายความว่าฉันควรใช้ DbContext ภายในวัตถุโดเมนองค์กรเพื่อทำสิ่งนี้หรือไม่ การใช้พื้นที่เก็บข้อมูล / EF ภายในโดเมนปฏิบัติไม่ดีวัตถุ? ถ้าเป็นเช่นนั้นความเพียรนี้อยู่ที่ไหน

public class Organisation
{
    public IList<Member> Members { get; set; }
    public IList<License> Licenses { get; set; }

    public void AddLicensesToOrganisation(IList<License> licensesToAdd)
    {
        // Do validation/other stuff/

        // And now Save to the DB???
        using(var context = new EFContext())
        {
            context.Licenses.Add(...
        }

        // Add to the Licenses collection in Memory
        Licenses.AddRange(licensesToAdd);
    }
}

ฉันควรจะกลายพันธุ์วัตถุโดเมนองค์กรในหน่วยความจำและจากนั้นดันกลับไปที่ OrganisationService เพื่อคงอยู่? จากนั้นฉันต้องติดตามสิ่งที่เปลี่ยนแปลงจริงบนวัตถุ (ซึ่งเป็นสิ่งที่ EF ทำกับ POCO ของตัวเอง! ฉันรู้สึกว่า EF ไม่ได้เป็นเพียงการเปลี่ยนพื้นที่เก็บข้อมูล แต่อาจเป็นเลเยอร์โดเมน!)

คำแนะนำใด ๆ ที่นี่ชื่นชม

คำตอบ:


2

คุณสามารถใช้วิธีใดวิธีหนึ่งและทำงานได้ดี - มีแน่นอนข้อดีและข้อเสีย

Entity Framework มีจุดประสงค์เพื่อทำให้โดเมนของคุณไม่ถูกต้อง มันทำงานได้ดีเมื่อเอนทิตีโดเมนและเอนทิตีข้อมูลของคุณเป็นคลาสเดียวกัน มันดีกว่าถ้าคุณสามารถพึ่งพา EF เพื่อติดตามการเปลี่ยนแปลงของคุณและเพียงโทรหาcontext.SaveChanges()เมื่อคุณทำธุรกรรมเสร็จแล้ว ก็หมายความว่าการตรวจสอบคุณลักษณะของคุณไม่ได้จะต้องมีการตั้งสองครั้งครั้งในรูปแบบโดเมนของคุณและครั้งเดียวในหน่วยงานยังคงคุณ - สิ่งที่ต้องการ[Required]หรือ[StringLength(x)]สามารถตรวจสอบได้ในตรรกะทางธุรกิจของคุณช่วยให้คุณสามารถจับรัฐข้อมูลไม่ถูกต้องก่อนที่จะพยายาม ทำธุรกรรม DB และรับEntityValidationException. สุดท้ายคือการเขียนโค้ดอย่างรวดเร็ว - คุณไม่จำเป็นต้องเขียนเลเยอร์การทำแผนที่หรือที่เก็บ แต่สามารถทำงานกับบริบทของ EF ได้โดยตรง มันเป็นที่เก็บและหน่วยการทำงานอยู่แล้วดังนั้นสิ่งที่เป็นนามธรรมอีกชั้นหนึ่งจึงไม่สำเร็จ

ข้อเสียของการรวมโดเมนของคุณกับเอนทิตีที่ยังคงมีอยู่คือคุณจะต้องมี[NotMapped]แอตทริบิวต์ที่กระจัดกระจายอยู่ทั่วพื้นที่ของคุณ บ่อยครั้งที่คุณจะต้องการคุณสมบัติเฉพาะโดเมนซึ่งเป็นตัวกรองแบบรับเฉพาะสำหรับข้อมูลที่มีอยู่หรือตั้งค่าในตรรกะทางธุรกิจของคุณในภายหลังและจะไม่คงอยู่ในฐานข้อมูลของคุณ บางครั้งคุณจะต้องการแสดงข้อมูลของคุณในแบบจำลองโดเมนของคุณในแบบที่ทำงานได้ไม่ดีกับฐานข้อมูล - ตัวอย่างเช่นเมื่อใช้ enums เอ็นติตี้จะทำการแมปเหล่านี้กับintคอลัมน์ - แต่บางทีคุณต้องการแมป พวกเขาเป็นมนุษย์สามารถอ่านได้stringดังนั้นคุณไม่จำเป็นต้องปรึกษาการค้นหาเมื่อตรวจสอบฐานข้อมูล จากนั้นคุณจะจบลงด้วยstringคุณสมบัติที่แมปenumคุณสมบัติซึ่งไม่ใช่ (แต่รับสตริงและแม็พกับ enum) และ API ที่แสดงทั้งสอง! ในทำนองเดียวกันถ้าคุณต้องการรวมประเภทที่ซับซ้อน (ตาราง) ข้ามบริบทคุณอาจปิดท้ายด้วยmappedOtherTableId และComplexTypeคุณสมบัติที่ไม่ได้แมปซึ่งทั้งสองอย่างนั้นเปิดเผยต่อสาธารณะในชั้นเรียนของคุณ สิ่งนี้อาจสร้างความสับสนให้กับคนที่ไม่คุ้นเคยกับโครงการและการขยายรูปแบบโดเมนของคุณโดยไม่จำเป็น

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

เคล็ดลับหนึ่งในการหลีกเลี่ยงปัญหาในการติดตามการเปลี่ยนแปลงเอนทิตีของคุณคือการจัดเก็บ ID เอนทิตีที่สอดคล้องกันในรูปแบบโดเมนของคุณ สิ่งนี้สามารถถูกเติมโดยเลเยอร์การแมปของคุณโดยอัตโนมัติ จากนั้นเมื่อคุณต้องการยืนยันการเปลี่ยนแปลงกลับไปเป็น EF ให้ดึงเอนทิตีถาวรที่เกี่ยวข้องก่อนทำการแมปใด ๆ จากนั้นเมื่อคุณแมปการเปลี่ยนแปลง EF จะตรวจจับการเปลี่ยนแปลงเหล่านั้นโดยอัตโนมัติและคุณสามารถโทรได้context.SaveChanges()โดยไม่ต้องติดตามด้วยตนเอง

public class OrganisationService
{
    public void PersistLicenses(IList<DomainLicenses> licenses) 
    {
        using (var context = new EFContext()) 
        {
            foreach (DomainLicense license in licenses) 
            {
                var persistedLicense = context.Licenses.Find(pl => pl.Id == license.PersistedId);
                MappingService.Map(persistedLicense, license); //Right-left mapping
                context.Update(persistedLicense);
            }
            context.SaveChanges();
        }
    }
}

มีปัญหาหรือไม่เมื่อรวมวิธีการต่าง ๆ กับความซับซ้อนของข้อมูลที่แตกต่างกัน? หากคุณมีสิ่งที่แมปกับ DB อย่างดีให้ใช้ EF โดยตรง หากคุณมีสิ่งที่ไม่ดีให้แนะนำการทำแผนที่ / นามธรรมก่อนใช้ EF
ร่าเริง

@Euphoric ความแตกต่างของโดเมนและ db สามารถจัดการได้ในการจับคู่ ฉันไม่สามารถนึกถึงกรณีการใช้งานที่เพิ่มโดเมนสองครั้ง (ไม่ใช่แบบถาวรและแบบถาวร) และการจัดการการติดตามการเปลี่ยนแปลงด้วยมือนั้นทำได้น้อยกว่าการเพิ่มการแมปสำหรับกรณีพิเศษ คุณสามารถชี้ให้ฉันเป็นหนึ่งได้หรือไม่ (ฉันถือว่าใช้ NHibernate บางที EF มีข้อ จำกัด มากกว่านี้หรือไม่)
Firo

คำตอบที่เป็นประโยชน์มากในทางปฏิบัติและให้ข้อมูล ทำเครื่องหมายว่าเป็นคำตอบ ขอบคุณ!
MrLane

3

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

public void AddLicenses(IEnumerable<License> licensesToAdd)
{
    // Do validation/other stuff/
    // Add to the Licenses collection in Memory
    Licenses.AddRange(licensesToAdd);
}

และรหัสรอบ ๆ

var someOrg = Load(orgId);

someOrg.AddLicenses(someLicences);

SaveChanges();

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

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