Jakie właściwości klasy powinny być w ViewModelu, a jakie w warstwie logiki biznesowej

0

W warstwie logiki biznesowej mam obiekt reprezentujący wykres słupkowy BarChart. Składa się on m.in. z listy słupków (DataPoint). Użytkownikowi chcę wyświetlić następujące dane na temat słupka: nazwę, wartość (de facto obiekt graficzny) i tooltip (zawierający info o nazwie, wartości i ilości). Zastanawiam się, czy obiekt DataPoint powinien mieć właściwości Name, Value, Quantity, a w ViewModelu "składać" Tooltip, czy może w DataPoint mieć już Tooltip, tam go "poskładać", i wtety w ViewModelu tylko Tooltip = dataPoint.Tooltip. Wtedy też już Quantity nie byłoby potrzebne. Poniżej przykłady. Który jest poprawny od strony dobrych praktyk, clean code itd?

#1

public class BarChart
{
    public List<DataPoint> DataPoints { get; set; } = new List<DataPoint>();

    public class DataPoint
    {
        public DataPoint(SomeData someData)
        {
            Name = someData.Name;
            Value = someData.Value;
            Quantity= someData.Quantity;
        }

        public string Name { get; set; }
        public decimal Value { get; set; }
        public int Quantity { get; set; }
    }
}

public class BarChartViewModel
{
    public BarChartViewModel(BarChart barChart)
    {
        Labels = barChart.DataPoints.Select(x => x.Label).ToList();
        Values = barChart.DataPoints.Select(x => x.Value).ToList();
        Tooltips = barChart.DataPoints.Select(x => $"{x.Name} {x.Value}zł ({x.Quantity} szt.)").ToList();
    }

    public List<string> Names { get; set; }
    public List<decimal> Values { get; set; }
    public List<string> Tooltips { get; set; }
}

#2

public class BarChart
{
    public List<DataPoint> DataPoints { get; set; } = new List<DataPoint>();

    public class DataPoint
    {
        public DataPoint(SomeData someData)
        {
            Name = someData.Name;
            Value = someData.Value;
            Tooltip = $"{someData.Name} {someData.Value}zł ({someData.Quantity} szt.)"
        }

        public string Name { get; set; }
        public decimal Value { get; set; }
        public string Tooltip { get; set; }
    }
}

public class BarChartViewModel
{
    public BarChartViewModel(BarChart barChart)
    {
        Labels = barChart.DataPoints.Select(x => x.Label).ToList();
        Values = barChart.DataPoints.Select(x => x.Value).ToList();
        Tooltips = barChart.DataPoints.Select(x => x.Tooltip).ToList();
    }

    public List<string> Names { get; set; }
    public List<decimal> Values { get; set; }
    public List<string> Tooltips { get; set; }
}
2

To pierwsze, bo formatowanie tekstu dla użytkownika jest na pewno powinno znajdować się po stronie aplikacji. Ale ja tu widzę większy problem. Na czym właściwie u Ciebie polega logika biznesowa, że występuje w niej coś takiego jak wykres? Jak dla mnie logika biznesowa powinna dostarczać jakichś danych, a za formę wyświetlenia (konsola, tabelka, wykres, itd.) odpowiada logika prezentacji.

0

Logika biznesowa polega na ustaleniu jaki kolor będzie miał słupek, a to zależy od wartości pola w bazie, dlatego umieściłem nadawanie koloru w klasie DataPoint. Myślisz, że to powinno być w ViewModelu? W sumie to BarChart nie ma żadnej logiki biznesowej i rzeczywiście może być niepotrzebny i mógłbym zostawić tylko DataPoint, nie mniej mam też inny obiekt reprezentujący wykres PieChart i tam już trochę jest tej logiki biznesowej. Na wejściu w konstruktorze przekazywane są wydatki (zakupy) użytkownika oraz kategorie, a następnie wydatki te są grupowane, ale nie przez GroupBy bo grupowanie jest zbyt skomplikowane. Potem taki zgrupowany item (Name i Value (wartość wydatków w złotówkach)) trafia do klasy DataPoint (która ma następujące właściwości: Name, Value,Percentage, HexBackgroundColor) . Jak już wszystkie zgrupowane wartości tam trafią, to dla każdego DataPoint trzeba obliczyć jaki odsetek stanowi wartość Value względem pozostałych (suma Percentage wszystkich DataPoint musi być równa 100%). Tym zajmuje się metoda CalculatePercentages() w klasie PieChart. Potem trzeba nadać każdemu DataPoint unikalny kolor. Tym zajmuje się kolejna metoda SetBgColors().

W tym wypadku wydaje mi się, że klasa PieChart jest potrzebna, bo przetwarza i dostarcza jakieś tam dane. To że akurat jej nazwa to wykres kołowy, to na przykład wykorzystując tę logikę biznesową w aplikacji konsolowej można by wyświetlić te dane w formie tekstowej, fakt że jednak bez kolorów, ale sama klasa nie wymusza na nas tworzenia wykresu kołowego, nie mniej sugeruje, że dane powinny być w takiej formie zaprezentowane oraz że struktura klasy odpowiada wykresowi kołowemu.

2
panDawid napisał(a):

Logika biznesowa polega na ustaleniu jaki kolor będzie miał słupek

Kolor słupka jak dla mnie to też logika prezentacji, jaką wartość dla logiki biznesowej ma wpychanie do niej koloru?

mam też inny obiekt reprezentujący wykres PieChart i tam już trochę jest tej logiki biznesowej. Na wejściu w konstruktorze przekazywane są wydatki (zakupy) użytkownika oraz kategorie, a następnie wydatki te są grupowane, ale nie przez GroupBy bo grupowanie jest zbyt skomplikowane. Potem taki zgrupowany item (Name i Value (wartość wydatków w złotówkach)) trafia do klasy DataPoint (która ma następujące właściwości: Name, Value,Percentage, HexBackgroundColor) .

Pogrupowanie wydatków i obliczenie ich procentowej wartości w całości to logika biznesowa, ale kolor na wykresie na pewno nią nie jest.

sama klasa nie wymusza na nas tworzenia wykresu kołowego, nie mniej sugeruje, że dane powinny być w takiej formie zaprezentowane oraz że struktura klasy odpowiada wykresowi kołowemu.

Nie, niczemu takiemu nie odpowiada. To po prostu statystyka wydatków. Zamiast PieChart możesz mieć ExpensesSummary zawierający kolekcję obiektów CategoryExpensesSummary (zamiast DataPoint), i tyle. To, czy zaprezentujesz te dane na wykresie kołowym, słupkowym, tabelce, czy w ASCIIart to kwestia logiki prezentacji, nie logiki biznesowej.

1 użytkowników online, w tym zalogowanych: 0, gości: 1