Un poco de conocimiento de dominio.
Estoy escribiendo un software de punto de venta (POS) que permite pagar bienes o reembolsarlos. Al pagar o reembolsar, uno debe especificar qué transferencia de dinero quiere usar: efectivo, EFT (~ = tarjeta de crédito), tarjeta de fidelidad, cupón, etc.
Estos medios de transferencia de dinero son un conjunto finito y conocido de valores (una especie de enumeración).
La parte difícil es que necesito poder almacenar un subconjunto personalizado de estos medios para pagos y reembolsos (los dos conjuntos pueden ser diferentes) en el terminal POS.
Por ejemplo:
- Medios de pago disponibles: efectivo, EFT, tarjeta de fidelidad, comprobante
- Reembolso disponible significa: efectivo, cupón
Estado actual de implementación
Elijo implementar el concepto de transferencia de dinero como sigue:
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 razón por la que no es una "enumeración simple" es que existe algún comportamiento dentro de estas clases. También tuve que declarar las clases internas públicas (en lugar de privadas) para hacer referencia a ellas en el mapeo FluentNHibernate (ver más abajo).
Como se usa
Tanto el medio de pago como el de reembolso siempre se almacenan o recuperan en / desde la base de datos como un conjunto. Son realmente dos conjuntos distintos, aunque algunos valores dentro de ambos conjuntos pueden ser iguales.
Caso de uso 1: defina un nuevo conjunto de medios de pago / reembolso
- Eliminar todos los medios de pago / reembolso existentes
- Insertar los nuevos
Caso de uso 2: recupere todos los medios de pago / reembolso
- Obtenga una colección de todos los medios de pago / reembolso almacenados
Problema
Estoy atrapado con mi diseño actual en el aspecto de persistencia. Estoy usando NHibernate (con FluentNHibernate para declarar mapas de clase) y no puedo encontrar una manera de asignarlo a algún esquema de base de datos válido.
Descubrí que es posible mapear una clase varias veces usando nombre-entidad, sin embargo, no estoy seguro de que sea posible con subclases.
Lo que no estoy listo para hacer es alterar la API pública de MoneyTransferMean para poder persistir (por ejemplo, agregando un bool isRefund
para diferenciar entre los dos). Sin embargo, agregar algún campo discriminador privado más o menos está bien.
Mi mapeo actual:
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...
Esta asignación se compila, sin embargo, solo produce 1 tabla y no puedo diferenciar entre pago / reembolso al consultar esta tabla.
Intenté declarar dos asignaciones que hacen referencia tanto MoneyTransferMean
a una tabla como a un nombre de entidad diferentes, sin embargo, esto me lleva a una excepción Duplicate class/entity mapping MoneyTransferMean+CashMoneyTransferMean
.
También intenté duplicar las asignaciones de subclase, pero no puedo especificar una "asignación principal" que me lleva a la misma excepción que la anterior.
Pregunta
¿Existe una solución para persistir mis entidades de dominio actuales?
Si no, ¿cuál sería el refactor más pequeño que debo realizar en mis entidades para que sean persistentes con 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 qué no? Es un cambio simple y dulce que debería resolver su problema. Usted puede hacer hasta con tres valores posibles (aunque dos también lo hará con registros duplicados oFlag
tipo):Payment
,Refund
,Both
. Si dos valores lo hacen por usted, labool
propiedad es excelente.Respuestas:
¿Por qué no crea una sola entidad MoneyTransferMean , con todas las propiedades comunes (campos) y simplemente agrega 2 campos adicionales (booleanos) para determinar si MoneyTransferMean es Pago o Reembolso, o ambos ???? Persistir o no.
También se puede hacer con una Entidad adicional con Id (PK), agregar los mismos campos adicionales, la relación sería 1: 1 con MoneyTransferMean. Feo, lo sé, pero debería funcionar.
fuente
En segundo lugar, agregaría a lo que @ DEVX75 sugirió, en el sentido de que sus tipos de transacción esencialmente describen el mismo concepto, aunque uno es + ve mientras que el otro es -ve. Sin embargo, probablemente agregaría solo un campo booleano y tendría registros separados para discernir los reembolsos de los pagos.
Suponiendo que tiene un UID y no está utilizando el nombre de la etiqueta de medios como ID, puede permitir nombres duplicados para los medios e incluir dos entradas de efectivo, por ejemplo:
Entonces puede obtener fácilmente lo siguiente:
De esa manera, si en sus transacciones ha hecho referencia a MoneyTransferMean.UID = 2, sabe que es un reembolso en efectivo, en lugar de saber que es un tipo de transacción que podría ser un reembolso en efectivo o un pago en efectivo.
fuente
Finalmente, decidí resolver el problema duplicando mi entidad
MoneyTransferMean
en dos entidadesPaymentMean
yRefundMean
.Aunque similar en la implementación, la distinción entre las dos entidades tiene sentido en el negocio y fue para mí la peor solución.
fuente