域驱动设计的一部分,似乎没有太多细节,是您应该如何以及为什么要将域模型与界面隔离开来。我试图说服我的同事,这是一个很好的做法,但我似乎没有取得多大进展......
他们在演示文稿和界面层中随意使用域实体。当我向他们争辩说他们应该使用显示模型或DTO来将Domain层与接口层隔离时,他们反驳说他们在做类似的事情时看不到业务价值,因为现在你有一个UI对象要维护以及原始域对象。
所以我正在寻找一些可以用来支持这一点的具体原因。具体做法是:
答案 0 :(得分:46)
很简单,原因是实施和漂移。是的,您的表示层需要了解您的业务对象才能正确表示它们。是的,最初看起来两种类型的对象的实现之间存在很多重叠。问题是,随着时间的推移,双方都会增加一些东西。演示文稿更改以及表示层的需求演变为包括完全独立于业务层(例如颜色)的内容。同时,您的域对象会随着时间的推移而发生变化,如果您没有从界面中进行适当的分离,则可能会通过对业务对象进行看似良性的更改来调整界面层的风险。
就个人而言,我认为处理事情的最佳方式是通过严格执行的界面范式;也就是说,您的业务对象层公开了一个接口,这是与之通信的唯一方式;没有公开关于接口的实现细节(即域对象)。是的,这意味着您必须在两个位置实现域对象;你的界面层和你的BO层。但是,这种重新实现虽然最初可能看起来像是额外的工作,但却有助于强化解耦,从而在未来的某个时刻节省大量的工作。
答案 1 :(得分:19)
我自己也在努力解决这个问题。在某些情况下,DTO在演示中使用是有意义的。假设我想在我的系统中显示公司的下拉,我需要他们的id来绑定值。
好吧,我可以发回一个名称和ID的DTO,而不是加载可能引用订阅或知道其他内容的CompanyObject。这是一个很好用的恕我直言。
现在再看一个例子。我有一个表示估计的对象,这个估计可能由人工,设备等组成,它可能有很多由用户定义的计算,它们将所有这些项目总结起来(每个估计可能因不同类型而有所不同)计算)。我为什么要两次模拟这个对象?为什么我不能简单地让我的UI枚举计算并显示它们?
我通常不使用DTO来将我的域图层与我的UI隔离开来。我使用它们来隔离我的域层与我无法控制的边界。有人将导航信息放入其业务对象的想法是荒谬的,不要污染您的业务对象。
有人会将验证放在业务对象中吗?好吧,我说这是件好事。您的用户界面不应单独负责验证您的业务对象。您的业务层必须进行自己的验证。
为什么要将UI生成代码放在业务对象中?在我的情况下,我有单独的对象,从UI生成UI代码seperatley。我有一些对象将我的业务对象渲染成Xml,你必须分离你的图层来防止这种类型的污染对我来说是如此陌生,因为你为什么甚至将HTML生成代码放在业务对象中......
修改强> 正如我想的那样,有些情况下UI信息可能属于域层。这可能会覆盖你所谓的域层,但我在一个多租户应用程序上工作,它具有非常不同的行为,包括UI外观和功能工作流。取决于各种因素。在这种情况下,我们有一个代表租户及其配置的域模型。他们的配置恰好包括UI信息(例如,通用字段的标签)。
如果我必须设计我的对象以使它们可以持久化,我是否还必须复制对象?请记住,如果您想要添加新字段,现在有两个地方可以添加它。也许这会引发另一个问题,如果您使用DDD,都是持久化实体域对象?我在他的例子中知道他们是。
答案 2 :(得分:16)
您这样做的原因与您将SQL保留在ASP / JSP页面之外的原因相同。
如果您只保留一个域对象,以便在演示文稿AND域层中使用,那么该对象很快就会变成单一的。它开始包括UI验证代码,UI导航代码和UI生成代码。然后,您很快就会添加所有业务层方法。现在你的业务层和用户界面都混淆了,所有这些都在域实体层搞乱。
您想在另一个应用中重用那个漂亮的UI小部件吗?好吧,你必须用这个名字,这两个模式和这18个表创建一个数据库。您还必须配置Hibernate和Spring(或您选择的框架)来进行业务验证。哦,你还必须包括这85个其他非相关类,因为它们在业务层中引用,它恰好位于同一个文件中。
答案 3 :(得分:13)
我不同意。
我认为最好的方法是从表示层中的域对象开始,直到它做出其他方式的意见。
与流行的看法相反,“域对象”和“价值对象”可以在表示层中愉快地共存。这是最好的方法 - 你可以获得两个世界的好处,减少与域对象的重复(和样板代码);以及在请求中使用值对象的定制和概念简化。
答案 4 :(得分:4)
我们在服务器和ui上使用相同的模型。而且这很痛苦。我们有一天必须重构它。
问题主要是因为域模型需要被切割成更小的部分才能序列化它而不需要引用整个数据库。这使得在服务器上使用起来更加困难。缺少重要的链接。某些类型也不可序列化,无法发送到客户端。例如'Type'或任何泛型类。它们需要是非泛型的,Type需要作为字符串传输。这会为序列化生成额外的属性,它们是多余的并且令人困惑。
另一个问题是UI上的实体并不适合。我们正在使用数据绑定,并且许多实体仅为ui目的而具有许多冗余属性。此外,实体模型中还有许多“BrowsableAttribute”和其他内容。这真的很糟糕。
最后,我认为这只是一个更容易的问题。可能有一些项目可以正常工作,而且不需要编写另一个DTO模型。
答案 5 :(得分:4)
答案取决于您的申请规模。
对于基本的crud应用程序,您没有任何功能。在实体上添加DTO会浪费时间。它会增加复杂性而不会增加可扩展性。
在这种规模的应用程序中,您将拥有几个具有真正生命周期和与之相关的业务逻辑的实体。
在这种情况下添加DTO是个好主意,原因如下:
单个实体可能需要多种呈现方式。他们每个人都需要不同的字段。在这种情况下,您遇到与前一个示例相同的问题,并且需要控制每个客户端可见的字段数量。 为每个客户提供单独的DTO将帮助您选择应该可见的内容。
答案 6 :(得分:3)
这主要是关于依赖性的。组织的核心功能结构有其自身的功能需求,UI应该使人们能够修改和查看核心;但不应要求核心本身适应UI。 (如果需要发生,通常表明核心不是财产设计。)
我的会计系统有一个结构和内容(和数据),可以模拟我公司的运作。无论我使用什么会计软件,这种结构都是真实存在的。 (不可避免地,给定的软件包本身就包含结构和内容,但部分挑战是最大限度地减少这种开销。)
基本上一个人有工作要做。 DDD应该与作业的流程和内容相匹配。 DDD旨在尽可能完全独立地明确所有需要完成的工作。然后,UI有希望尽可能透明地尽可能透明地完成工作。
接口是关于为正确建模和不变的功能核心提供的输入和视图。
答案 7 :(得分:3)
该死的,我发誓这说坚持。
无论如何,这是同一件事的又一个例子:Parnas的法律规定模块应该保守秘密,秘密是一个可以改变的要求。 (Bob Martin的规则是另一个版本。)在这样的系统中,表示可以独立于域进行更改。例如,一家公司以欧元维持价格并在公司办公室使用法语,但希望以普通话的文本用美元来表示价格。 域是一样的;演示文稿可以改变。因此,为了最大限度地降低系统的脆弱性 - 即,为了实现需求变更而必须更改的事物数量 - 您可以将问题分开。
答案 8 :(得分:2)
您的演示文稿可以引用您的域图层,但不应该直接从您的ui绑定到您的域对象。域对象不适用于UI,因为如果设计得当,它们通常基于行为而非数据表示。 UI和域之间应该有一个映射层。 MVVM或MVP是一个很好的模式。如果您尝试直接将UI绑定到域,那么probalby会为您自己造成很多麻烦。它们有两个不同的目的。
答案 9 :(得分:1)
也许您没有广泛地概念化UI层。考虑多种形式的响应(网页,语音响应,印刷字母等)和多种语言(英语,法语等)。
现在假设电话呼入系统的语音引擎在运行网站的计算机(可能是Windows)上运行在完全不同类型的计算机(例如Mac)上。
当然很容易陷入困境“在我的公司里,我们只关心英语,在LAMP上运行我们的网站(Linux,Apache,MySQL和PHP),每个人都使用相同版本的Firefox”。但是5年或10年呢?
答案 10 :(得分:1)
另请参阅下面的“层之间的数据传播”一节,我认为这些论点提出了令人信服的论据:
http://galaxy.andromda.org/docs/andromda-documentation/andromda-getting-started-java/java/index.html
答案 11 :(得分:1)
借助“Value Injecter”等工具以及在处理视图时表示层中“Mappers”的概念,可以更加轻松地理解每段代码。如果您有一些代码,您将不会立即看到优势,但是当您的项目越来越多时,您将非常高兴在处理视图时无需进入服务的逻辑,存储库以了解视图模型。 View Model是广阔反腐败领域的另一个守护者,在长期项目中值得重视黄金。
我认为使用视图模型没有优势的唯一原因是,如果您的项目很小且非常简单,可以将视图直接绑定到模型的每个属性。但是,如果在未来,视图中的需求更改和某些控件将不会绑定到模型,并且您没有视图模型概念,您将开始在许多地方添加修补程序,并且您将开始使用遗留代码你不会欣赏。当然,您可以进行一些重构以在view-viewmodel中转换视图模型并遵循YAGNI原则而不添加代码(如果您不需要它),但对于我自己来说,这是我必须遵循的最佳实践表示层仅公开视图模型对象。
答案 12 :(得分:1)
以下是一个真实的例子,说明为什么我认为将域实体与视图分开是一种很好的做法。
几个月前,我创建了一个简单的用户界面,通过一系列3个仪表显示土壤样品中氮,磷和钾的值。每个仪表都有一个红色,绿色和红色部分,即每个部件可能有太少或太多,但中间有一个安全的绿色等级。
我没有多想,我模拟了我的业务逻辑,为这3种化学成分提供数据和单独的数据表,其中包含3种情况中每种情况的可接受水平数据(包括使用的测量单位,即痣或百分比)。然后我将我的UI建模为使用一个非常不同的模型,这个模型关注的是标尺标签,值,边界值和颜色。
这意味着当我后来必须显示12个组件时,我只是将额外的数据映射到12个新的仪表视图模型中,它们出现在屏幕上。这也意味着我可以轻松地重复使用仪表控件并让它们显示其他数据集。
如果我将这些量表直接耦合到我的域名实体中,我就没有上述任何灵活性,任何未来的修改都会令人头疼。在UI中建模日历时,我遇到了非常类似的问题。如果有10个以上的与会者要求日历约会变为红色,那么处理此问题的业务逻辑应保留在业务层中,并且UI中的所有日历都需要知道,是否已指示变红了,不应该知道原因。
答案 13 :(得分:-1)
在通用语义和域特定语义之间添加附加映射的唯一合理理由是您拥有(访问)基于与域语义不同的通用(但可映射)语义的现有代码体(和工具)。
域驱动设计与正交的功能域框架集(例如ORM,GUI,工作流等)结合使用时效果最佳。永远记住,只有在外层邻接中才需要暴露域语义。通常,这是前端(GUI)和持久后端(RDBM,ORM)。任何有效设计的干预层都可以而且应该是域不变的。