Row Data Gateway i Active Record – dostęp do bazy

Kontynuujemy wzorce projektowe dostępu do bazy danych. Dziś uporządkujemy sobie Data Mapper, ORM oraz zajmiemy się wzorcami Row Data Gateway i Active Record.

Zanim przejdziemy do samych wzorców przyjrzyjmy się dwóm najważniejszym praktykom.

ORM czyli Object Relational Mapping to ogólny wzorzec. Z jednej strony mamy obiekty biznesowe. Z drugiej relacyjną bazę danych. ORM oznacza, że mamy jakąś warstwę pośrednią, która potrafi zmapować jedno na drugie.

Data Mapper to jeszcze bardziej ogólne pojęcie. Zakłada, że dane, niekoniecznie obiektowe mapujemy na inne dane. Mamy tu oddzielną warstwę zajmującą się tłumaczeniem danych.

Wzorzec Repozytorium opisany w poprzednim artykule jest Data Mapperem. Co więcej, jest on też wzorcem ORM ponieważ mapuje obiekty na bazę danych. Z drugiej strony Table Data Gateway nie musi być ORM. Można uznać go za wzorzec Data Mapper gdyż nasze dane aplikacji mapuje na zapytania SQL.

Przyjrzyjmy się teraz dwóm kolejnym wzorcom. Row Data Gateway i Active Record to bardzo podobne wzorce.

Row data gateway

Ten wzorzec koncentruje się na zmapowaniu pojedynczego wiersza tabeli do naszego języka programowania. Nie ma tu już dowolności w implementacji logiki biznesowej. Chcemy tylko w łatwy sposób przenosić cały rekord między bazą danych a naszą aplikacją.

Jeśli wrócimy do naszego przykładu z samochodami, klasa mogłaby wyglądać tak:

class CarRow {
   public $id;
   public $name;
   public $model;

   public function save() { ... }
   public function delete() { ... }
   public function reload() { ... }
}

Jeden obiekt to jeden rekord w tabeli. Możemy stworzyć nowy obiekt, przypisać mu dane i wywołać save(). Możemy też pobrać rekord z bazy, zmienić jego dane i tak samo zapisać. Dostępne są jedynie metody operujące na bazie danych. Nie implementujemy tu logiki biznesowej.

Pobieranie rekordów przeważnie wykonujemy za pomocą innego obiektu, np. Modelu. Taki model zwraca nam np. metodą find() obiekty Row Data Gateway.

Wzorzec ten nie mówi nam jak aktualizować powiązane obiekty. Musimy sami zadbać o poprawność relacji. Próba zapisania niepoprawnego obiektu zapewne skończy się błędem bazy danych.

Rzadko używa się tego wzorca. Najczęściej, gdy automatycznie generujemy kod na podstawie bazy danych. Brak logiki biznesowej nieco utrudnia korzystanie z niego.

Active Record

Aktywne rekordy są prawie identyczne jak poprzedni wzorzec. Różnica polega na tym, że możemy dodać tu logikę biznesową. Nasza klasa może być bogatsza o walidację, automatyczne przeliczanie danych, uzupełnianie lub inne ważne metody. Możemy też (ale nie musimy) w samej klasie umieścić statyczne metody szukające i zwracające obiekty active record. Przykład będzie podobny do poprzedniego:

class CarActiveRecord {
   public $id;
   public $name;
   public $model;

   // nowe metody szukająca
   static public getById($id) { ... }
   static public find($criteria) { ... }

   // rozszerzamy save o walidację
   public function save() {
      if ($this->validate) ...
   }

   // walidacja naszych rekordów
   pivate function validate() { ... }

}

Możemy też zapisać tu logikę relacji między obiektami. Niech nasza tabela ma klucz obcy, np. pole owner. Wtedy w klasie pole $owner może zawierać kolejny Active Record. Zapisanie jednego rekordu może sapisać wszystkie związane z nim dane.

Active record pojawia się w wielu frameworkach. Często najpierw generujemy kod na podstawie bazy otrzymując Row Data Gateway. Potem dopisujemy własne funkcje tworząc Active Record.

Wadą tego wzorca jest złamanie zasady jednej odpowiedzialności. Jeden obiekt służy nam zarówno jako obiekt biznesowy jaki do komunikacji z bazą danych. Porównując do samochodu – wygląda to tak, jakby auto oprócz jeżdżenia musiało też umieć samo zaparkować. Na dodatek nie możemy zmienić adresu garażu. Komunikacja z bazą może być co najwyżej wstrzyknięta w Active Record ale nadal to obiekt musi zajmować się przechowywaniem samego siebie.

Robert C. Martin krytykuje ten wzorzec w taki sposób:

Wiele osób umieszcza logikę biznesową w swoich klasach Active Record. To prowadzi do dylematu – po której stronie jest ten wzorzec? Czy jest on obiektem czy strukturą danych?

Z tego powodu niektórzy uważają ActiveRecord za antywzorzec. We wzorcu Row Data Gateway sytuacja była jasna – wzorzec należał do bazy danych. Active Record ułatwia mieszanie pojęć i poziomów abstrakcji.

Na koniec

Czy to znaczy, że powinniśmy wyrzucić Active Record do kosza? Niekoniecznie. Jak zwykle warto stosować zasadę właściwego narzędzia do właściwej pracy. Jeśli nasza aplikacja nigdy nie będzie zmieniać dostępu do bazy – możemy ostrożnie rozważyć Active Record. Gdy tworzymy niewielki, prosty program, tym bardziej Active Record ułatwi nam pracę.

Z drugiej strony Row Data Gateway łatwo wygenerować w wielu frameworkach. Może być podstawą do zbudowania np. repozytorium lub innego wzorca dostępu do bazy.

Warto zapoznać się z tym artykułem o wzorcach przechowywania danych w bazie.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *