Mappage de la même entité à différentes tables

9

Un peu de connaissance du domaine

J'écris un logiciel POS (Point Of Sales) qui permet de payer des marchandises ou de les rembourser. Lors du paiement ou du remboursement, il faut spécifier quel moyen de transfert d'argent utiliser: argent comptant, EFT (~ = carte de crédit), carte de fidélité, bon, etc.

Ces moyens de transfert d'argent sont un ensemble fini et connu de valeurs (une sorte d'énumération).

La partie délicate est que je dois être en mesure de stocker un sous-ensemble personnalisé de ces moyens pour les paiements et les remboursements (les deux ensembles peuvent être différents) sur le terminal PDV.

Par exemple:

  • Moyens de paiement disponibles: Espèces, EFT, Carte de fidélité, Bon d'achat
  • Remboursement disponible signifie: Argent comptant, Bon

État actuel de mise en œuvre

Je choisis de mettre en œuvre le concept de transfert d'argent comme suit:

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

La raison pour laquelle il ne s'agit pas d'une "énumération simple" est qu'il existe un comportement à l'intérieur de ces classes. J'ai également dû déclarer des classes internes publiques (au lieu de privées) afin de les référencer dans la cartographie FluentNHibernate (voir ci-dessous).

Comment est-il utilisé

Les moyens de paiement et de remboursement sont toujours stockés ou récupérés dans / depuis la base de données sous forme d'ensemble. Ce sont vraiment deux ensembles distincts, même si certaines valeurs à l'intérieur des deux ensembles peuvent être identiques.

Cas d'utilisation 1: définir un nouvel ensemble de moyens de paiement / remboursement

  • Supprimer tous les moyens de paiement / remboursement existants
  • Insérez les nouveaux

Cas d'utilisation 2: récupérer tous les moyens de paiement / remboursement

  • Obtenez une collection de tous les moyens de paiement / remboursement stockés

Problème

Je suis coincé avec ma conception actuelle sur l'aspect persistance. J'utilise NHibernate (avec FluentNHibernate pour déclarer des mappages de classe) et je ne trouve pas de moyen de le mapper à un schéma de base de données valide.

J'ai trouvé qu'il est possible de mapper une classe plusieurs fois en utilisant le nom d'entité mais je ne suis pas sûr que ce soit possible avec des sous-classes.

Ce que je ne suis pas prêt à faire, c'est de modifier l'API publique MoneyTransferMean pour pouvoir la conserver (par exemple en ajoutant un bool isRefundpour différencier les deux). Cependant, l'ajout d'un champ de discriminateur privé est correct.

Ma cartographie actuelle:

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

Ce mappage compile cependant il ne produit qu'une seule table et je ne suis pas en mesure de faire la différence entre paiement / remboursement lors de l'interrogation de cette table.

J'ai essayé de déclarer deux mappages référençant à la fois MoneyTransferMeanavec une table et un nom d'entité différents, mais cela m'amène à une exception Duplicate class/entity mapping MoneyTransferMean+CashMoneyTransferMean.

J'ai également essayé de dupliquer les mappages de sous-classe, mais je ne suis pas en mesure de spécifier un "mappage parent" qui m'amène à la même exception que ci-dessus.

Question

Existe-t-il une solution pour conserver mes entités de domaine actuelles?

Si ce n'est pas le cas, quel serait le plus petit refactor dont j'ai besoin pour effectuer sur mes entités pour les rendre persistantes avec NHibnernate?

Pointé
la source
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).: Pourquoi pas? C'est un changement simple et doux qui devrait résoudre votre problème. Vous pouvez faire avec trois valeurs possibles (bien que deux seront également faire avec des enregistrements en double ou Flagtype): Payment, Refund, Both. Si deux valeurs vous boolconviennent , la propriété est idéale.
Amit Joshi
1
Pourquoi voulez-vous stocker ces méthodes de paiement dans la base de données? Quel est l'état là-bas, à part le nom?
berhalak
@AmitJoshi Bien qu'un si petit changement puisse résoudre le problème (en surface), je veux éviter d'ajouter une logique qui n'est pas liée aux affaires dans mon domaine.
repéré le
@berhalak En effet, leur stockage dans la base de données semble un peu maladroit. Cependant, il s'agit d'une exigence du projet que tout l'état soit dans la base de données.
Repéré le

Réponses:

0

Pourquoi ne créez-vous pas une seule entité MoneyTransferMean , avec toutes les propriétés communes (champs) et ajoutez simplement 2 champs supplémentaires (booléens) pour déterminer si MoneyTransferMean est soit Paiement, soit Remboursement, soit les deux ???? Persistez ou non.

Cela peut également être fait avec une entité supplémentaire avec Id (PK), ajoutez les mêmes champs supplémentaires, la relation serait 1: 1 avec MoneyTransferMean. Moche, je sais, mais ça devrait marcher.

DEVX75
la source
Je ne veux pas ajouter de complexité qui n'est pas liée au domaine dans mon projet de domaine (comme ajouter un booléen sur lequel un if / else ultérieur est nécessaire). De plus, je ne veux pas que les gens qui utiliseront ces classes se trompent (en oubliant de vérifier le booléen et en pensant que chaque valeur est un moyen de remboursement par exemple). Tout est question d'explicitation.
Repéré le
0

J'appuierais et ajouterais à ce que @ DEVX75 a suggéré, en ce que vos types de transaction décrivent essentiellement le même concept, bien que l'un soit + ve tandis que l'autre est -ve. J'ajouterais probablement un seul champ booléen, et j'aurais des enregistrements distincts pour discerner les remboursements des paiements.

En supposant que vous avez un UID et que vous n'utilisez pas le nom d'étiquette de moyen comme ID, vous pouvez autoriser les noms en double pour les moyens et inclure deux entrées en espèces, par exemple:

UID, étiquette, IsRefund

1, argent, faux

2, Cash, true

3, bon, faux

4, bon, vrai

Ensuite, vous pouvez facilement obtenir les éléments suivants:

Type de transaction = MoneyTransferMean.IsRefund? "Remboursement"

Valeur de transaction = MoneyTransferMean.IsRefund? MoneyTransfer.amount * -1: MoneyTransfer.amount

De cette façon, si dans vos transactions vous avez référencé MoneyTransferMean.UID = 2, vous savez qu'il s'agit d'un remboursement en espèces, plutôt que de savoir qu'il s'agit d'un type de transaction qui peut être un remboursement en espèces ou un paiement en espèces.

FrugalTPH
la source
Ah bugger, je viens de remarquer que vous avez dit que vous ne voulez pas / ne pouvez pas modifier l'API publique. Désolé / ignore ma réponse, bien que je la laisse car elle sera peut-être utile pour d'autres personnes ayant un problème / cas d'utilisation similaire.
FrugalTPH
0

Enfin, j'ai décidé de résoudre le problème en dupliquant mon entité MoneyTransferMeanen deux entités PaymentMeanet RefundMean.

Bien que similaire dans la mise en œuvre, la distinction entre les deux entités est logique dans l'entreprise et était pour moi la solution la moins mauvaise.

Pointé
la source