Business Objects - Kontenery czy funkcjonalne?

19

To pytanie zadałem jakiś czas temu na temat SO, ale może być lepiej omówione tutaj ...

Gdzie pracuję, posunęliśmy się tam iz powrotem na ten temat wiele razy i szukasz kontrola poprawności. Oto pytanie: Czy Business Objects być kontenery danych (więcej jak DTOs ) lub powinny one również zawierać logikę, które mogą wykonywać pewne funkcje na tym obiekcie.

Przykład - Take obiektu klienta, to prawdopodobnie zawiera pewne wspólne właściwości (nazwa, identyfikator itp), że obiekt powinien obejmować również funkcje klienta (SAVE, calc, etc.)?

Jedna linia rozumowania mówi oddzielić obiekt z funkcjonalności (jeden odpowiedzialność zleceniodawcy) i umieścić funkcjonalność w warstwie logiki biznesowej lub obiektu.

Druga linia rozumowania mówi, nie, jeśli mam obiekt klienta, po prostu chcę zadzwonić Customer.Save i być z nim zrobić. Dlaczego muszę wiedzieć o innej klasy, aby zaoszczędzić klientowi jakbym spożywania obiekt?

Nasze ostatnie dwa projekty miały obiekty oddzielone od funkcji, ale debata została ponownie podniesiona nad nowym projektem.

Co ma sens i dlaczego?

Walter
źródło
1
Czy możesz powiedzieć nam więcej o swoim projekcie? Charakter projektu będzie określić, które rozwiązanie jest lepsze.

Odpowiedzi:

5

Jeśli uważasz, że klient jest częścią modelu domeny, wówczas sensowne jest (szczególnie w kontekście DDD, ale nie tylko) posiadanie zarówno właściwości, jak i operacji dla tego obiektu.

Z powiedział, że jednak myślę, że przykład użyłeś jest biedny jeden, i jest przyczyną sporu. Jeśli mówimy o trwałości, następnie Klienci zazwyczaj nie „Zapisz” sami; cokolwiek używasz do wytrwałości. To ma sens, że każdy rodzaj wytrwałości powinien należeć do warstwy Trwałość / partycji. Jest to generalnie sposób myślenia za wzorcem repozytorium. ** Metoda taka jak Customer.UpgradeWarranty () lub Customer.PromoteToPreferred () sprawia, że ​​argument jest bardziej zrozumiały.

To też nie usuwa możliwość posiadania DTOs . Rozważmy sytuację, w której masz zamiar przekazać informacje o klientach do zdalnego serwisu, na przykład. Klient może nie mieć sensu tworzyć własnego DTO na potrzeby transportu, co stanowi problem architektoniczny, ale można by to zrobić w warstwie trwałej lub w warstwie sieciowej / partycji / kodzie / czymkolwiek. W tym przypadku, taki objectcould mają metody, które wyglądają jak to

public static CustomerDTO GetDTO(Customer c) { /* ... */ }

public static Customer GetCustomer(CustomerDTO cdto) { /* ... */ }

Tak, w skrócie, to ma sens, aby operacje na obiekcie domeny, które są zbieżne z operacji logicznych w domenie.

Google za „Trwałą ignorancję” za szereg dobrych dyskusji na ten temat ( to pytanie SO i zaakceptowana odpowiedź to dobre miejsce na początek).

** To staje się trochę zabłoconych z pewnym OR / M oprogramowania, gdzie jesteś zmuszony do dziedziczenia z przetrwałym bazy, która ma „Zapisz” metody.

Steven Evers
źródło
3

I rzeczywiście niedawno przerobione jakiś kod, aby oddzielić obiektów z danymi. Powodem jest to, że dane są uzyskiwane za pomocą oddzielnej usługi, i to o wiele łatwiej jest po prostu przekazać surowych danych do / z serwera zamiast przechodzenia całego obiektu iz powrotem i mając do czynienia z błędami walidacji na serwerze.

W przypadku małych projektów posiadanie obiektów zawierających logikę biznesową i dane są prawdopodobnie w porządku, ale w przypadku dużych projektów lub projektów, które mogą się rozwijać z czasem, zdecydowanie oddzieliłbym warstwy.

Rachel
źródło
3

Myślę, że oba sposoby na to mogą mieć swoje zalety, ale przyjrzałbym się temu z perspektywy obiektowej lub zorientowanej na domenę: jakie są obowiązki różnych klas i obiektów.

Tak więc zadałbym sobie to pytanie w konkretnym przypadku: czy klient powinien wiedzieć, jak się uratować?

Dla mnie odpowiedź brzmiałaby „nie”: logicznie dla mnie nie ma to sensu, a klient nie powinien w ogóle wiedzieć o jakichkolwiek ramach trwałości (podział obowiązków).

Jeśli chodzi o przykłady SnOrfusa z Customer.UpgradeWarranty () lub Customer.PromoteToPreferred (), są one oczywiście bardziej zorientowane na logikę biznesową niż Customer.Save (). Istnieją różne podejścia do tego, ale ponownie, jeśli zapytasz siebie, czy klient powinien być w stanie uaktualnić swoją gwarancję, odpowiedź może być zarówno tak, jak i nie, w zależności od tego, jak na to spojrzysz:

  • Tak: klient może oczywiście uaktualnić swoją gwarancję
  • no: klient mógłby zapytać zostać zmodernizowane, ale aktualizacja jest wykonywana przez kogoś innego

Powrót do pierwotnego pytania; to, który sposób będzie bardziej sensowny, będzie prawdopodobnie zależeć od tego, kogo zapytasz i preferowanej metody, ale najważniejsze jest prawdopodobnie trzymanie się jednego ze sposobów.

Z mojego doświadczenia wynika jednak, oddzielając dane i logikę (Biznes) sprawia, że ​​dla uproszczenia architektury, choć nie tak ekscytujące.

Vetle
źródło
2

Myślę, że specjalnie mówić o różnicy między ActiveRecordwzorem aRepository wzorem. W pierwszym przypadku istoty wiedzą, jak się zachować, aw drugim repozytorium wie o trwałości. Myślę, że te ostatnie oferuje lepsze oddzielenie obawy.

W szerszym znaczeniu, jeśli byty zachowują się bardziej jak struktura danych, to nie powinny mieć zachowań, ale jeśli mają zachowania, to nie powinny być używane jak struktura danych. Przykład:

Struktura danych:

class CustomerController
{
    public int MostRecentOrderLines(Customer customer)
    {
        var o = (from order in customer.Orders
                orderby order.OrderDate desc
                select order).First();
        return o.OrderLines.ToList().Count;
    }
}

Struktura nie-danych:

class Customer
{
    public int MostRecentOrderLines()
    {
        // ... same ...
    }
}

W pierwszym przypadku można bez problemu poruszać się po drzewie struktury danych modelu, z dowolnego miejsca w działającym kodzie i jest to w porządku, ponieważ traktuje się go jak strukturę danych. W tym drugim przypadku klient bardziej przypomina usługę dla danego klienta, więc nie powinieneś wywoływać metod w wyniku. Wszystkie rzeczy, które chcesz wiedzieć o kliencie, powinny być dostępne poprzez wywołanie metody na obiekcie klienta.

Czy chcesz, aby Twoje podmioty były strukturami danych lub usługami? Aby zachować spójność, lepiej trzymać się jednego. W pierwszym przypadku umieść logikę typu „usługa” w innym miejscu, a nie w encji.

Scott Whitlock
źródło
1

Naprawdę nie potrafię odpowiedzieć na twoje pytanie, ale wydaje mi się zabawne, że rozmawiamy również o jednym z naszych projektów w szkole. Sam chciałbym oddzielić logikę od danych. Ale wielu moich kolegów z klasy twierdzi, że obiekt musi przechowywać wszystkie dane logiczne ORAZ.

Spróbuję podsumować fakty, które przywołują:

  • Obiekt klasy biznesowej reprezentuje rzecz, dlatego wszystkie dane i logika powinny być zawarte. (np. jeśli chcesz otworzyć drzwi, robisz to sam, nie pytasz kogoś innego. zły przykład, wiem)

  • Łatwiej zrozumieć kod i funkcjonalność obiektu bu.

  • Mniejsza złożoność w projekcie

Mówię im, że są leniwi i zrobiłbym to tak:

  • Tak, klasy biznesowe reprezentują rzeczy, więc przechowują dane. Ale źle się uratować, a nawet skopiować. Nie możesz tego zrobić w rl.

  • Uczynienie obiektu odpowiedzialnym za wszystko sprawia, że ​​nie jest on dobry z punktu widzenia trwałości i konserwacji w przyszłości. Jeśli masz oddzielną klasę odpowiedzialną za zapisywanie, jeśli dodasz nowy obiekt do projektu, możesz łatwo wdrożyć jego funkcję zapisywania. zamiast kodować to wszystko ponownie w tej klasie.

  • Mając jeden obiekt w stanie utrwalić dane, obiekt ten może utrzymywać połączenie z bazą danych, a cały ruch do bazy danych jest kierowany do tego obiektu. (zasadniczo jest to warstwa dostępu do danych), w przeciwnym razie cały obiekt biznesowy musiałby mieć połączenie. (co dodaje złożoności)

Tak czy inaczej, zapytam nauczyciela. Kiedy to zrobię, opublikuję jego odpowiedź tutaj, jeśli chcesz. ;)

edytować:

Zapomniałem wspomnieć, że ten projekt dotyczył księgarni.

Emerion
źródło
Twoi koledzy mają rację, chyba że są one mylące logiki biznesowej z innymi formami logiki (w tym przypadku logika architektoniczny lub utrzymywanie).
Steven Evers,
1

Odpowiedź naprawdę zależy od tego, jaka jest twoja architektura / projekt - architektura DDD z modelem domeny będzie bardzo różnić się od modelu skoncentrowanego na danych CRUD, jeśli chodzi o projektowanie obiektów.

Ogólnie jednak pamiętaj, że w projektowaniu obiektowym próbujesz obudować stan i ujawnić zachowanie. Oznacza to, że zwykle będziesz próbował powiązać stan z zachowaniem jak najbliżej tego zachowania - gdy tego nie zrobisz, będziesz zmuszony ujawnić stan w takiej czy innej formie.

W modelu domeny chcesz oddzielić modelu biznesowego z obawy infrastruktury - tak absolutnie chcesz uniknąć metod, takich jak „.save”. Nie pamiętam, kiedy ostatni raz „uratowałem” klienta w prawdziwym życiu!

W aplikacji CRUD dane są obywatelami pierwszej klasy, więc „.Save” jest całkowicie odpowiednie.

Większość spore rzeczywistych aplikacjach będą mieć mieszankę tych paradygmatów - model domeny gdzie szybko się zmieniają lub skomplikowanych zasad biznesowych, DTOs do przesyłania danych przez granice, CRUD (lub coś pomiędzy CRUD i modelu domeny, takie jak ActiveRecord) gdzie indziej. Nie ma jednej uniwersalnej zasady.

FinnNk
źródło
0

Przez chwilę zastanawiałem się nad tym samym pytaniem - myślę, że komponent danych i komponent logiczny muszą być osobne. Wierzę w to, ponieważ pozwala ci zachować właściwe nastawienie, jeśli chodzi o logikę biznesową jako interfejs do danych, które mają znaczenie.

Zmodyfikowałbym również punkt Scotta Whitlocka z góry (z wyjątkiem tego, że nie jestem nowym członkiem), klasy danych lub logiki biznesowej nie powinny tak naprawdę martwić się o to, jak obiekt jest przechowywany w sposób trwały.

Biorąc to pod uwagę, jeśli masz do czynienia z istniejącym produktem, o ile masz czyste, umowne interfejsy - jest to w porządku i również możliwe do utrzymania ...

heretik
źródło