Я использую такой подход:
Есть три интерфейса EntityInterface, RepositoryInterface, RepositoryDriverInterface
interface EntityInterface extends Serializable {
public function getId();
}
interface RepositoryInterface {
public function getDriver();
public function findById($id, EntityInterface $Entity);
public function save(EntityInterface $Entity);
}
interface RepositoryDriverInterface {
public function set($id, $data);
public function get($id);
}
Драйвер репозитория работает с базой данных. Сам репозиторий занимается сериализацией/десериализацией сущностей. Примеры драйверов:
Драйвер, работающий с массивами:
class RepositoryDriverArray implements RepositoryDriverInterface {
private $data = [];
public function set($id, $data) {
$this->data[$id] = $data;
}
public function get($id) {
return $this->data[$id];
}
}
Драйвер работающий с Redis'ом:
class RepositoryDriverRedis implements RepositoryDriverInterface {
protected $Redis = null;
public function __construct(Client $Redis) {
$this->Redis = $Redis;
}
public function set($id, $data) {
$jsonData = json_encode($data);
$this->Redis->hset($this->getContainerName(), $id, $jsonData);
}
public function get($id) {
$jsonData = $this->Redis->hget($this->getContainerName(), $id);
$data = json_decode($jsonData, true);
return $data;
}
public function getContainerName() {
return 'myContainer';
}
}
Пример реализации самих репозиториев:
abstract class AbstractRepository implements RepositoryInterface {
protected $RepositoryDriver = null;
public function __construct(RepositoryDriverInterface $RepositoryDriver) {
$this->RepositoryDriver = $RepositoryDriver;
}
public function save(EntityInterface $Entity) {
$this->getDriver()->set($Entity->getId(), $Entity->serialize());
}
public function findById($id, EntityInterface $Entity) {
$data = $this->getDriver()->get($id);
$Entity->unserialize($data);
return $Entity;
}
public function getDriver() {
return $this->RepositoryDriver;
}
}
Далее, пишем бизнес-логику:
class User implements EntityInterface {
// TODO: Implement methods
}
class UserRepository extends AbstractRepository {}
И вот так работаем со всей этой системой:
$Redis = new Client([/* connection params */]);
$RedisDriver = new RepositoryDriverRedis($Redis);
$UserRepository = new UserRepository($RedisDriver);
$User = new User();
$UserRepository->save($User);
$User = new User();
$UserRepository->findById(1, $User);
Пишем юнит тесты? Подменяем драйвер репозитория:
$ArrayDriver = new RepositoryDriverArray();
$UserRepository = new UserRepository($ArrayDriver);
$User = new User();
$UserRepository->save($User);
$User = new User();
$UserRepository->findById(1, $User);
Такую систему очень просто тестировать. Всю "рутину" можно обернуть в различные фасады, "конфигураторы", провайдеры.
Я написал лишь примеры, конечно, в реальной системе больше возможностей драйвера, репозиториев.
Кэширование стоит делать на уровне репозитория. Драйвер занимается только сохранением/загрузкой данных.