Haruskah saya menggunakan repositori di Objek Domain atau mendorong Objek Domain kembali ke Lapisan Layanan?

10

Saya berasal dari dunia skrip transaksi dan saya baru mulai melihat DDD. Saya tidak yakin tentang cara yang benar untuk mengintegrasikan desain DDD dengan kegigihan basis data. Inilah yang saya miliki:

Kelas layanan yang disebut OrganisationService yang antarmukanya berisi metode untuk mengambil dan menyimpan contoh objek domain Organisasi. Organisasi adalah akar agregat dan memiliki data lain yang terkait dengannya: Anggota dan Lisensi. DBContextext database pertama EF6 digunakan dalam OrganisationService untuk mengambil entitas OrganisationDB dan entitas MemberDB dan LicenseDB yang terkait. Ini semua bisa ditransformasikan menjadi setara kelas objek domain mereka ketika diambil oleh OrganisationService dan dimuat ke objek domain organisasi. Objek ini terlihat seperti ini:

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

Saya tidak menggunakan pola Repositori di OrganisationService ... Saya menggunakan EF itu sendiri sebagai Repositori karena sepertinya EF6 telah membuat repositori secara berlebihan sekarang.

Pada titik ini dalam desain objek domain Organisasi adalah anemia: terlihat seperti kelas Organisasi EF POCO. Kelas OrganisationService sangat mirip dengan Repositori!

Sekarang saya harus mulai menambahkan logika. Logika ini termasuk mengelola Lisensi dan Anggota Organisasi. Sekarang di hari-hari skrip transaksi saya akan menambahkan metode ke dalam OrganisationService untuk menangani operasi ini dan memanggil ke dalam Repositori untuk berinteraksi dengan DB, tetapi dengan DDD saya percaya logika ini harus dienkapsulasi dalam objek domain Organisasi itu sendiri ...

Di sinilah saya tidak yakin dengan apa yang harus saya lakukan: Saya perlu mempertahankan data ini kembali ke database sebagai bagian dari logika. Apakah ini berarti saya harus menggunakan DbContext dalam objek domain Organisasi untuk melakukan ini? Apakah menggunakan repositori / EF dalam praktik buruk objek domain? Jika demikian, di manakah kegigihan ini?

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);
    }
}

Haruskah saya hanya bermutasi objek domain Organisasi dalam memori dan kemudian mendorongnya kembali ke OrganisationService untuk kegigihan? Lalu saya harus melacak apa yang sebenarnya berubah pada objek (yang dilakukan EF terhadap POCO-nya sendiri! Saya agak merasa bahwa EF bukan hanya pengganti repositori, tetapi juga bisa menjadi lapisan domain!)

Bimbingan apa pun di sini sangat dihargai.

MrLane
sumber

Jawaban:

2

Anda dapat mengambil salah satu pendekatan dan membuatnya bekerja dengan baik - tentu saja ada pro dan kontra.

Entity Framework pasti dimaksudkan untuk membuat entitas domain Anda suffuse. Ini berfungsi dengan baik ketika entitas domain Anda dan entitas data adalah kelas yang sama. Jauh lebih baik jika Anda dapat mengandalkan EF untuk melacak perubahan untuk Anda, dan teleponlah context.SaveChanges()saat Anda selesai dengan pekerjaan transaksional Anda. Ini juga berarti bahwa atribut validasi Anda tidak harus ditetapkan dua kali, sekali pada model domain Anda dan sekali pada entitas yang bertahan - hal-hal seperti [Required]atau [StringLength(x)]dapat diperiksa dalam logika bisnis Anda , memungkinkan Anda untuk menangkap status data yang tidak valid sebelum Anda mencoba untuk lakukan transaksi DB dan dapatkanEntityValidationException. Akhirnya, ini cepat untuk dikodekan - Anda tidak perlu menulis layer pemetaan atau repositori, tetapi sebaliknya dapat bekerja secara langsung dengan konteks EF. Itu sudah repositori dan unit kerja, jadi lapisan abstraksi tambahan tidak mencapai apa-apa.

Kelemahan untuk menggabungkan domain Anda dan entitas yang bertahan adalah Anda berakhir dengan banyak [NotMapped]atribut yang tersebar di seluruh properti Anda. Seringkali, Anda akan menginginkan properti khusus domain yang berupa filter hanya-mendapatkan pada data yang ada, atau ditetapkan kemudian dalam logika bisnis Anda dan tidak bertahan kembali ke database Anda. Beberapa kali, Anda akan ingin mengekspresikan data Anda dalam model domain Anda dengan cara yang tidak berfungsi dengan baik dengan database - misalnya, saat menggunakan enums, Entity akan memetakan ini ke intkolom - tetapi mungkin Anda ingin memetakan mereka dapat dibaca oleh manusia string, jadi Anda tidak perlu berkonsultasi dengan pencarian saat memeriksa database. Kemudian Anda berakhir dengan stringproperti yang dipetakan, sebuahenumproperti yang bukan (tetapi mendapatkan string dan peta ke enum), dan API yang memaparkan keduanya! Demikian pula, jika Anda ingin menggabungkan tipe (tabel) kompleks di seluruh konteks, Anda dapat berakhir dengan mappedOtherTableId dan ComplexTypeproperti yang tidak dipetakan , yang keduanya terbuka sebagai publik di kelas Anda. Ini dapat membingungkan bagi seseorang yang tidak terbiasa dengan proyek, dan membengkokkan model domain Anda yang tidak perlu.

Semakin kompleks logika bisnis / domain saya, semakin ketat atau rumit saya menemukan menggabungkan domain saya dan entitas bertahan menjadi. Untuk proyek dengan tenggat waktu pendek, atau yang tidak mengungkapkan lapisan bisnis yang kompleks, saya merasa bahwa menggunakan entitas EF untuk kedua tujuan itu tepat, dan tidak perlu untuk mengabstraksi domain Anda dari ketekunan Anda. Untuk proyek yang membutuhkan kemudahan maksimum ekstensi, atau yang perlu mengekspresikan logika yang sangat rumit, saya pikir Anda lebih baik memisahkan keduanya dan berurusan dengan kompleksitas kegigihan ekstra.

Salah satu trik untuk menghindari masalah melacak perubahan entitas Anda secara manual adalah dengan menyimpan ID entitas yang sesuai yang ada dalam model domain Anda. Ini dapat diisi secara otomatis oleh layer pemetaan Anda. Kemudian, ketika Anda perlu bertahan kembali ke EF, ambil entitas persisten yang relevan sebelum melakukan pemetaan apa pun. Kemudian ketika Anda memetakan perubahan, EF akan mendeteksinya secara otomatis, dan Anda dapat menelepon context.SaveChanges()tanpa harus melacaknya dengan tangan.

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();
        }
    }
}
NWard
sumber
Apakah ada masalah ketika menggabungkan pendekatan yang berbeda dengan kompleksitas data yang berbeda? jika Anda memiliki sesuatu yang memetakan dengan baik ke DB, cukup gunakan EF secara langsung. Jika Anda memiliki sesuatu yang tidak, maka perkenalkan pemetaan / abstraksi sebelum menggunakan EF.
Euforia
@Euphoric perbedaan domain dan db dapat ditangani dalam pemetaan. Saya tidak bisa memikirkan kasus penggunaan di mana menambahkan domain dua kali (tidak persisten dan gigih) dan menangani pelacakan perubahan dengan tangan kurang bekerja daripada menambahkan pemetaan untuk kasus khusus. Bisakah Anda mengarahkan saya ke satu? (Saya berasumsi menggunakan NHibernate, mungkin EF lebih terbatas?)
Firo
Jawabannya sangat membantu, praktis dan informatif. Menandai sebagai jawabannya. Terima kasih!
MrLane
3

saya tidak tahu EF6 tetapi ORM lain menangani ini secara transparan sehingga Anda tidak perlu menambahkan lisensi ke konteks secara eksplisit, itu akan mendeteksi mereka ketika menyimpan perubahan. Jadi metodenya akan turun ke

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

dan kode di sekitarnya

var someOrg = Load(orgId);

someOrg.AddLicenses(someLicences);

SaveChanges();
Firo
sumber
Objek domain saya bukan Entitas, jadi menambahkannya ke koleksi Lisensi, yang hanya Daftar tidak akan memotongnya. Pertanyaannya bukan tentang EF tetapi karena di mana kegigihan yang sebenarnya terjadi. Semua kegigihan dalam EF harus terjadi dalam lingkup konteks. Pertanyaannya adalah di mana ini berada?
MrLane
2
entitas domain dan entitas yang bertahan dapat / harus sama jika tidak Anda perkenalkan lapisan abstraksi lain yang 99% waktunya tidak menambahkan nilai dan pelacakan perubahan hilang.
Firo