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 selects
dla 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 DataSet
z 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?
Odpowiedzi:
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.źródło
BusinessObject bo = DAL.LoadRecord(id);
- brzmi dobrze? Logika odwzorowywania zapytania na samą BO byłaby zawarta w DAL i tylko tam.Get
zamiastLoad
, jakCustomer c = DAL.GetCustomer(id);
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ą.
źródło
ExecuteScalar
zapytań, aby zwracały wartości, które mają większy sens w warstwie biznesowej, na przykładbool
? Myślę, że inaczej, jest to bardzo podobna odpowiedź do Rachel.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.
źródło
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.
źródło