Mappatura della stessa entità su tabelle diverse


9

Un po 'di conoscenza del dominio

Sto scrivendo un software POS (Point Of Sales) che consente di pagare merci o rimborsarle. Quando si paga o si rimborsa, è necessario specificare quale trasferimento di denaro si intende utilizzare: contanti, EFT (~ = carta di credito), carta fedeltà, voucher, ecc.

Questi mezzi di trasferimento di denaro sono un insieme finito e noto di valori (una sorta di enum).

La parte difficile è che devo essere in grado di memorizzare un sottoinsieme personalizzato di questi mezzi sia per i pagamenti che per i rimborsi (i due set potrebbero essere diversi) sul terminale POS.

Per esempio:

  • Mezzi di pagamento disponibili: contanti, EFT, carta fedeltà, voucher
  • Il rimborso disponibile significa: contanti, buono

Stato attuale di attuazione

Ho scelto di implementare il concetto di trasferimento di denaro come segue:

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

Il motivo per cui non è un "semplice enum" è che esiste un comportamento all'interno di queste classi. Ho anche dovuto dichiarare le classi interne pubbliche (anziché private) per poterle fare riferimento nella mappatura FluentNHibernate (vedi sotto).

Come viene usato

Sia il mezzo di pagamento che quello di rimborso sono sempre memorizzati o recuperati nel / dal DB come set. Sono in realtà due insiemi distinti anche se alcuni valori all'interno di entrambi gli insiemi possono essere gli stessi.

Caso d'uso 1: definire una nuova serie di mezzi di pagamento / rimborso

  • Elimina tutti i mezzi di pagamento / rimborso esistenti
  • Inserisci quelli nuovi

Caso d'uso 2: recuperare tutti i mezzi di pagamento / rimborso

  • Ottieni una raccolta di tutti i mezzi di pagamento / rimborso memorizzati

Problema

Sono bloccato con il mio progetto attuale sull'aspetto della persistenza. Sto usando NHibernate (con FluentNHibernate per dichiarare le mappe di classe) e non riesco a trovare un modo per mapparlo su uno schema DB valido.

Ho scoperto che è possibile mappare una classe più volte usando nome-entità tuttavia non sono sicuro che sia possibile con le sottoclassi.

Quello che non sono pronto a fare è modificare l'API pubblica MoneyTransferMean per poterla perseguire (ad esempio aggiungendo un bool isRefundper differenziare tra i due). Tuttavia, aggiungere un campo discriminatore privato o giù di lì è ok.

La mia mappatura attuale:

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

Questa mappatura viene compilata tuttavia produce solo 1 tabella e non sono in grado di distinguere tra pagamento / rimborso durante l'interrogazione di questa tabella.

Ho provato a dichiarare due mappature che fanno riferimento sia MoneyTransferMeana tabella che a nome-entità diversi, ma ciò mi porta a un'eccezione Duplicate class/entity mapping MoneyTransferMean+CashMoneyTransferMean.

Ho anche provato a duplicare i mapping delle sottoclassi ma non riesco a specificare un "mapping principale" che mi porta alla stessa eccezione di cui sopra.

Domanda

Esiste una soluzione per mantenere le mie attuali entità di dominio?

In caso contrario, quale sarebbe il più piccolo refattore che devo eseguire sulle mie entità per renderle persistenti con 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).: Perchè no? È un cambiamento semplice e dolce che dovrebbe risolvere il tuo problema. È possibile effettuare fino a tre valori possibili (anche se due saranno anche fare con i record duplicati o Flagtipo): Payment, Refund, Both. Se due valori fanno per te, la boolproprietà è fantastica.
Amit Joshi

1
Perché vuoi archiviare questi metodi di pagamento nel database? Qual è lo stato lì, a parte il nome?
Berhalak,

@AmitJoshi Mentre un cambiamento così piccolo potrebbe risolvere il problema (in superficie), voglio evitare di aggiungere nel mio dominio logiche non legate al business.
Scoperto il

@berhalak In effetti, archiviarli nel database sembra un po 'goffo. Tuttavia, questo è un requisito richiesto dal progetto che tutto lo stato sia nel database.
Scoperto il

Risposte:


0

Perché non crei una singola entità MoneyTransferMean , con tutte le proprietà comuni (campi) e aggiungi semplicemente 2 campi extra (booleani) per determinare se tale MoneyTransferMean è Pagamento o Rimborso o entrambi ???? Persevera o no.

Inoltre può essere fatto con un'entità aggiuntiva con ID (PK), aggiungere gli stessi campi extra, la relazione sarebbe 1: 1 con MoneyTransferMean. Brutto, lo so, ma dovrebbe funzionare.


Non voglio aggiungere complessità che non sia correlata al dominio nel mio progetto di dominio (come l'aggiunta di un valore booleano su cui è necessario un successivo if / else). Inoltre, non voglio che le persone che useranno queste classi facciano errori (dimenticando di controllare il valore booleano e pensando che ogni valore sia un mezzo di rimborso, ad esempio). Si tratta di esplicitare.
Scoperto il

0

Vorrei quindi aggiungere ciò che @ DEVX75 ha suggerito, in quanto i tipi di transazione descrivono essenzialmente lo stesso concetto, sebbene uno sia + ve mentre l'altro sia -ve. Probabilmente aggiungerei solo un campo booleano e avrei record separati per discernere i rimborsi dai pagamenti.

Supponendo di avere un UID e di non utilizzare il nome dell'etichetta dei mezzi come ID, è possibile consentire nomi duplicati per i mezzi e includere due registrazioni in contanti, ad esempio:

UID, Etichetta, IsRefund

1, contanti, falso

2, contanti, vero

3, buono, falso

4, buono, vero

Quindi puoi facilmente ottenere quanto segue:

Tipo di transazione = MoneyTransferMean.IsRefund? "Rimborso": "Pagamento"

Valore transazione = MoneyTransferMean.IsRefund? MoneyTransfer.amount * -1: MoneyTransfer.amount

In questo modo, se nelle tue transazioni hai fatto riferimento a MoneyTransferMean.UID = 2, sai che si tratta di un rimborso in contanti, piuttosto che sapere che si tratta di un tipo di transazione che potrebbe essere un rimborso in contanti o un pagamento in contanti.


Ah bugger, ho appena notato che hai detto che non vuoi / non puoi modificare l'API pubblica. Scusa / ignora la mia risposta, anche se la lascerò perché forse sarà utile per altri con problemi / casi d'uso simili.
FrugalTPH

0

Alla fine, ho deciso di risolvere il problema duplicando la mia entità MoneyTransferMeanin due entità PaymentMeaneRefundMean .

Sebbene simile nell'implementazione, la distinzione tra le due entità ha senso nel business ed è stata per me la soluzione meno peggiore.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.