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 isRefund
pour 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 MoneyTransferMean
avec 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?
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 ouFlag
type):Payment
,Refund
,Both
. Si deux valeurs vousbool
conviennent , la propriété est idéale.Réponses:
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.
la source
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:
Ensuite, vous pouvez facilement obtenir les éléments suivants:
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.
la source
Enfin, j'ai décidé de résoudre le problème en dupliquant mon entité
MoneyTransferMean
en deux entitésPaymentMean
etRefundMean
.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.
la source