Clean清洁领域模型的几个特点 -Kamil Grzybek


如今,有关干净代码和体系结构的讨论很多。关于如何实现它的讨论越来越多。罗伯特·C·马丁(Robert C. Martin)描述的规则是通用的,我认为,我们可以在其他各种情况下使用它们。
在本文中,我想让他们参考领域模型实现的上下文,这通常是我们系统的核心。我们想拥有一颗干净的核心,不是吗?
从领域驱动设计方法可以知道,我们有两个空间-问题空间和解决方案空间。Vaughn Vernon在DDD Distilled书中对这些空间的定义如下:
问题空间是您在给定项目的约束下执行高级战略分析和设计步骤的地方。
解决方案空间是您实际实施问题空间讨论确定为核心域的解决方案的地方。

因此,领域模型的实现是解决问题的方法。要讨论领域模型是否干净,我们必须首先定义清洁解决方案的含义。

什么是清洁解决方案
我认为每个人都对干净解决方案的含义有所定义,或者至少感觉到自己所做的工作是否干净。无论如何,让我描述一下这对我意味着什么。
首先,指定的解决方案必须解决已定义的问题。这是强制性的。如果无法解决我们问题,即使是最优雅的解决方案,对于该问题空间而言也是一文不值。
其次,从时间的观点来看,干净的解决方案应该是最佳的。它应该花费的时间应该不超过或少于所需的必要时间。
第三,它应该是基于解决简单问题的基础上,以简单性为前提,但要适应变化。我们应该始终保持平衡-不少(无设计或欠设计),也不多(过度设计,请参见YAGNI)。可以用Antoine Airman的Odyssey书中的一句话来总结:
所谓达到完美,不是没有什么东西可添加了,而是再没有什么东西可移除了
注意:当然,我们应该努力做到完美,因为要意识到我们将永远无法实现它。
第四,我们应该根据他人的经验解决问题,并从他们的错误中吸取教训。这就是为什么架构和设计模式或原则如此有用的原因。我们可以采用某人的解决方案并使之适应我们的需求。而且,我们可以使用其他人创建的工具并使用它们。这是IT世界的颠覆者。
最后但并非最不重要的一点是对解决方案的理解程度。即使经过很长时间,我们也应该能够理解我们的解决方案。更重要的是,其他人应该能够理解它。该代码读起来比编写的要多得多。马丁·福勒说:
任何傻瓜都可以编写计算机可以理解的代码。好的程序员编写人类可以理解的代码。
总而言之,干净的解决方案应在简单时间与复杂性之间取得良好的平衡,从而在最佳时间内准确解决我们的问题。它应该基于其他经验,常识,使用适当的工具来开发,并且应该易于理解。考虑到所有这些因素,我们如何确定我们的领域模型是一个干净的解决方案?

Clean清洁领域模型
1.使用无所不在的统一语言
无处不在的语言是域模型中DDD战略模式的基础之一,在其中起着关键作用。好像是在编写商业书籍一样,商人或业务人士产品经理应该能够理解所使用的所有逻辑,术语和概念,即使这是计算机程序,他们也无需掌握编程语言!如果我们在定义的上下文中到处使用相同的语言,概念和含义,那么大家就容易对领域的问题空间及其解决方案增加理解,只会增加项目成功的可能性。

2.行为丰富
顾名思义,领域模型(Domain Model)对领域进行建模。我们在领域中有行为,因此我们的模型也应该有它。如果没有这种行为,我们将处理所谓的“ 贫血域模型”反模式。对我来说,这实际上是一个数据模型,而不是领域模型。这种模型有时很有用,但当我们具有复杂的行为和逻辑时却没有用。它使我们的代码具有程序性,并且面向对象编程的所有好处都消失了。真正的领域模型具有丰富的行为。

3.被封装
封装的领域模型是非常重要的。我们只想向外界公开有关我们模型的最少信息。我们应该防止业务逻辑泄漏到我们的应用程序逻辑,甚至更糟– GUI。理想情况下,我们应该只在聚合上公开公共方法(领域模型仅有的输入渠道)。

4.持久性是无感的
持久性无感(PI)是领域模型的另一个众所周知的概念和理想属性。我们不想在模型和持久性存储之间建立高度的耦合。这些是不同的概念,任务和责任。一件事的改变对另一件事的影响应该最小,反之亦然。该区域的低耦合意味着我们的系统可以更好地适应变化。

5.坚持SOLID原则
由于领域模型是面向对象的,因此应遵循所有SOLID原则。例子:

  • SRP –一个实体代表一个业务概念。一种方法可以做一件事。一个事件代表一个事实。
  • O / C –业务逻辑实现应易于扩展而不更改其他位置。在这种情况下,最常使用策略/策略模式
  • LSP –由于坚持继承规则组成,遵守LSP规则应该很容易。继承是类之间的最强耦合,这是我们要避免的
  • ISP –我们的域服务或策略的接口应较小–理想情况下应采用一种方法
  • DI –要使我们的域模型与其余应用程序和基础结构脱钩,我们需要使用依赖注入和依赖倒置原理。这种组合为我们提供了强大的武器–我们可以在模型中保持一定的抽象水平,在整个模型中使用业务语言,并隐藏技术实现细节。

6.使用领域原语
我看到的大多数代码库都对基本类型进行操作-字符串,整数,小数和guid。在领域模型中,此抽象级别绝对太低。
我们应该始终尝试使用类(实体或值对象)来表达重要的业务概念。通过这种方式,我们的模型更具可读性,易于维护,行为丰富。另外,我们的代码更加安全,因为我们可以在类级别添加验证。您可以在此处阅读有关域基元的更多信息。

7.高效
即使是写得最好的领域模型,也无法在令人满意的时间内处理请求,这是没有用的。这就是为什么实体大小和集合边界如此重要的原因。
在设计聚合时,您需要知道如何处理它们–多久一次,有多少用户将尝试同时使用它们,是否会增加活动周期等等。聚合越小,需要加载和保存的数据越短,事务也就越短。另一方面,总量越小,一致性边界就越小,因此需要正确的平衡。

8.富有表现力
面向对象语言的类结构是对业务概念进行建模的强大工具。不幸的是,它经常被错误地使用。最常见的错误是用一个类对许多概念进行建模。为此(通常不自觉地)使用状态和数据位标志。
通常,此类可以分为几个类别,这些类别支持SRP。这里的一种启发式方法是研究业务语言并研究我们实体的行为。

9.可测试和测试
领域模型用于解决复杂的问题。复杂的问题意味着需要复杂的业务逻辑来解决。如果您有复杂的解决方案,则必须确保它能够正确运行,并且不必担心更改此逻辑。
这是单元测试输入的地方,这是干净的Domain Model的组成部分。干净的领域模型是可测试的,应该有一个最大的测试覆盖因素。这是我们应用程序的核心,我们应始终百分百确保它能够按预期工作。

10.应该轻松编写
这是以上所有内容的总结。如果每次都编写域模型代码是一个问题-很难更改,理解,测试,使用它-出现问题。你应该怎么做?
查看我上面描述的所有要点,看看您的模型不符合什么条件?也许取决于基础架构?也许他说的是非商业语言?也许是贫血,封装不良或无法测试?如果您的模型满足本文中描述的所有属性,那么解决业务问题应该轻松而愉快。我向你保证。
所以我有一个问题–今天您是通过编写业务逻辑来放松一下,还是再次碰壁?您的领域模型干净吗?