Obiekty biznesowe w warstwie dostępu do danych

12

Tworzyłem więc warstwę dostępu do danych za pośrednictwem TDD i podniosłem nieco problem. Wolę nie zaczynać złą drogą, więc pomyślałem, że poproszę was, abyście sprawdzili, czy moje myśli są zgodne z czystą architekturą.

Metody w mojej warstwie dostępu do danych (w skrócie DAL) są dość proste. Są one zgodne z procedurami przechowywanymi w bazie danych (nie ma innego sposobu na wywołanie go w celu zachowania czystości) i zawierają te same parametry, co procedury. Następnie łączą się z bazą danych i zwracają wynik zapytania. Oto jeden przykład:

public int DeleteRecord(int recordId)
{
    recordId.RequireThat("recordId").NotZeroOrLess();

    List<SqlParameter> parameters = new List<SqlParameter>();
    parameters.Add(new SqlParameter { ParameterName = "@RecordId", SqlDbType = SqlDbType.Int, Direction = ParameterDirection.Input, Value = recordId});

    return this.ExecuteNonQuery("DeleteRecord", parameters.ToArray());
}

Działa to doskonale dla tego typu metody, ponieważ nie robię nic znaczącego z zestawem wyników. Chcę tylko upewnić się, że polecenie zadziałało, więc zwrócę wynik braku zapytania, który dotyczy tylko wierszy, i mogę zweryfikować logikę przy użyciu tej liczby.

Powiedzmy jednak, że w innej metodzie DAL chcę załadować rekord. Moja procedura ładowania będzie wykonywana selectsdla wielu tabel i zwraca a DataSet, ale walczę z tym, czy mój DAL powinien tworzyć obiekty biznesowe w ramach metody przy użyciu DataSet, czy też same moje obiekty biznesowe powinny mieć po prostu Load()metodę, która pobiera DataSetz DAL, a następnie w zasadzie się wypełnia.

Wykonanie tego za pośrednictwem DAL spowodowałoby mniej logiki w obiektach biznesowych (nawet jeśli jest to tylko logika wybierania, to wciąż jest logika), ale zepchnęłoby trochę DAL i sprawiałoby wrażenie, jakby naprawdę robił coś, czego nie powinien robię.

Co myślicie?


źródło
Dlaczego nie korzystałeś z Entity Framework?
jfrankcarr
@jfrankcarr - Szczerze mówiąc, głównie dlatego, że nie znam się tak dobrze, jak powinienem. Musiałbym jednak przerobić moje tabele i dodać odpowiednie klucze obce itp., Aby Entity Framework poprawnie rozpoznał relacje. Jednak z ciekawości, gdybym go używał, czy po prostu dokonałbym całego wyboru przy użyciu frameworku z samymi obiektami biznesowymi, czy też nadal byłaby decyzja, gdzie umieścić te zapytania LINQ?
Polecam poświęcić czas na naukę EF. Na początku może się to wydawać nieco zniechęcające, zwłaszcza gdy próbuje się dopasować do istniejącej bazy danych, która ma pewne problemy z projektowaniem, ale warto.
jfrankcarr
Możesz także spojrzeć na NHibernate, jeśli chcesz spojrzeć na inną opcję.
Don 01001100,
@ jfrankcarr - na pewno przyjrzę się temu, ale jak to pasuje do aplikacji do wielopoziomowego dostępu do danych? Czy sama struktura encji mogłaby zostać zaimplementowana w samym DAL, czy w innej warstwie, a nawet w samych obiektach biznesowych?

Odpowiedzi:

4

Twój DAL powinien zwrócić twoje obiekty danych

W idealnym przypadku DAL powinien być obiektem „czarnej skrzynki”, za pomocą którego kod aplikacji może prosić o obiekt danych lub manipulować istniejącymi obiektami danych. Czasami między DAL a kodem aplikacji umieszczana jest inna warstwa, zwana dalej Repository, która dodatkowo dzieli dwie warstwy, chociaż nie zawsze jest to potrzebne.

Ponadto zwykle nie chcesz, aby obiekty biznesowe mogły same tworzyć. Może to spowodować dziury w zabezpieczeniach, w których ktoś może użyć twojej biblioteki, i utworzyć nową instancję obiektu, wywołując .Load(someId)ją, i scala ze sobą dwie warstwy, które powinny być całkowicie oddzielne.

Nie polecam też podania .Load(DataSet ds)metody, ponieważ jeśli zmieni się definicja zestawu danych, będziesz musiał wyśledzić obiekty danych, które używają tego zestawu danych, i zmienić je. Łatwiej jest przechowywać cały kod dostępu do danych w jednym miejscu, więc jeśli zmienisz zapytanie dostępu do danych, wystarczy zmienić warstwę DAL.

Rachel
źródło
Nie ma pewności, w jaki sposób można polegać na jednej warstwie, aby „zwrócić właściwy obiekt”, jeśli definicja „poprawnego obiektu” jest przechowywana na innej warstwie.
TMN
@TMN To było źle sformułowane. Zmieniłem nieco sformułowanie, ponieważ masz rację, kod aplikacji powinien wiedzieć, o jaki obiekt prosi.
Rachel
@Rachel - Gotcha. Zaleca się więc, aby DAL zwrócił instancję dowolnego obiektu biznesowego, prawda? Byłem trochę zdezorientowany przez twoje sformułowanie „obiektów danych”, ale myślę, że to rozumiem. W ten sposób mój kod może żądać obiektu biznesowego z dowolnego miejsca (nie przez nich samych), po prostu wywołując BusinessObject bo = DAL.LoadRecord(id);- brzmi dobrze? Logika odwzorowywania zapytania na samą BO byłaby zawarta w DAL i tylko tam.
1
@Scott Zgadza się, chociaż chciałbym wymienić metody DAL coś takiego Getzamiast Load, jakCustomer c = DAL.GetCustomer(id);
Rachel
2

Moją metodą, nawet przed LINQ-To-SQL i Entity Framework, było posiadanie interfejsu i biblioteki klas abstrakcyjnych, która zapewniała „pisemną umowę” na komunikację między różnymi warstwami aplikacji. Jest to czasami nazywane ontologią , definicją dziedziny pracy. Wszystko, co przechodziło między warstwami, wykorzystywało ten „kontrakt”.

Nie podoba mi się pomysł przekazywania surowych obiektów zestawu danych z warstwy danych do warstwy biznesowej. Widziałem ten wynik w wielu problemach, szczególnie podczas integracji starszych źródeł danych. Może to również bardzo utrudnić nowym osobom przystępującym do projektu zrozumienie, skąd pochodzą dane. Wreszcie, wymaga, aby Twoja warstwa biznesowa zajmowała się przetwarzaniem danych bezpośrednio z bazy danych, co może prowadzić do komplikacji na drodze.

Przykładowy kod, który miałeś, wygląda podobnie do kodu, który miałem przed LINQ. Miałem wspólną klasę funkcji DB, której używałem wewnątrz moich obiektów DAL. Klasy DAL odczytują dane i dopasowują je do obiektów „kontraktu”. Wyniki skalarne, podobnie jak przykład usunięcia, zwracałyby wartość, zwykle wartość logiczną.

jfrankcarr
źródło
1
„Nie podoba mi się pomysł przekazywania surowych obiektów zestawu danych z warstwy danych do warstwy biznesowej”. To. Tysiąc razy to.
Joshua Smith
@jfrankcarr - Mój DAL faktycznie implementuje interfejs i planuję mieć interfejsy do wszystkiego, co przenosi dane z warstwy na warstwę, więc myślę, że nasze pomysły na wzorce pasują do tego. Czy zatem zalecam zmianę metod zwracających bezpośredni wynik ExecuteScalarzapytań, aby zwracały wartości, które mają większy sens w warstwie biznesowej, na przykład bool? Myślę, że inaczej, jest to bardzo podobna odpowiedź do Rachel.
Zwykle zwracam wartość logiczną dla wywołań tworzenia / aktualizacji / usuwania, chyba że potrzebuję liczby rekordów, których to dotyczy. Na przykład mogę zwrócić wartość int, jeśli przechowywany proc przetwarza wiele wierszy zamówienia lub coś w tym rodzaju.
jfrankcarr
0

Twój DAL powinien zwrócić zestaw danych. Zwrócony zestaw danych powinien być obiektem biznesowym, nie powinno być nic, co trzeba by zrobić, poza sprawdzeniem, czy zawiera oczekiwane dane. Jeśli chcesz z tym zrobić więcej, albo próbujesz zrobić zbyt wiele w jednej procedurze przechowywanej, albo nie zwracasz poprawnie danych w procedurze przechowywanej.

Ryathal
źródło
0

Polecam, aby obiekty biznesowe miały konstruktor, który wypełniałby się z zestawu wyników. To usuwa sprzężenie między DAL a warstwą biznesową. Jeśli chcesz całkowicie oddzielić te dwie wartości, utwórz prostą mapę par kolumn nazwa => wartości z zestawu wyników i przekaż ją do konstruktora.

TMN
źródło