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 isRefund
untuk 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 MoneyTransferMean
pada 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?
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 atauFlag
jenis):Payment
,Refund
,Both
. Jika dua nilai cocok untuk Anda,bool
properti itu bagus.Jawaban:
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.
sumber
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:
Maka Anda dapat dengan mudah mendapatkan yang berikut:
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.
sumber
Akhirnya, saya memutuskan untuk menyelesaikan masalah dengan menduplikasi entitas saya
MoneyTransferMean
menjadi dua entitasPaymentMean
danRefundMean
.Meskipun serupa dalam implementasinya, perbedaan antara kedua entitas tersebut masuk akal dalam bisnis dan bagi saya solusi yang paling buruk.
sumber