【问题标题】:Are Doctrine2 repositories a good place to save my entities?Doctrine2 存储库是保存我的实体的好地方吗?
【发布时间】:2012-01-12 06:53:03
【问题描述】:

当我阅读有关存储库的文档时,通常是使用实体和集合,但以“只读”方式。

从来没有例子说明存储库有像insertUser(User $user)updateUser(User $user) 这样的方法。

但是,在使用 SOA 时,Service 不应该与 Entity Manager 一起使用(没错,不是吗?)所以:

  1. 我的服务应该知道全局 EntityManager 吗?
  2. 我的服务是否应该只知道使用的存储库(比如 UserRepository 和 ArticleRepository)

从这两个问题,另一个问题,我的服务是否应该明确地persist() & flush() 我的实体?

【问题讨论】:

    标签: design-patterns symfony doctrine-orm soa service-layer


    【解决方案1】:

    好吧,不使用 entityManager 时如何获取存储库?毕竟,如果没有与数据库的连接,实体不会被神奇地保存,因此您的服务必须以某种方式知道任何类型的连接。

    我不了解 SOA 服务,但在我看来,使用 $_em->getRepository()->save($entity)$_em->persist($entity) 完全没有区别。另一方面,如果您在存储库中使用刷新,则最终可能会得到比所需更多的查询,因为您的存储库现在知道业务逻辑。

    我认为有一种方法可以做到这一点“SOA 方式”,但我想它不会在存储库中持久化实体。

    【讨论】:

    • 关于flush() 问题的观点非常好。也许是冗长的冲洗参数?
    • 你也不应该因为其他原因刷新 Doctrine 存储库。它可能会破坏事务或给出意想不到的结果,这可能是 Doctrine 不包括在存储库中刷新的能力的原因。他们是故意这样做的,您不应该添加该功能。从实体管理器刷新和从理论存储库中刷新是有区别的。
    【解决方案2】:

    是的,存储库通常仅用于查询。

    这是我的做法。 service layer 管理持久性。控制器层知道服务层,但不知道模型对象是如何持久化的,也不知道它们来自哪里。控制器层关心的是要求服务层持久化并返回对象——它并不关心它实际上是如何完成的。

    服务层本身非常适合了解持久层:实体或文档管理器、存储库等。

    这里有一些代码让它更清楚:

    class UserController
    {
        public function indexAction()
        {
            $users = $this->get('user.service')->findAll();
            // ...
        }
    
        public function createAction()
        {
            // ...
            $user = new User();
            // fill the user object here
            $this->get('user.service')->create($user);
            // ...
        }
    }
    
    class UserService
    {
        const ENTITY_NAME = 'UserBundle:User';
    
        private $em;
    
        public function __construct(EntityManager $em)
        {
            $this->em = $em;
        }
    
        public function findAll()
        {
            return $this->em->getRepository(self::ENTITY_NAME)->findAll();
        }
    
        public function create(User $user)
        {
            // possibly validation here
    
            $this->em->persist($user);
            $this->em->flush($user);
        }
    }
    

    【讨论】:

    • 我很好奇你是如何实现这个的(从服务层持久化)。通常我从控制器创建和持久化实体,但承认它感觉不“正确”。您是否对每个需要持久化的实体使用 1 项服务?你介意更详细一点吗?
    • 坚持在控制器层感觉不对,因为控制器应该只关心 GUI,即他们只是解析请求,将所有“真实”工作委托给服务层,然后返回响应。大多数时候,我每个实体/文档都有一个服务,但这不是规则,因为有时您可以拥有根本没有实体的服务,并且理论上(我还没有)管理多个实体的服务。
    • “GUI”是错误的术语,因为并非所有界面都是图形化的。可以有 REST、命令行和其他类型的接口,它们都将“真正的”工作委托给服务层。使用服务层有助于遵守 DRY 原则,因为您在不同类型的接口中没有相同的代码,并将它们与持久层解耦,因为有一天您可能希望从 ORM 切换到 ODM,例如,或从本地存储到云等第三方存储。
    • 有趣。我同意使用服务层进行持久性比使用控制器层更干燥。在您的示例中,您没有设置任何 User 属性,您会在控制器中设置这些属性,还是将它们传递给服务的 create 方法?然而,让我感到困扰的是findAll 被服务代理。我认为使用该服务比使用存储库本身没有优势。
    • 存储库(如Repository Pattern)绝对可以有“添加”或“保存”的方法。
    【解决方案3】:

    如果您查看存储库模式http://martinfowler.com/eaaCatalog/repository.html

    据说存储库使用“类似集合的接口”。

    后来,还写到“对象可以添加到存储库中,也可以从存储库中删除,就像它们可以从简单的对象集合中一样”。

    我并不是说这是一本圣经,但从概念上讲,将存储库视为可以查询的集合并没有错。 但由于它是一个集合,您可以添加、删除、... 事实上,ObjectRepository 应该实现 Doctrine\Common\Collection :)

    另一方面,最重要的是不要像 CQS 所说的那样搞乱读写。 这可能是他们不允许直接这样做的原因,以避免滥用和读/写混合。

    编辑:我应该谈论flush。这应该在存储库本身中进行,因为它可能会破坏事务一致性。

    您最好将 flush 调用移动到包装整个业务事务逻辑的东西(处理命令 f.e 的命令总线?)

    【讨论】:

    • 我认为您可能会将“通用存储库”与 OP 问题上下文中的“存储库”混淆,后者是隔离对数据库的访问的层。 symfony/doctrine 世界中的通用存储库实际上是 ORM 而不是所讨论的实体存储库。
    • 您能详细说明什么是“通用存储库”吗?我的回答直接针对 OP 所谈论的理论 ORM 存储库模式
    • 学说 ORM 中的 entityManager 在该示例中可以被视为“通用存储库”层。 reads 类,“它是所有不同 ORM 子系统(例如 UnitOfWork、查询语言和存储库 API)的外观”。另一方面,EntityRepository 没有实现任何save() 方法,只有find*()。谢谢。
    • 我不会将 ORM 视为通用存储库。教义是一个 ORM。这里有两个不同的主题。第一个是仅用于查找实体的 Doctrine 存储库,您不应该刷新这些存储库。然后是存储库模式,这是一个不同的概念。从这个意义上说,存储库是“事物”的集合。正如@Florian 所说,它可以只实现学说的 Collection 接口。然后,您创建特定的实现,如果他们愿意,它们可以持续存在。这是一种允许您的业务逻辑使用这些存储库接口并且与持久性无关的方法。
    猜你喜欢
    • 2012-07-08
    • 1970-01-01
    • 2019-10-03
    • 2011-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-01
    相关资源
    最近更新 更多