Memetakan entitas yang sama ke tabel yang berbeda

9

Sedikit pengetahuan domain

Saya sedang menulis perangkat lunak POS (Point Of Sales) yang memungkinkan untuk membayar barang atau mengembalikannya. Saat membayar atau mengembalikan uang, orang perlu menentukan transfer uang mana yang digunakan: uang tunai, EFT (~ = kartu kredit), kartu loyalitas, voucher, dll.

Cara pengiriman uang ini adalah seperangkat nilai yang terbatas dan dikenal (semacam enum).

Bagian yang sulit adalah bahwa saya harus dapat menyimpan subset kustom cara ini untuk pembayaran dan pengembalian uang (dua set mungkin berbeda) di terminal POS.

Sebagai contoh:

  • Pembayaran yang tersedia berarti: Tunai, EFT, kartu Loyalitas, Voucher
  • Pengembalian dana yang tersedia berarti: Tunai, Voucher

Kondisi implementasi saat ini

Saya memilih untuk menerapkan konsep transfer uang sebagai berikut:

public abstract class MoneyTransferMean : AggregateRoot
{
    public static readonly MoneyTransferMean Cash = new CashMoneyTransferMean();
    public static readonly MoneyTransferMean EFT = new EFTMoneyTransferMean();
    // and so on...

    //abstract method

    public class CashMoneyTransferMean : MoneyTransferMean
    {
        //impl of abstract method
    }

    public class EFTMoneyTransferMean : MoneyTransferMean
    {
        //impl of abstract method
    }

    //and so on...
}

Alasannya bukan "plain enum" adalah bahwa ada beberapa perilaku yang ada di dalam kelas-kelas ini. Saya juga harus mendeklarasikan kelas dalam publik (bukan privat) untuk merujuk mereka dalam pemetaan FluentNHibernate (lihat di bawah).

Bagaimana ini digunakan

Baik sarana pembayaran dan pengembalian dana selalu disimpan atau diambil di / dari DB sebagai set. Mereka benar-benar dua set yang berbeda meskipun beberapa nilai di dalam kedua set mungkin sama.

Use case 1: tentukan perangkat pembayaran / pengembalian uang yang baru

  • Hapus semua pembayaran / pengembalian dana yang ada
  • Masukkan yang baru

Use case 2: ambil semua sarana pembayaran / pengembalian dana

  • Dapatkan koleksi semua pembayaran / pengembalian dana yang tersimpan

Masalah

Saya terjebak dengan desain saya saat ini pada aspek ketekunan. Saya menggunakan NHibernate (dengan FluentNHibernate untuk mendeklarasikan peta kelas) dan saya tidak dapat menemukan cara untuk memetakannya ke beberapa skema DB yang valid.

Saya menemukan bahwa dimungkinkan untuk memetakan kelas beberapa kali menggunakan entitas-nama namun saya tidak yakin bahwa itu mungkin dengan subclass.

Apa yang saya tidak siap lakukan adalah mengubah API publik MoneyTransferMean agar dapat bertahan (misalnya menambahkan bool isRefunduntuk membedakan antara keduanya). Namun menambahkan beberapa bidang diskriminator pribadi tidak masalah.

Pemetaan saya saat ini:

public sealed class MoneyTransferMeanMap : ClassMap<MoneyTransferMean>
{
    public MoneyTransferMeanMap()
    {
        Id(Entity.Expressions<MoneyTransferMean>.Id);
        DiscriminateSubClassesOnColumn("Type")
            .Not.Nullable();
    }
}

public sealed class CashMoneyTransferMeanMap : SubclassMap<MoneyTransferMean.CashMoneyTransferMean>
{
    public CashMoneyTransferMeanMap()
    {
        DiscriminatorValue("Cash");
    }
}

public sealed class EFTMoneyTransferMeanMap : SubclassMap<MoneyTransferMean.EFTMoneyTransferMean>
{
    public EFTMoneyTransferMeanMap()
    {
        DiscriminatorValue("EFT");
    }
}

//and so on...

Namun pemetaan ini mengkompilasi hanya menghasilkan 1 tabel dan saya tidak dapat membedakan antara pembayaran / pengembalian dana saat kueri tabel ini.

Saya mencoba untuk mendeklarasikan dua pemetaan yang merujuk MoneyTransferMeanpada tabel dan entitas-nama yang berbeda namun ini membuat saya terkecuali Duplicate class/entity mapping MoneyTransferMean+CashMoneyTransferMean.

Saya juga mencoba untuk menduplikasi pemetaan subclass tapi saya tidak dapat menentukan "pemetaan orangtua" yang membawa saya ke pengecualian yang sama seperti di atas.

Pertanyaan

Apakah ada solusi untuk mempertahankan entitas domain saya saat ini?

Jika tidak, apa yang akan menjadi refactor terkecil yang perlu saya lakukan pada entitas saya untuk membuat mereka bertahan dengan NHibnernate?

Tutul
sumber
1
What I'm not ready to do is to alter the MoneyTransferMean public API to be able to persist it (for example adding a bool isRefund to differentiate between the two).: Kenapa tidak? Ini adalah perubahan yang sederhana dan manis yang harus menyelesaikan masalah Anda. Anda dapat membuat dengan tiga kemungkinan nilai (meskipun dua juga akan melakukan dengan catatan duplikat atau Flagjenis): Payment, Refund, Both. Jika dua nilai cocok untuk Anda, boolproperti itu bagus.
Amit Joshi
1
Mengapa Anda ingin menyimpan metode pembayaran itu dalam database? Apa keadaan di sana, selain dari namanya?
berhalak
@AmitJoshi Sementara perubahan kecil seperti itu bisa menyelesaikan masalah (di permukaan), saya ingin menghindari menambahkan logika yang tidak terkait bisnis ke dalam domain saya.
Terlihat
@berhalak Memang, menyimpan ini dalam database terlihat agak kikuk. Namun ini adalah persyaratan dari proyek bahwa semua negara harus dalam database.
Terlihat

Jawaban:

0

Mengapa Anda tidak membuat satu entitas tunggal MoneyTransferMean , dengan semua properti umum (bidang) dan hanya menambahkan 2 bidang tambahan (boolean) untuk menentukan apakah MoneyTransferMean itu Pembayaran atau Pengembalian Uang, atau keduanya ???? Tetap bertahan atau tidak.

Juga dapat dilakukan dengan Entitas tambahan dengan Id (PK), tambahkan bidang tambahan yang sama, hubungan akan 1: 1 dengan MoneyTransferMean. Jelek, aku tahu, tapi itu harusnya berhasil.

DEVX75
sumber
Saya tidak ingin menambahkan kompleksitas yang tidak terkait dengan domain ke dalam proyek domain saya (seperti menambahkan boolean di mana / jika diperlukan selanjutnya). Juga saya tidak ingin orang-orang yang akan menggunakan kelas-kelas ini untuk membuat kesalahan (dengan lupa memeriksa boolean dan berpikir bahwa setiap nilai adalah pengembalian dana berarti misalnya). Ini semua tentang kesederhanaan.
Terlihat
0

Saya akan menambahkan dan menambahkan apa yang disarankan oleh DEVX75, dalam hal jenis transaksi Anda pada dasarnya menggambarkan konsep yang sama, meskipun satu adalah + ve sementara yang lain adalah -ve. Saya mungkin akan menambahkan hanya satu bidang boolean, dan memiliki catatan terpisah untuk membedakan pengembalian uang dari pembayaran.

Dengan asumsi Anda memiliki UID dan tidak menggunakan nama label sarana sebagai ID, Anda dapat mengizinkan nama duplikat untuk sarana, dan termasuk dua entri tunai, misalnya:

UID, Label, IsRefund

1, Uang tunai, salah

2, Tunai, benar

3, Voucher, salah

4, Voucher, benar

Maka Anda dapat dengan mudah mendapatkan yang berikut:

Jenis Transaksi = MoneyTransferMean.IsRefund? "Pengembalian Uang": "Pembayaran"

Nilai Transaksi = MoneyTransferMean.IsRefund? MoneyTransfer.amount * -1: MoneyTransfer.amount

Dengan begitu, jika dalam transaksi Anda, Anda telah mereferensikan MoneyTransferMean.UID = 2, Anda tahu bahwa itu adalah pengembalian uang tunai, daripada mengetahui bahwa itu adalah jenis transaksi yang bisa berupa pengembalian uang tunai atau pembayaran tunai.

FrugalTPH
sumber
Ah bugger, perhatikan saja Anda mengatakan Anda tidak ingin / tidak bisa mengedit API publik. Maaf / abaikan jawaban saya, meskipun saya akan meninggalkannya karena mungkin akan bermanfaat bagi orang lain dengan masalah / kasus penggunaan yang serupa.
FrugalTPH
0

Akhirnya, saya memutuskan untuk menyelesaikan masalah dengan menduplikasi entitas saya MoneyTransferMeanmenjadi dua entitas PaymentMeandan RefundMean.

Meskipun serupa dalam implementasinya, perbedaan antara kedua entitas tersebut masuk akal dalam bisnis dan bagi saya solusi yang paling buruk.

Tutul
sumber