Mapowanie tego samego elementu do różnych tabel

9

Trochę wiedzy domenowej

Piszę oprogramowanie POS (Point Of Sales), które pozwala płacić towary lub zwracać je. Płacąc lub zwracając pieniądze , należy określić, z którego przelewu pieniężnego korzystać: gotówka, EFT (~ = karta kredytowa), karta lojalnościowa, kupon itp.

Te środki przekazu pieniężnego są skończonym i znanym zestawem wartości (rodzaj wyliczenia).

Problem polega na tym, że muszę mieć możliwość przechowywania niestandardowego podzbioru tych środków na potrzeby płatności i zwrotów (oba zestawy mogą być różne) na terminalu POS.

Na przykład:

  • Dostępne środki płatności: gotówka, EFT, karta stałego klienta, kupon
  • Dostępne zwroty oznaczają: gotówka, kupon

Aktualny stan realizacji

Wybieram wdrożenie koncepcji przekazu pieniężnego w następujący sposób:

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

Powodem, dla którego nie jest to „zwykły wyliczenie” jest to, że istnieje pewne zachowanie wewnątrz tych klas. Musiałem także zadeklarować klasy wewnętrzne jako publiczne (zamiast prywatne), aby odwoływać się do nich w mapowaniu FluentNHibernate (patrz poniżej).

Jak to jest używane

Zarówno środki płatności, jak i środki zwrotu są zawsze przechowywane lub pobierane w / z DB jako zestaw. Są to tak naprawdę dwa odrębne zestawy, chociaż niektóre wartości w obu zestawach mogą być takie same.

Przypadek zastosowania 1: zdefiniuj nowy zestaw środków płatności / zwrotu

  • Usuń wszystkie istniejące środki płatności / zwrotu
  • Wstaw nowe

Przypadek użycia 2: odzyskaj wszystkie środki płatności / zwrotu

  • Uzyskaj zbiór wszystkich przechowywanych środków płatności / zwrotów

Problem

Utknąłem z obecnym projektem dotyczącym aspektu trwałości. Używam NHibernate (z FluentNHibernate do deklarowania map klas) i nie mogę znaleźć sposobu na mapowanie go na jakiś prawidłowy schemat DB.

Odkryłem, że możliwe jest mapowanie klasy wiele razy przy użyciu encji-nazwy jednak nie jestem pewien, czy jest to możliwe w przypadku podklas.

Nie jestem gotowy, aby zmienić publiczny interfejs API MoneyTransferMean, aby móc go utrzymać (na przykład dodanie bool isRefund rozróżnienia między nimi). Jednak dodanie jakiegoś prywatnego pola dyskryminującego jest w porządku.

Moje obecne mapowanie:

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

To mapowanie kompiluje się, ale generuje tylko 1 tabelę i nie jestem w stanie rozróżnić płatności / zwrotu przy zapytaniu o tę tabelę.

Próbowałem zadeklarować dwa odwzorowania odwołujące się zarówno MoneyTransferMeando innej tabeli, jak i nazwy encji, ale prowadzi mnie to do wyjątkuDuplicate class/entity mapping MoneyTransferMean+CashMoneyTransferMean .

Próbowałem również powielić mapowania podklas, ale nie jestem w stanie określić „mapowania nadrzędnego”, co prowadzi mnie do tego samego wyjątku jak powyżej.

Pytanie

Czy istnieje rozwiązanie pozwalające zachować moje obecne domeny?

Jeśli nie, to jaki byłby najmniejszy refaktor, który muszę wykonać na moich podmiotach, aby były trwałe w NHibnernate?

Cętkowany
źródło
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).: Dlaczego nie? To prosta i słodka zmiana, która powinna rozwiązać twój problem. Można uzupełnić z trzech możliwych wartości (choć dwa zrobi z duplikatów zapisów lub Flagtyp): Payment, Refund, Both. Jeśli dwie wartości Ci odpowiadają, boolwłaściwość jest świetna.
Amit Joshi
1
Dlaczego chcesz przechowywać te metody płatności w bazie danych? Jaki jest tam stan oprócz nazwy?
berhalak
@AmitJoshi Chociaż tak mała zmiana może rozwiązać problem (na powierzchni), chcę uniknąć dodawania logiki niezwiązanej z biznesem do mojej domeny.
Zauważyłem
@berhalak Rzeczywiście, przechowywanie ich w bazie danych wygląda trochę niezgrabnie. Jednak jest to wymagane przez projekt, aby cały stan znajdował się w bazie danych.
Zauważyłem

Odpowiedzi:

0

Dlaczego nie utworzysz jednego podmiotu MoneyTransferMean ze wszystkimi wspólnymi właściwościami (pola) i po prostu dodasz 2 dodatkowe pola (boolean), aby ustalić, czy MoneyTransferMean jest płatnością czy zwrotem, czy też obydwoma ???? Utrzymaj to czy nie.

Można to również zrobić za pomocą dodatkowej jednostki z identyfikatorem (PK), dodać te same dodatkowe pola, relacja wynosiłaby 1: 1 z MoneyTransferMean. Wstrętne, wiem, ale powinno działać.

DEVX75
źródło
Nie chcę dodawać do mojego projektu domeny złożoności niezwiązanej z domeną (takiej jak dodawanie wartości logicznej, w przypadku której konieczne jest kolejne, jeśli / else). Nie chcę też, aby ludzie, którzy będą korzystać z tych klas, popełnili błąd (zapominając o sprawdzeniu wartości logicznej i myśląc, że na przykład każda wartość jest środkiem zwrotu). Chodzi o jawność.
Zauważyłem
0

Chciałbym dodać i dodać do sugestii @ DEVX75, ponieważ typy transakcji zasadniczo opisują tę samą koncepcję, chociaż jedna jest + ve, a druga jest -ve. Prawdopodobnie dodałbym tylko jedno pole boolowskie i posiadam osobne zapisy, aby rozróżnić zwroty od płatności.

Zakładając, że masz identyfikator UID i nie używasz nazwy etykiety środka jako identyfikatora, możesz zezwolić na duplikowanie nazw środków i dołączyć dwa wpisy kasowe, na przykład:

UID, etykieta, IsRefund

1, Gotówka, fałsz

2, Gotówka, prawda

3, Kupon, fałsz

4, Kupon, prawda

Następnie możesz łatwo uzyskać następujące informacje:

Rodzaj transakcji = MoneyTransferMean.IsRefund? „Zwrot”: „Płatność”

Wartość transakcji = MoneyTransferMean.IsRefund? MoneyTransfer.amount * -1: MoneyTransfer.amount

W ten sposób, jeśli w swoich transakcjach odwoływałeś się do MoneyTransferMean.UID = 2, wiesz, że jest to zwrot gotówki, zamiast wiedzieć, że jest to typ transakcji, który może być zwrotem gotówki lub płatności gotówką.

FrugalTPH
źródło
Ach, kurde, właśnie zauważyłem, że powiedziałeś, że nie chcesz / nie możesz edytować publicznego API. Przepraszam / zignoruj ​​moją odpowiedź, chociaż zostawię ją, ponieważ być może będzie przydatna dla osób z podobnym problemem / przypadkiem użycia.
FrugalTPH
0

W końcu postanowiłem rozwiązać problem, duplikując mój byt MoneyTransferMeanna dwa byty PaymentMeaniRefundMean .

Mimo podobnej implementacji rozróżnienie między dwoma podmiotami ma sens w biznesie i było dla mnie najgorszym rozwiązaniem.

Cętkowany
źródło