Repozytoria to w sumie nic innego jak unikalna nazwa, dla pewnej kategorii obiektów-serwisów, służących wyłącznie pobieraniu obiektów ze źródła danych (w przypadku Doctrine: bazy danych). Są więc analogiczne do obiektów typu DAO, służących utrwalaniu/usuwaniu danych w źródle danych.
W zależności od wymagań projektu można zrezygnować z części poniższych elementów i uprościć całość.
1. DAO - czyli obiekt pozwalający na zapisanie, aktualizację i usunięcie danych; Raczej na wewnętrzny użytek warstwy modelu; W przypadku, gdy korzystamy z Doctrine obiekt taki będzie właściwie wszystko przekazywał do wykonania Doctrine'owskiemu EntityManagerowi (metody persist(), delete()/remove() - nie pamiętam jaką tam nazwę ta ostatnia ma); Obiekt ten raczej nie powinien wykonywać jakiś dodatkowych operacji; Ewentualnie można w nim dodać bardzo podstawowe odczytywanie danych, najczęściej jedynie po kluczu głównym czy jakiejś tablicy
właściwość => wartość.
2. Rozbudowana warstwa serwisowa/usługowa, realizująca logikę biznesową. To tutaj znajdą się usługi pozwalające na:
2.1. Wybieranie danych, na podstawie różnych kryteriów, różnymi metodami, np.
$userLookupServiceA->findActiveUsersWithRange($range),
$userLookupServiceB->find(array $criteria)->orderBy($property, $direction)->limit($offset, $limit)->filter(callback $filter)->getResults() czy proste
$userLookupServiceC->find($pk). Te usługi mogłyby wykonywać zapytania DQL.
2.2. Zarządzenie tymi danymi, czyli ich utrwalanie i kasowanie. Takie usługi wewnętrznie będą korzystać właśnie z DAO. To tutaj powinna zostać rozpoczęta transakcja, sprawdzone uprawnienia użytkownika do wykonania takiej akcji, czy wreszcie zmodyfikowane innych obiektów w bazie danych. Jest to też dobre miejsce by wywołać jakieś zdarzenia (
eventy), np.
entity_a.pre_delete,
entity_a.post_delete. Pod te zdarzenia, będą mogły być podpięte inne usługi - np. usługa wysyłająca emaila do użytkownika z informacją o usunięciu danych.
2.3. W końcu, faktyczna logika biznesowa, czyli np. usługa pozwalająca na przesłanie kredytów/pieniędzy pomiędzy różnymi użytkownikami, czy złożeniu zamówienia. Na przykład w przypadku, gdy w systemie istnieją użytkownicy, którzy mogą kupić/dostać tzw. kredyty, które później wymieniają na produkty/dodatkowe funkcje w serwisie, interfejs dla usługi zajmującej się operowaniem na pojedynczym koncie (kredytowym) użytkownika mógłby wyglądać tak:
interface UserCreditManagerInterface {
public function getBalance();
public function setBalance(int $credits);
public function zasil/obciążSaldo(int $credits); // zapomniałem jak to się ładnie po angielsku nazywa ;)
public function transferCredits(User $other, int $amount);
// jakieś inne metody, typu "czy saldo jest dodatnie czy ujemne"
// jednak tutaj już nie powinno być operacji typu "wymień 100 kredytów na miesiąc konta premium"
}
4. Oczywiście wszystkie opisane powyżej elementy to interfejsy, do których należałoby utworzyć konkretną implementację.
5. Takie obiekty jak EntityManager na dobrą sprawę nigdy nie powinny pojawić się kontrolerze aplikacji. Najlepiej gdyby jedynie interfejsy (nie konkretne implementacje) były znane
Jak widzisz da się to dosyć mocno rozdrobić, a 95% - jak nie więcej - aplikacji nie potrzebuje tak silnego
oddzielenia tych elementów. Nie mniej jednak, Twój problem, tj. nietrywialne usunięcie obiektu z bazy danych, powinno zostać wykonane w jakiejś usłudze opisanej w punkcie #2.3.