Warstwa Abstrakcji - stanowi izolację pomiędzy kodem programisty a zewnętrznymi modułami, pozwala odseparować system od jego zależności przez co czyni go podatnym na zmiany i aktualizacje. Przywiązując system do innej usługi czynimy go słabym.

Repository

Repozytorium stanowi separację pomiędzy domeną a warstwą persystencji. Repozytorium dostarcza interfejs kolekcji w celu dostępu do danych przechowywanych w bazie danych, systemie plików czy zewnętrznej usłudze. Dane zwracane są w postaci obiektów.

Przykładowa implementacja

interface ObjectRepository
{
    public function add(Object $object);

    public function getById(Id $id);

    public function remove(Object $object);

    public function contains(Object $object);
}

final class InMemoryRepository implements ObjectRepository
{
    private $objects;

    public function __construct()
    {
        $this->objects = [];    
    }

    public function add(Object $object)
    {
        $this->objects[(string) $object->getId()] = $object;
    }

    public function getById(Id $id)
    {
        if (!array_key_exists((string) $id, $object)) {
            throw new UserNotFoundException();
        }

        return $this->objects[(string) $id];
    }

    public function remove(Object $object)
    {
        unset($this->objects[(string) $object->getId()]);
    }

    public function contains(Object $object)
    {
        return array_key_exists((string) $object->getId(), $this->objects);
    }
}

Kiedy używać

Repozytorium reprezentuje kolekcję encji tego samego typu. Przykładem może być kolekcja użytkowników. Ponieważ większość encji występuje w postaci kolekcji, repozytorium praktycznie należy stosować za każdym razem kiedy potrzebujemy przeszukać kolekcję encji.
W przypadkach gdzie kolekcja ma być przeszukana przy użyciu większej ilości kryteriów, można wprowadzić dodatkowy obiekt Criteria który przyjmie wszystkie parametry wyszukiwania i na ich podstawie zwróci pasujące encje.

Przykład użycia

Specyfikacja

  • należy utworzyć usługę, która zarejestruje nowego użytkownika
  • podczas rejestracji należy upewnić się czy użytkownik o podanym adresie email nie został już wcześniej stworzony
  • wykorzystując repozytorium należy znaleźć użytkownika o konkretnym adresie email
  • repozytorium powinno bazować na DAO (Data Access Object), który bezpośrednio wykorzystuje data storage

Implementacja


class User { private $email; public function __construct(Email $email) { $this->email = $email; } public function getEmail() { return $this->email; } } interface UserRepository { public function add(User $user); public function getByEmail(Email $email); public function hasUserWithEmail(Email $email); } final class DAORepository implements UserRepository { private $dao; public function __construct(UserDAO $userDAO) { $this->dao = $userDAO; } public function add(User $user) { $this->dao->saveNew([ 'email' => (string) $user->getEmail() ]); } public function getByEmail(Email $email) { $userData = $this->dao->getUserDataByEmail((string) $email); if (empty($userData)) { throw new UserNotFoundException(); } return new User(new Email($userData['email'])); } public function hasUserWithEmail(Email $email) { return $this->dao->hasUserDataWithEmail((string) $email); } } final class UserRegistrationService { private $users; public function __construct(UserRepository $repository) { $this->users = $repository; } public function register(array $data) { $email = new Email($data['email']); if ($this->users->hasUserWithEmail($email)) { throw new EmailAlreadyUsedException; } $this->users->add(new User($email)); } } class UserController { private $userRepository; private $templateEngine; public function __construct(UserRepository $userRepository, TemplateEngine $templateEngine) { $this->userRepository = $userRepository; $this->templateEngine = $templateEngine; } /** * @Route('/user/{email}') */ public function displayUserAction($email) { try { $user = $this->userRepository->getByEmail(new Email($email)); } catch (UserNotFoundException $e) { throw new HttpNotFoundException; } return new Response($this->templateEngine->render('user_template.html', ['user' => $user])); } }

Omówienie

W przykładzie pokazano najbardziej istotną cechę repozytorium a jest nią dostarczenie interfejsu kolekcji reprezentującej zbiór encji. Poza wyżej wymienionymi przykładami, kolekcja pozwala również na usunięcie elementu. W bardziej złożonych przypadkach, metody getByXXX lub findByXXX można zastąpić metodą getBy(Criteria $criteria) gdzie obiekt Criteria zawiera wszystkie dane na podstawie których należy przeszukać kolekcję.

Błędy?

Znalazłeś błąd?
Chciałbyś podać lepszy przykład użycia?
A może nie zgadzasz się z implementacją przykładu?

Edytuj na Github.com Popraw lub zgłoś błąd

Pytania

Masz pytania odnośnie omawianego wzorca? Chciałbyś zapytać czy wzorzec pasuje do problemu, który aktualnie rozwiązujesz? Dobrze się składa, to jest idealne miejsce.

Błędy, sugestie ich poprawy, dyskusję na temat poprawności implementacji danego przykładu prosimy prowadzić poprzez Github Issues
Akceptuję

Ten serwis używa plików cookies. Więcej o plikach cookies.