将同一实体映射到不同的表


9

一点领域知识

我正在写一个POS(销售点)软件,该软件可以付款或退款。付款或退款时,需要指定使用哪种汇款方式:现金,EFT(〜=信用卡),会员卡,代金券等。

这些汇款方式是一组有限的已知值(一种枚举)。

棘手的是,我需要能够在POS终端上存储这些方式的自定义子集,以用于付款和退款(两组可能不同)。

例如:

  • 可用付款方式:现金,电子转帐,会员卡,优惠券
  • 可用退款方式:现金,代金券

实施现状

我选择实施汇款的概念意味如下:

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...
}

它不是“普通枚举”的原因是这些类内部存在某些行为。为了在FluentNHibernate映射中引用它们,我还必须声明内部类为public(而不是private)(请参见下文)。

如何使用

始终将付款和退款方式作为一组存储在DB中或从DB中检索出来。它们实际上是两个不同的集合,即使两个集合中的某些值可能相同。

用例1:定义一组新的付款/退款方式

  • 删除所有现有的付款/退款方式
  • 插入新的

用例2:检索所有付款/退款方式

  • 获取所有存储的付款/退款方式的集合

问题

我在持久性方面停留在当前的设计上。我正在使用NHibernate(使用FluentNHibernate来声明类映射),但是我找不到将其映射到某些有效DB模式的方法。

我发现可以使用实体名称多次映射一个类,但是我不确定子类是否可以映射。

我不准备做的是更改MoneyTransferMean公共API以使其能够持久(例如,添加a bool isRefund来区分两者)。但是,可以添加一些私有区分符字段。

我当前的映射:

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...

该映射会编译,但是它仅生成1个表,查询该表时我无法区分付款/退款。

我试图声明两个引用MoneyTransferMean不同表和实体名称的映射,但是这导致了异常Duplicate class/entity mapping MoneyTransferMean+CashMoneyTransferMean

我也尝试复制子类映射,但是无法指定“父映射”,这导致我遇到与上述相同的异常。

是否存在解决方案来保留我当前的域实体?

如果不是,那么我需要对实体执行的最小重构是什么,以使其可以与NHibnernate持久化?


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).: 为什么不?应该解决您的问题的简单而甜蜜的更改。您可以了三个可能的值(虽然两人还将会重复记录或做Flag类型): ,Payment,。Refund Both如果有两个值对您有用,那么bool属性就很棒。
阿米特·乔希

1
为什么要将这些付款方式存储在数据库中?除了名称,那里的状态是什么?
berhalak

@AmitJoshi尽管这么小的更改可以(从表面上)解决问题,但我想避免将与业务无关的逻辑添加到我的域中。
发现

@berhalak确实,将它们存储在数据库中看起来有些笨拙。但是,这是项目要求所有状态都在数据库中。
斑点

Answers:


0

为什么不创建一个具有所有常用属性(字段)的单个实体MoneyTransferMean,而仅添加2个额外的字段(布尔值)来确定MoneyTransferMean是付款还是退款,或者两者都是???坚持与否。

也可以使用带有ID(PK)的额外实体,添加相同的额外字段,与MoneyTransferMean的关系为1:1。我知道很丑,但是应该可以。


我不想将与领域无关的复杂性添加到我的领域项目中(例如,添加需要后续if / else的布尔值)。我也不希望使用这些类的人犯错(例如,忘记检查布尔值并认为每个值都是退款的意思)。这一切都与明确性有关。
斑点

0

我第二次添加@ DEVX75的建议,因为您的事务类型本质上描述了相同的概念,尽管一个是+ ve,而另一个是-ve。不过,我可能只添加一个布尔值字段,并有单独的记录来区分付款中的退款。

假设您有一个UID,并且没有使用均值标签名称作为ID,则可以允许均值名称重复,并包括两个现金条目,例如:

UID,标签,退款

1,现金,假

2,现金真实

3,代金券,假

4,凭证真实

然后,您可以轻松获得以下内容:

交易类型 = MoneyTransferMean.IsRefund吗?“退款”:“付款”

交易金额 = MoneyTransferMean.Is退款吗?MoneyTransfer.amount * -1:MoneyTransfer.amount

这样,如果您在交易中引用了MoneyTransferMean.UID = 2,则您知道那是现金退款,而不是那是可能是现金退款或现金付款的交易类型。


臭虫,刚注意到您说您不想/不能编辑公共API。抱歉/忽略我的答案,尽管我会保留它,因为它可能对具有类似问题/用例的其他人很有用。
FrugalTPH

0

最后,我决定通过将我的实体复制MoneyTransferMean到两个实体PaymentMean和中来解决该问题RefundMean

尽管实现方式相似,但这两个实体之间的区别在业务上还是有意义的,对我而言,这是最差的解决方案。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.