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 isRefund
para 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 MoneyTransferMean
com 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?
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 ouFlag
tipo):Payment
,Refund
,Both
. Se dois valores contribuem para você, abool
propriedade é ótima.Respostas:
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.
fonte
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:
Então você pode facilmente obter o seguinte:
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.
fonte
Finalmente, decidi resolver o problema duplicando minha entidade
MoneyTransferMean
em duas entidadesPaymentMean
eRefundMean
.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.
fonte