Ciekawe, jakie są wady używania wzorca ActiveRecord do dostępu do danych / obiektów biznesowych. Jedyne, co mogę wymyślić z góry, to to, że narusza zasadę pojedynczej odpowiedzialności, ale wzorzec AR jest na tyle powszechny, że sam ten powód nie wydaje się „wystarczająco dobry”, aby uzasadnić nieużywanie go (oczywiście moje widok może być wypaczony, ponieważ często żaden kod, z którym pracuję, nie jest zgodny z żadną z zasad SOLID).
Ja osobiście nie fanem ActiveRecord (z wyjątkiem pisania Ruby on Rails aplikacji, gdzie AR czuje się „naturalne”), ponieważ czuje się jak w klasie robi zbyt wiele, a dostęp do danych nie powinien być aż do samej klasie Poradzić sobie. Wolę używać repozytoriów, które zwracają obiekty biznesowe. Większość kodu, z którym pracuję, ma tendencję do używania odmiany ActiveRecord w postaci (nie wiem, dlaczego metoda jest wartością logiczną):
public class Foo
{
// properties...
public Foo(int fooID)
{
this.fooID = fooID;
}
public bool Load()
{
// DB stuff here...
// map DataReader to properties...
bool returnCode = false;
if (dr.HasRows)
returnCode = true;
return returnCode;
}
}
lub czasami bardziej „tradycyjny” sposób posiadania public static Foo FindFooByID(int fooID)
metody dla wyszukiwarek i coś podobnego public void Save()
do zapisywania / aktualizacji.
Rozumiem, że ActiveRecord jest zwykle o wiele prostszy we wdrożeniu i użyciu, ale wydaje się nieco zbyt prosty w przypadku złożonych aplikacji i możesz mieć bardziej solidną architekturę, zamykając logikę dostępu do danych w repozytorium (nie wspominając o łatwiejszej wymianie strategie dostępu do danych, np. może korzystasz z Przechowanych Procs + DataSets i chcesz przejść na LINQ lub coś takiego)
Jakie więc są inne wady tego wzorca, które należy wziąć pod uwagę przy podejmowaniu decyzji, czy ActiveRecord jest najlepszym kandydatem do pracy?
źródło
Największą wadą aktywnego zapisu jest to, że twoja domena zazwyczaj jest ściśle związana z określonym mechanizmem trwałości. Gdyby ten mechanizm wymagał globalnej zmiany, być może z trwałości opartej na plikach na trwałość opartą na DB, lub między ramami dostępu do danych, KAŻDA klasa, która implementuje ten wzorzec, może się zmienić. W zależności od języka, frameworka i projektu nawet coś tak prostego, jak zmiana lokalizacji bazy danych lub kto „jest właścicielem”, może wymagać przejścia przez każdy obiekt w celu zaktualizowania metod dostępu do danych (jest to rzadkie w większości języków, które zapewniają łatwy dostęp do konfigurowania plików za pomocą parametrów połączenia).
Ogólnie wymaga to również powtórzenia się. Większość mechanizmów trwałości ma wiele wspólnego kodu, aby połączyć się z bazą danych i rozpocząć transakcję. DRY (Don't Repeat Yourself) powiedziałby ci jako programistę do scentralizowania takiej logiki.
Utrudnia to także operacje atomowe. Jeśli grupa obiektów musi zostać zapisana w trybie „wszystko albo nic” (na przykład faktura i jej faktury i / lub wpisy klienta i / lub GL), jeden z obiektów musi wiedzieć o wszystkich innych obiektach i kontrolować ich trwałość ( co rozciąga się na zakres obiektu kontrolującego; duże połączone ze sobą rekordy mogą łatwo stać się „obiektami boskimi”, które wiedzą wszystko o ich zależnościach), lub kontrola nad całą transakcją musi być obsługiwana spoza domeny (i w takim przypadku dlaczego używasz AR?)
Jest to również „złe” z perspektywy obiektowej. W prawdziwym świecie faktura nie wie, jak się złożyć, więc dlaczego obiekt kodu faktury miałby wiedzieć, jak zapisać się w bazie danych? Oczywiście zbyt religijne przestrzeganie „obiektów powinno modelować tylko to, co potrafią ich odpowiedniki w świecie rzeczywistym”, prowadziłoby do anemicznych modeli domenowych (faktura również nie wie, jak obliczyć własną sumę, ale dzielenie obliczenia tej sumy na inny obiekt jest ogólnie uważany za zły pomysł).
źródło
Podstawową wadą jest to, że sprawia, że model domeny jest złożony, ponieważ zawiera nie tylko logikę biznesową, ale także informacje o trwałości.
Tak więc rozwiązaniem jest wykorzystanie implementacji ORM Data Mappera . To oddziela warstwę trwałości i jesteśmy teraz bardziej skoncentrowani na logice biznesowej encji. Doctrine to ORM Mapera danych .
Ale to podejście ma również pewną złożoność. W przypadku zapytań w zbyt dużym stopniu zależy od Mapera danych, sprawia, że środowisko zorientowane na zapytania. Aby to uprościć, wprowadzono kolejną warstwę między Modelem Domeny, a Maperem Danych nazywa się Repozytorium .
Repozytorium wyodrębnia warstwę trwałości. Wyczuwa programowanie obiektowe w tym sensie, że jest kolekcją wszystkich obiektów tego samego typu (jak wszystkie encje przechowywane w tabeli bazy danych) i możesz wykonywać na nich operacje jako operacje kolekcjonowania, dodawania , usuwania . zawiera itp.
Na przykład dla encji użytkownika będzie UserRepository, które reprezentują kolekcję tego samego typu obiektów użytkownika (przechowywanych w tabeli użytkowników), nad którymi można wykonać operację. W celu zapytania do tabeli użytkowników wykorzystuje Mapowanie danych użytkownika, ale wyodrębnił ją do modelu domeny Użytkownik .
Wzorzec repozytorium jest rodzajem warstwy dostępu do danych , inną są różnice tylko w obiekcie dostępu do danych Repozytorium ma funkcję Agreguj root
źródło