res*_*ija 12 domain-driven-design value-objects
当您从控制器内的UI接收字符串格式的参数时,是否直接将字符串传递给应用程序服务(或命令)?
或者,您是否从控制器内的字符串创建值对象?
new Command(new SomeId("id"), Weight.create("80 kg"), new Date())
Run Code Online (Sandbox Code Playgroud)
要么
new Command("id", "80 kg", new Date())
new Command("id", "80", "kg", new Date())
Run Code Online (Sandbox Code Playgroud)
也许这不重要,但它困扰我.
问题是,我们应该将域中的值对象耦合到控制器(内部)吗?
想象一下,在应用程序层和表示层之间没有Web(如android活动或swing),你会在UI中推动使用值对象吗?
另外,你是否将值对象序列化/反序列化为字符串?
Weight weight = Weight.create("80 kg");
weight.getValue().equals(80.0);
weight.getUnit().equals(Unit.KILOGRAMS);
weight.toString().equals("80 kg");
Run Code Online (Sandbox Code Playgroud)
在将字符串传递给命令的情况下,我宁愿传递"80 kg"而不是"80"和"kg".
对不起,如果问题不相关或有趣.
谢谢.
我在搜索有关完全不同的主题的信息时遇到了这个帖子:CQRS中的值对象 - 在哪里使用
他们似乎更喜欢基元或DTO,并将VO保留在域内.
我还看了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("...");
Run Code Online (Sandbox Code Playgroud)
它是由控制器映射的命令对象.值对象初始化将出现在命令处理程序方法中,并且它们会在值不正确的情况下抛出一些东西(初始化中的自我验证).
我想我开始不那么烦恼,但其他一些意见也会受到欢迎.
作为介绍,这是非常自以为是的,我相信每个人对它应该如何工作都有不同的想法。但我在这里的努力是概述一个策略,背后有一些很好的理由,这样你就可以做出自己的评估。
我个人的偏好是解析控制器中的所有内容并将结果发送到服务。这种方法有两个主要阶段,每个阶段都可以回吐错误情况:
当一堆strings 从 UI 进来时,我认为尝试立即解释它们是有意义的。对于像ints 和bools这样的简单目标,这些转换是微不足道的,许多 Web 框架的模型绑定器会自动处理它们。
对于更复杂的对象,比如自定义类,在这个位置处理它仍然是有意义的,这样所有的解析都发生在同一个位置。如果您在一个提供模型绑定的框架中,那么大部分解析可能是自动完成的;如果没有 - 或者您正在组装一个更复杂的对象以发送到服务 - 您可以在控制器中手动完成。
故障条件当解析失败("hello"在int字段7中输入或为 a 输入bool)时,在您甚至必须调用服务之前向用户发送反馈非常容易。
即使解析成功,仍然需要验证条目是否合法,然后提交。我更喜欢在提交之前立即处理服务级别的验证。这让控制器负责解析,并在代码中非常清楚地表明对提交的每条数据都进行验证。
通过这样做,我们可以消除服务层的辅助责任。没有必要让它解析对象——它的唯一目的是提交信息。
故障条件当验证失败(有人在月球上输入地址,或输入过去 300 年的出生日期)时,应将失败报告给调用者(在这种情况下为控制器)。虽然用户可能不会区分解析失败和验证失败,但对于软件来说这是一个重要的区别。
每次我都会尽可能多地接受已解析的对象。如果您可以让其他人的框架处理那一点转换,为什么不这样做呢?此外,对象所在的 UI 越接近,就越容易向用户提供关于他们正在做什么的良好、快速的反馈。
总的来说,将对象向上推栈确实会导致更大的耦合。然而,为特定领域编写软件确实涉及与该领域紧密耦合,无论它是什么。如果更多的组件与整个域中普遍存在的一些概念紧密耦合 - 或者至少与被调用服务的 API 接触点 - 我看不到架构完整性或灵活性的任何真正降低。
通常,将整个传递string到Parse()方法中进行排序往往是最容易的。以你的例子为例"80 kg":
"80 kg"并且都"120 lbs"可能是有效的重量输入strings传递给一个Parse()方法,它可能会做一些相当繁重的工作。期望它string根据空间拆分 a并不过分。Weight.create(inputString)比 split inputStringby " ",然后调用容易得多Weight.create(split[0], split[1])。Parse()函数也更容易。如果出现一些新的要求,即Weight该类必须支持磅和盎司,则新的有效输入可能是"120 lbs 6 oz". 如果要拆分输入,则现在需要四个参数。而如果它完全封装在Parse()逻辑中,那么外部消费者就没有负担。这使得代码更具可扩展性和灵活性。