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.