Mapeando a mesma entidade para tabelas diferentes

9

Um pouco de conhecimento de domínio

Estou escrevendo um software de ponto de venda que permite pagar ou reembolsá-los. Ao pagar ou reembolsar, é necessário especificar qual transferência de dinheiro significa usar: dinheiro, EFT (~ = cartão de crédito), cartão de fidelidade, voucher etc.

Esses meios de transferência de dinheiro são um conjunto finito e conhecido de valores (uma espécie de enum).

A parte complicada é que preciso armazenar um subconjunto personalizado desses meios para pagamentos e reembolsos (os dois conjuntos podem ser diferentes) no terminal do POS.

Por exemplo:

  • Meios de pagamento disponíveis: Dinheiro, EFT, Cartão de fidelidade, Voucher
  • O reembolso disponível significa: Dinheiro, Voucher

Estado atual de implementação

Eu escolho implementar o conceito de transferência de dinheiro da seguinte forma:

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

O motivo de não ser um "enum comum" é que existe algum comportamento dentro dessas classes. Também tive que declarar classes internas públicas (em vez de privadas) para referenciá-las no mapeamento do FluentNHibernate (veja abaixo).

Como é usado

Os meios de pagamento e reembolso são sempre armazenados ou recuperados no / do DB como um conjunto. Na verdade, são dois conjuntos distintos, embora alguns valores dentro de ambos sejam iguais.

Caso de uso 1: defina um novo conjunto de meios de pagamento / reembolso

  • Excluir todos os meios de pagamento / reembolso existentes
  • Inserir os novos

Caso de uso 2: recuperar todos os meios de pagamento / reembolso

  • Obtenha uma coleção de todos os meios de pagamento / reembolso armazenados

Problema

Estou preso ao meu design atual no aspecto persistente. Estou usando o NHibernate (com FluentNHibernate para declarar mapas de classe) e não consigo encontrar uma maneira de mapeá-lo para algum esquema de banco de dados válido.

Descobri que é possível mapear uma classe várias vezes usando o nome da entidade, no entanto, não tenho certeza de que seja possível com subclasses.

O que não estou pronto para fazer é alterar a API pública MoneyTransferMean para poder persistir (por exemplo, adicionar um bool isRefundpara diferenciar os dois). No entanto, adicionar algum campo de discriminador privado é bom.

Meu mapeamento atual:

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

Esse mapeamento é compilado, porém produz apenas 1 tabela e não sou capaz de diferenciar entre pagamento / reembolso ao consultar esta tabela.

Tentei declarar dois mapeamentos referenciando ambos MoneyTransferMeancom tabela e nome da entidade diferentes, mas isso me leva a uma exceção Duplicate class/entity mapping MoneyTransferMean+CashMoneyTransferMean.

Também tentei duplicar os mapeamentos de subclasses, mas não consigo especificar um "mapeamento pai" que me leva à mesma exceção que acima.

Questão

Existe uma solução para manter minhas entidades de domínio atuais?

Caso contrário, qual seria o menor refator que eu preciso executar em minhas entidades para torná-las persistentes com o NHibnernate?

Visto
fonte
11
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).: Por que não? É uma mudança simples e agradável que deve resolver seu problema. Você pode fazer-se com três valores possíveis (embora dois também vão fazer com registros duplicados ou Flagtipo): Payment, Refund, Both. Se dois valores contribuem para você, a boolpropriedade é ótima.
Amit Joshi
11
Por que você deseja armazenar esses métodos de pagamento no banco de dados? Qual é o estado lá, além do nome?
berhalak
@AmitJoshi Embora uma mudança tão pequena possa resolver o problema (na superfície), quero evitar adicionar uma lógica que não seja relacionada aos negócios no meu domínio.
Visto
@berhalak De fato, armazená-los no banco de dados parece um pouco desajeitado. No entanto, é um requisito do projeto que todo o estado esteja no banco de dados.
Visto

Respostas:

0

Por que você não cria uma única entidade MoneyTransferMean , com todas as propriedades comuns (campos) e apenas adiciona 2 campos extras (booleano) para determinar se esse MoneyTransferMean é Pagamento ou Reembolso ou ambos ???? Persista ou não.

Também pode ser feito com uma entidade extra com ID (PK), adicionar os mesmos campos extras, o relacionamento seria 1: 1 com MoneyTransferMean. Feio, eu sei, mas deve funcionar.

DEVX75
fonte
Não quero adicionar complexidade que não esteja relacionada ao domínio no meu projeto de domínio (como adicionar booleano no qual é necessário um if / else subsequente). Também não quero que as pessoas que usarão essas classes cometam erros (esquecendo de verificar o booleano e achando que cada valor é uma média de reembolso, por exemplo). É tudo uma questão de explicitação.
Visto em
0

Gostaria de acrescentar o que o @ DEVX75 sugeriu, pois seus tipos de transação estão basicamente descrevendo o mesmo conceito, embora um seja + ve enquanto o outro é -ve. Eu provavelmente adicionaria apenas um campo booleano e tenho registros separados para discernir reembolsos de pagamentos.

Supondo que você tenha um UID e não esteja usando o nome do rótulo de meios como o ID, é possível permitir nomes duplicados para meios e incluir duas entradas de caixa, por exemplo:

UID, etiqueta, IsRefund

1, dinheiro, falso

2, dinheiro, verdadeiro

3, comprovante, falso

4, comprovante, verdadeiro

Então você pode facilmente obter o seguinte:

Tipo de transação = MoneyTransferMean.IsRefund? "Reembolso": "Pagamento"

Valor da transação = MoneyTransferMean.IsRefund? MoneyTransfer.amount * -1: MoneyTransfer.amount

Dessa forma, se em suas transações você referenciar MoneyTransferMean.UID = 2, você sabe que é um reembolso em dinheiro, em vez de saber que é um tipo de transação que pode ser um reembolso em dinheiro ou um pagamento em dinheiro.

FrugalTPH
fonte
Ah, droga, acabei de notar que você disse que não deseja / não pode editar a API pública. Desculpe / ignore minha resposta, embora eu a deixe como talvez seja útil para outras pessoas com problemas / casos de uso semelhantes.
FrugalTPH
0

Finalmente, decidi resolver o problema duplicando minha entidade MoneyTransferMeanem duas entidades PaymentMeaneRefundMean .

Embora semelhante na implementação, a distinção entre as duas entidades faz sentido nos negócios e foi para mim a pior solução.

Visto
fonte