领域驱动的设计依赖于静态方法



我在线/离线阅读了很多关于域驱动设计的验证和业务规则的内容。我无法理解的是,一个实体如何在不诉诸静态方法或拥有服务的情况下提供执行验证和业务规则的方法?对于域对象还不需要实例化,但我们需要验证最终用于设置对象属性的值的情况,这一点尤为重要。

我注意到一些博客文章http://lostechies.com/jimmybogard/2007/10/24/entity-validation-with-visitors-and-extension-methods/依赖于.NET的特定扩展方法,而这种方法在Java等编程语言中是不可用的。我个人不喜欢静态方法,因为它们不能被覆盖并且很难测试。

不管怎样,我可以在没有静态方法的情况下做到这一点,或者不必实例化一个不必要的域对象来使用它的验证和业务规则方法。如果不是,这是否意味着领域驱动的设计非常依赖于静态方法?

感谢

使用ValueObjects实体。

在注册的情况下,可以引入UserName值对象。在收到注册时创建用户名对象。在UserName的构造函数中实现验证。

请参阅此问题和此演示以了解更多详细信息。

第1版:
1.如何处理不同验证规则适用于不同上下文的情况
例如:用户名对于某些类型的成员不能有数字,但对于其他类型的成员必须有数字?

也许不同的工厂方法可以做到这一点。例如UserName.forGoldenCardMember(…)或UserName.for PlainMember(..)。或者使MemberType(可能是层次结构)验证UserName。

另一个替代解决方案是使用AggregateFactory(在本例中为AccountFactory)。

2.构造函数是唯一放置验证代码的地方吗?我确实在网上读到了两种观点:一个对象必须总是有效的,而不是总是有效的。两者都提出了很好的论点,但还有其他方法吗?

我个人更喜欢有效的方法。传递一个可能无效的值对象会损害封装性。

第2版:
要求a) 基于上下文的验证业务规则(针对成员类型的不同用户名规则)b) 继续验证所有业务规则,即使其中一个未通过

通过使用Value Object(本例为MemberType),坚持单一责任原则。可以引入AggregateFactory来简化应用层(更粗的粒度)。

class AccoutFactory {
    Account registerWith(Username username, MemberType type, ....) {
        List<String> errors = new ArrayList<String>();
        errors.addAll(type.listErrorsWith(username));
        errors.add(//other error report...
        if (CollectionUtils.isEmpty(errors)) {
            return new Account(username,....);
        } else {
            throw new CannotRegisterAccountException(errors);
        }
    }
}

第3版:对于评论中的问题
a) Username对象不应该是一个返回错误的方法吗列表ErrorsWith()?毕竟,是用户名对不同的成员类型有不同的规则吗?

我们可以从另一个角度来检查这个问题:MemberTypes对用户名有不同的规则。这可以用多态性替换Username.listErrosWith(String,MemeberType)中的if/else块;

b) 如果我们在MemberType中有方法,那么知识将不会封装在Username中。此外,我们正在讨论确保Username始终有效。

我们可以在没有MemberType规则的情况下定义Username的有效性。让我们说"hippoom@stackoverflow.com"是一个有效的用户名,它是GoldenCard会员的好候选者,但不适合SilverCard会员。

c) 我仍然看不出如何在不从构造函数或静态方法抛出的异常中获取错误列表的情况下执行返回错误列表的验证。两者看起来都不理想。

是的,listErrorsWith():List的签名看起来很糟糕,我宁愿使用没有返回值的validate(username)(失败时抛出异常)。但这将迫使cilent捕获每个验证步骤,以便同时运行验证。

如果您决定在应用程序中使用DDD,则需要构建更复杂的解决方案。我同意@Hippoom的观点,你不应该为此目的使用实体。

我建议这个解决方案:

DTO->服务层(验证服务->转换器)->持久层(存储库)

一些解释:当您从客户端收到带有所有必要参数的DTO时,您应该在您的服务层中对其进行验证(例如,使用另一个服务,如ValidationService),如果出现问题,该服务可能会引发异常。如果一切正常,您可以从Converter中的DTO创建实体,并将其持久化到Repository

如果您想要灵活的ValidationService解决方案,我建议Drools

最新更新