在哪里检查域对象中的强制属性?

时间:2013-07-12 05:52:09

标签: php design-patterns domain-driven-design

我有一个PHP MVC应用程序,我的'M'有一个服务层,映射器层和域层。我应该在哪里以及如何检查以确保对象具有所有必需的属性?

在我看来,这些职责不属于映射器或服务层,因此离开了域层本身。我在我的基础域类中添加了一个方法checkRequired()。它根据$_required属性数组检查对象的属性,如果缺少任何属性则抛出错误。

从数据库中检索对象时,我一直在调用checkRequired()作为对象构造函数中的最后一个命令。如果对象是新实体(即未从数据库中检索),我提供一些默认值(在构造函数中),然后调用checkRequired()

虽然这一切工作正常但我现在开始在我的(有些贫血的)域对象上添加一些行为方法,并且我遇到了麻烦。例如,用户可以拥有许多宠物,因此在我的用户模型中,我放置了addPet()方法。我知道我需要传递Pet对象,因为最好注入依赖项,因此我的真实方法签名是User::addPet(ConcretePet)

但那就是问题所在!没有用户(作为他们的主人),我的宠物不能存在,所以我无法实例化ConcretePet!我可以在Pet上使用户可选,但这将是一个向后的步骤。我可以将checkRequired()的内容移到其他地方,但在哪里?

解决这种常见问题的典型方法是什么?

3 个答案:

答案 0 :(得分:1)

checkRequired不是DDD方式。一个人应该始终有效。换句话说 - 您不应该有办法将其置于无效状态(如缺少属性)。怎么样? 如果您只拥有自己设定的公共财产,那就是贫血。您在DB中保留的属性应该是私有的。设置它们的唯一方法是通过具有商业意义的方法。这些方法应检查所有不变量(不仅仅是必需的字段 - 使实体有效的所有类型的约束),如果不满足其中一些,则阻止更新。

关于用户 - >宠物主题:如果宠物在没有用户的情况下不能存在,那么用户可能是一个聚合根,负责保护与用户和宠物相关的不变量。这意味着应该有一个方法addPet ......好吧......也许更有意义的东西?采用Petet和breedPet(他们可能会有略微不同的规则和输入)?而且这个采用宠物应该确保拥有用户的宠物的不变...顺便说一句 - 为什么用户而不是所有者?

但Pet也可以是一个聚合根。这意味着它的构造函数应该需要一个User参数。

请注意,它取决于用例什么是聚合根。在某些情况下,Pet被视为聚合根,但在宠物收养的情况下,它是用户聚合的一部分。

答案 1 :(得分:0)

尽可能建立不定向的关系。如果可以单独跟踪宠物(我的意思是没有用户),请将其视为聚合根。

在“用户”中将“用户”属性放置在“宠物”但“没有宠物”属性因此,您不需要在User中使用addPet()方法。

如果您想查找属于某个用户的所有宠物,请改用查询:

public class PetRepository {
    public List<Pet> findByOwner(String uid) {
        //omitted codes
    }
}

希望这有帮助。

答案 2 :(得分:0)

  

从数据库中检索对象时,我一直在调用   checkRequired()作为对象构造函数中的最后一个命令。

偏离主题,但这可能会有问题。假设所需属性集在某些时候发生变化,使得持久化实体不再有效。你仍然希望能够重建它们,所以你不应该在重构时运行那个检查。相反,只在创建时或行为期间运行验证。

关于addPet方法,不是传递具体pet类的实例,而是将创建pet实例所需的数据作为方法参数或PetInfo类的实例传递。这将允许User类创建一个完全有效的Pet实例。