Asignación de la misma entidad a diferentes tablas

9

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 isRefundpara 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 MoneyTransferMeana 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?

Manchado
fuente
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).: ¿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 o Flagtipo): Payment, Refund, Both. Si dos valores lo hacen por usted, la boolpropiedad es excelente.
Amit Joshi
1
¿Por qué quiere almacenar esos métodos de pago en la base de datos? ¿Cuál es el estado allí, aparte del nombre?
Berhalak
@AmitJoshi Si bien un cambio tan pequeño podría resolver el problema (en la superficie), quiero evitar agregar lógica que no esté relacionada con los negocios en mi dominio.
Visto el
@berhalak De hecho, almacenarlos en la base de datos parece un poco torpe. Sin embargo, es un requisito del proyecto que todo el estado esté en la base de datos.
Visto el

Respuestas:

0

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

DEVX75
fuente
No quiero agregar complejidad que no esté relacionada con el dominio en mi proyecto de dominio (como agregar un booleano en el que se necesita un if / else posterior). Además, no quiero que las personas que usarán estas clases se equivoquen (olvidando verificar el valor booleano y pensando que cada valor es un medio de reembolso, por ejemplo). Se trata de lo explícito.
Visto el
0

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:

UID, etiqueta, IsRefund

1, efectivo, falso

2, efectivo, verdadero

3, Vale, falso

4, Vale, cierto

Entonces puede obtener fácilmente lo siguiente:

Tipo de transacción = MoneyTransferMean.IsRefund? "Reembolso"

Valor de transacción = MoneyTransferMean.IsRefund? MoneyTransfer.amount * -1: MoneyTransfer.amount

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.

FrugalTPH
fuente
Ah, cabrón, acabo de notar que dijiste que no quieres / no puedes editar la API pública. Lo siento / ignore mi respuesta, aunque la dejaré ya que quizás sea útil para otras personas con problemas / casos de uso similares.
FrugalTPH
0

Finalmente, decidí resolver el problema duplicando mi entidad MoneyTransferMeanen dos entidades PaymentMeany RefundMean.

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.

Manchado
fuente