Zuordnung derselben Entität zu verschiedenen Tabellen


9

Ein bisschen Domainwissen

Ich schreibe eine POS-Software (Point Of Sales), mit der Waren bezahlt oder erstattet werden können. Bei der Zahlung oder Rückerstattung muss angegeben werden, welche Geldüberweisung verwendet werden soll: Bargeld, Überweisung (~ = Kreditkarte), Kundenkarte, Gutschein usw.

Diese Geldtransfermittel sind eine endliche und bekannte Menge von Werten (eine Art Aufzählung).

Der schwierige Teil ist, dass ich in der Lage sein muss, eine benutzerdefinierte Teilmenge dieser Mittel sowohl für Zahlungen als auch für Rückerstattungen (die beiden Sätze können unterschiedlich sein) auf dem POS-Terminal zu speichern.

Zum Beispiel:

  • Verfügbare Zahlungsmittel: Bargeld, Überweisung, Kundenkarte, Gutschein
  • Verfügbare Rückerstattung bedeutet: Bargeld, Gutschein

Aktueller Stand der Umsetzung

Ich entscheide mich, das Konzept des Geldtransfermittels wie folgt umzusetzen:

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

Der Grund, warum es sich nicht um eine "einfache Aufzählung" handelt, ist, dass es ein Verhalten innerhalb dieser Klassen gibt. Ich musste auch innere Klassen öffentlich (statt privat) deklarieren, um sie in der FluentNHibernate-Zuordnung zu referenzieren (siehe unten).

Wie es verwendet wird

Sowohl das Zahlungs- als auch das Rückerstattungsmittel werden immer als Satz in / aus der Datenbank gespeichert oder abgerufen. Es handelt sich tatsächlich um zwei unterschiedliche Mengen, obwohl einige Werte in beiden Mengen möglicherweise gleich sind.

Anwendungsfall 1: Definieren Sie einen neuen Satz von Zahlungs- / Rückerstattungsmitteln

  • Löschen Sie alle vorhandenen Zahlungs- / Rückerstattungsmittel
  • Fügen Sie die neuen ein

Anwendungsfall 2: Rufen Sie alle Zahlungs- / Rückerstattungsmittel ab

  • Holen Sie sich eine Sammlung aller gespeicherten Zahlungs- / Rückerstattungsmittel

Problem

Ich bleibe bei meinem aktuellen Design in Bezug auf den Persistenzaspekt. Ich verwende NHibernate (mit FluentNHibernate zum Deklarieren von Klassenzuordnungen) und kann keine Möglichkeit finden, es einem gültigen DB-Schema zuzuordnen.

Ich habe festgestellt, dass es möglich ist, eine Klasse mehrmals mit dem Entitätsnamen zuzuordnen, bin mir jedoch nicht sicher, ob dies mit Unterklassen möglich ist.

Ich bin nicht bereit, die öffentliche MoneyTransferMean-API so zu ändern, dass sie beibehalten werden kann (z. B. Hinzufügen eines bool isRefund, um zwischen beiden zu unterscheiden). Das Hinzufügen eines privaten Diskriminatorfeldes oder so ist jedoch in Ordnung.

Mein aktuelles Mapping:

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

Diese Zuordnung wird kompiliert, es wird jedoch nur 1 Tabelle erstellt, und ich kann bei der Abfrage dieser Tabelle nicht zwischen Zahlung / Rückerstattung unterscheiden.

Ich habe versucht, zwei Zuordnungen zu deklarieren, die sowohl MoneyTransferMeanauf unterschiedliche Tabellen als auch auf unterschiedliche Entitätsnamen verweisen. Dies führt mich jedoch zu einer Ausnahme Duplicate class/entity mapping MoneyTransferMean+CashMoneyTransferMean.

Ich habe auch versucht, Zuordnungen von Unterklassen zu duplizieren, kann jedoch keine "übergeordnete Zuordnung" angeben, die mich zu derselben Ausnahme wie oben führt.

Frage

Gibt es eine Lösung, um meine aktuellen Domänenentitäten beizubehalten?

Wenn nicht, was wäre der kleinste Refaktor, den ich für meine Entitäten ausführen muss, um sie mit NHibnernate dauerhaft zu machen?


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).: Warum nicht? Es ist eine einfache und süße Veränderung, die Ihr Problem lösen sollte. Sie können mit drei möglichen Werte bilden (obwohl zwei auch mit doppelten Datensätzen oder tun FlagTyp): Payment, Refund, Both. Wenn zwei Werte für Sie zutreffen, ist die boolEigenschaft großartig.
Amit Joshi

1
Warum möchten Sie diese Zahlungsmethoden in der Datenbank speichern? Wie ist der Staat dort, abgesehen vom Namen?
Berhalak

@AmitJoshi Während eine so kleine Änderung das Problem (an der Oberfläche) lösen könnte, möchte ich vermeiden, meiner Domäne Logik hinzuzufügen, die nicht geschäftsbezogen ist.
Spotted

@berhalak In der Tat sieht das Speichern dieser in der Datenbank etwas umständlich aus. Dies ist jedoch eine Anforderung des Projekts, dass sich der gesamte Status in der Datenbank befindet.
Spotted

Antworten:


0

Warum erstellen Sie nicht eine einzelne Entität MoneyTransferMean mit allen allgemeinen Eigenschaften (Feldern) und fügen einfach zwei zusätzliche Felder (Boolesche Werte) hinzu, um festzustellen, ob es sich bei MoneyTransferMean entweder um Zahlung oder Rückerstattung oder um beides handelt? Behalte es bei oder nicht.

Es kann auch mit einer zusätzlichen Entität mit ID (PK) durchgeführt werden. Fügen Sie dieselben zusätzlichen Felder hinzu. Die Beziehung wäre 1: 1 mit MoneyTransferMean. Hässlich, ich weiß, aber es sollte funktionieren.


Ich möchte meinem Domänenprojekt keine Komplexität hinzufügen, die nicht domänenbezogen ist (z. B. das Hinzufügen eines Booleschen Werts, für den ein nachfolgendes if / else erforderlich ist). Ich möchte auch nicht, dass die Leute, die diese Klassen verwenden, Fehler machen (indem sie vergessen, den Booleschen Wert zu überprüfen und denken, dass jeder Wert zum Beispiel ein Rückerstattungsmittel ist). Es geht nur um explizite Aussagen.
Spotted

0

Ich würde dem, was @ DEVX75 vorschlug, zustimmen und hinzufügen, dass Ihre Transaktionstypen im Wesentlichen dasselbe Konzept beschreiben, obwohl einer + ve ist, während der andere -ve ist. Ich würde wahrscheinlich nur ein boolesches Feld hinzufügen und separate Datensätze haben, um Rückerstattungen von Zahlungen zu unterscheiden.

Angenommen, Sie haben eine UID und verwenden nicht den Namen der Mittelbezeichnung als ID. Sie können doppelte Namen für Mittel zulassen und zwei Bareinträge einschließen, z. B.:

UID, Label, IsRefund

1, Bargeld, falsch

2, Bargeld, stimmt

3, Gutschein, falsch

4, Gutschein, wahr

Dann können Sie leicht folgendes bekommen:

Transaktionstyp = MoneyTransferMean.IsRefund? "Zahlung zurückerstatten"

Transaktionswert = MoneyTransferMean.IsRefund? MoneyTransfer.amount * -1: MoneyTransfer.amount

Auf diese Weise wissen Sie, wenn Sie in Ihren Transaktionen auf MoneyTransferMean.UID = 2 verwiesen haben, dass dies eine Bargeldrückerstattung ist, anstatt zu wissen, dass es sich um einen Transaktionstyp handelt, der entweder eine Barrückerstattung oder eine Barzahlung sein kann.


Ah Mist, habe gerade bemerkt, dass du gesagt hast, du willst / kannst die öffentliche API nicht bearbeiten. Entschuldigung / ignoriere meine Antwort, obwohl ich sie belassen werde, da sie möglicherweise für andere mit ähnlichen Problemen / Anwendungsfällen nützlich ist.
FrugalTPH

0

Schließlich entschied ich mich, das Problem zu lösen, indem ich meine Entität MoneyTransferMeanin zwei Entitäten PaymentMeanund duplizierte RefundMean.

Obwohl in der Implementierung ähnlich, macht die Unterscheidung zwischen den beiden Einheiten im Geschäft Sinn und war für mich die am wenigsten schlechte Lösung.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.