或者,您是否从控制器中的字符串创建值对象?
new Command(new SomeId("id"),Weight.create("80 kg"),new Date())
要么
new Command("id","80 kg",new Date()) new Command("id","80","kg",new Date())
也许这并不重要,但是让我烦恼.
问题是,我们应该将值对象从域耦合到(内部)控制器吗?
想象一下,您的应用程序层和表示层之间没有网络(如Android活动或swing),您是否会在UI中推荐使用值对象?
另一件事,你把值对象序列化到/从字符串这样排序
Weight weight = Weight.create("80 kg"); weight.getValue().equals(80.0); weight.getUnit().equals(Unit.KILOGRAMS); weight.toString().equals("80 kg");
在将字符串传递到命令的情况下,我宁愿通过“80公斤”而不是“80”和“kg”.
对不起,如果问题不相关或有趣.
谢谢.
UPDATE
当我搜索关于完全不同的主题的信息时,我遇到了这个帖子:Value Objects in CQRS – where to use
他们似乎更喜欢原语或DTO,并将VOs保留在域内.
我也看过V. Vernon(实施DDD)这本书,并且在第14章(第522页)中谈到(正确的)
我注意到他没有任何DTO使用命令.
someCommand.setId("id"); someCommand.setWeightValue("80"); someCommand.setWeightUnit("kg"); someCommand.setOtherWeight("80 kg"); someCommand.setDate("17/03/2015 17:28:35"); someCommand.setUserName("..."); someCommand.setUserAttribute("..."); someCommand.setUserOtherAttributePartA("..."); someCommand.setUserOtherAttributePartB("...");
它是由控制器映射的命令对象.值对象初始化将在命令处理程序方法中得到应用,并且它们会在错误值(初始化时进行自验证)时抛出异常.
我觉得我开始不太在意,但其他一些意见也会受到欢迎.
通过字符串或解析?
我个人的喜好是解析控制器中的所有内容,并将结果发送给服务.这种方法有两个主要阶段,每个阶段都可以回溯错误条件:
尝试解析
当一串字符串从UI进入时,我认为尝试立即解释它是有意义的.对于像int和bools这样的简单目标,这些转换是微不足道的,许多Web框架的模型绑定可以自动处理它们.
对于更复杂的对象(如自定义类),在此位置处理它仍然有意义,以便所有解析都发生在同一位置.如果您在提供模型绑定的框架中,大部分此解析可能会自动完成;如果没有 – 或者您正在组装一个更复杂的对象发送到服务 – 您可以在控制器中手动执行.
故障条件
解析失败(在“int”字段中输入“hello”或为bool输入“7”),在您甚至必须调用该服务之前,很容易向用户发送反馈.
2.验证并提交
即使解析成功,仍然有必要验证该条目是否合法,然后提交.我更喜欢在提交之前立即在服务级别处理验证.这使得控制器负责解析,并在代码中非常清楚地确认每个提交的数据的验证.
在这方面,我们可以从服务层消除辅助责任.没有必要使它解析对象 – 其单一目的是提交信息.
故障条件
当验证失败(有人在月球上输入地址,或者在过去300年内输入出生日期)时,应该将故障报告给调用方(在这种情况下为Controller).虽然用户可能无法区分解析失败和无法验证,但这对软件是一个重要的区别.
将值对象推送到UI?
我会尽可能接受解析的对象尽可能多的堆栈.如果你可以让别人的框架处理这一点转变,为什么不这样做?此外,更接近对象可以存在的UI,向用户提供关于他们正在做什么的良好,快速的反馈越容易.
关于耦合的注意事项
总的来说,将物体推入堆栈确实会导致更大的耦合.然而,为特定领域编写软件确实涉及紧密耦合到该域,无论它是什么.如果还有一些组件与整个域中无处不在的概念紧密耦合,或者至少是被称为服务的API接触点,我看不出任何真正的架构完整性或灵活性的降低.
解析一个大字符串或组件?
一般来说,将整个字符串传递到Parse()方法中往往是最简单的,以便进行排序.以“80公斤”为例:
>“80公斤”和“120磅”都可以是有效的重量输入>如果你传递一个字符串到一个Parse()方法,它可能是做一些相当沉重的举重.期望它根据空间分割一个字符串不是过分的.调用Weight.create(inputString)比将“input”分割为“”更容易,然后调用Weight.create(split [0],split [1]).>保持单串输入的Parse()函数也很容易.如果有一些新的要求,重量级别必须支持磅和盎司,新的有效输入可能是“120磅6盎司”.如果你分开输入,你现在需要四个参数.而如果它完全封装在Parse()逻辑中,那么外部消费者就没有任何负担.这使得代码更加可扩展和灵活.