存储库的职责是为您的域对象提供非技术接口。
实际的查询实现可以保留在存储库中,但最好将其封装在一个只负责构建查询的类中。
控制器(或应用程序服务)不应该包含查询的实现细节,它们应该只调用存储库(好像它是一个集合)或查询工厂(如果您想自己处理查询或结果)。
理想情况下,控制器应该只有很少的代码,只需调用“SRPecialized”服务。
您可以尝试实现这个简单的架构选项。它适合 Doctrine 或任何其他 ORM 库。它尊重关注点分离,正如Martin Fowler 的 PoEAA 书中所预期的那样,为持久性细节和组合的抽象提供了一个层。
为什么是存储库?
存储库的职责是处理一个集合,所以实际的操作代码应该去那里。查询应该在其他地方收集/组合/生成,例如在工厂类中(如前所述)。这允许在不触及存储库中保存的域/应用程序逻辑部分的情况下切换底层持久层实现。
您可以从中派生,具体取决于您的应用程序,但是当查询开始是动态的、带有多个参数或只是太长时,我倾向于将每个查询抽象到其自己的类中,然后从存储库中调用它们。
查询构造
每个查询都可以有一个由存储库调用的静态构造函数。对于更复杂的情况,我QueryFactory
为可用的自定义查询定义了一个简单的入口点;这将允许更灵活的依赖配置,这要归功于 Symfony DI。
实现示例
namespace App\Repository;
// use statements
class ArticleRepository extends Doctrine\ORM\EntityRepository
{
public function getPublished()
{
$query = App\Query\Article::getPublished(\DateTimeImmutable $after = null);
$query_iterator = new Doctrine\ORM\Tools\Pagination\Paginator($query);
// ... run query ...
}
}
因此您可以为查询的特定变体使用自定义方法(例如 getPublished、getUnpublished、getNewArticles 或其他)。
namespace App\Query;
// use statements
final class Articles
{
public static function getPublished(\DateTimeImmutable $after = null)
{
return new self(Article::PUBLISHED_STATE, $this->sqlFormat($after));
}
public function __constructor(string $status, \DateTimeImmutable $date_filter = null) {
// ... you own complex query logic ...
}
}
// ...
$published_query = Articles::getPublished();
// ...
- 由于通过静态构造函数进行代理,并且在回溯堆栈上的错误时还有一个文件要经过,因此维护变得有点烦人。虽然它的扩展性更好。
- 一般来说,它在项目中提供了一个良好且灵活的组织,可以产生许多复杂或非标准的查询。
- 在我看来,存储库模式应该与持久性级别的实现细节无关(幕后使用什么数据库适配器:SQL?NoSQL?)。
- 对来自不同存储库的查询常见的部分的组合和抽象更容易实现。