草庐IT

领域驱动设计简介

不忘初心 方得始终 2023-03-28 原文

概述

当今的企业应用程序无疑是复杂的,并且依靠某些专门技术(持久性、AJAX、WEB服务器等)来完成其工作。作为开发人员,我们倾向于专注于这些技术细节是可以理解的,但事实就是,不能解决业务需求的系统对任何人都没有用,无论它的外观多么漂亮或其基础架构的如何牛逼。

领域驱动设计

(DDD)的哲学是关于将我们的注意力放在应用程序的核心,专注于业务领域固有复杂性本身。我们还将核心域(对于业务而言是唯一的)与支持子域(本质上通常是通用的,例如金钱和时间)区分开来,并将我们更多设计工作适当地放在核心上。领域驱动设计由一组模式组成,这些模式用于从领域模型开始构建企业应用程序。在你的软件职业生涯中,你可能已经遇到了许多这样的想法,尤其是如果你是一位使用OO语言经验丰富的开发人员。将他们一起应用将使您能够构建真正满足业务需求的系统。
下图是要展现的模式和模式间关系的总图。

代码和模型

借助DDD,我们正在寻求创建问题域的模型。持久性,用户界面和消息传递的内容可能会在以后出现,这是需要了解的领域。从模型中去除在设计中使用的术语和所赋予的基本职责后,代码就成了模型的表达式,所以对代码的一个变更就可能称为对模型的变更。
这个影响会涉及到项目的实现中。为了紧密捆绑起实现和模型,通常需要支持建模范型的软件开发工具和语言,例如面向对象编程。面向对象编程非常适合对模型的实现,因为它们基于同一个范型。面向对象编程提供了对象的类和类之间的关联关系、对象实例、
以及对象实例之间的消息通信。面向对象编程语言让建立模型对象、对象关系与它们的编程副本之间的直接映射成为可能。过程化语言提供了有限的模型驱动设计的支持。这样的语言不能提供实现模型关键组件所必须的构建能力。

这是DDD模式的第一个:模型驱动设计。这意味着能够将模型中的概念(理想情况下完全按字面意义)映射到设计/代码的概念。模型的改变意味着代码的改变。更改代码意味着模型已更改。DDD并不要求你使用面向对象对域进行建模-例如,我们可以使用规则引擎来构建模型-但鉴于主要的企业编程语言是基于OO的,因此大多数模型的本质上都是OO。毕竟,OO是基于建模范例的。模型的概念将表示为类的接口,职责将表示类成员。

模型上下文

每当我们讨论模型时,它总是在一定范围内。通常可以从使用该系统的最终用户集合中推断出此上下文。因此,我们有一个部署到交易员的前台交易系统,或一个超市收银员使用的销售点系统。这些用户以特定的方式与模型的概念相关,并且模型的术语对这些用户有意义,但对于上下文之外的任何其他人则不一定。不是试图保持一个迟早要四分五裂的大模型,我们应该做的是有意识地将大模型分解成数个较小的部分。只要遵守相绑定的契约,整合得好的小模型会越来越有独立性。每个模型都应该有一个清晰的边界,模型之间的关系也应该被精确地定义。DDD将此称为有界上下文(BC)。每个领域模型仅存在于一个BC中,而BC恰好包含一个领域模型。

我必须承认,当我第一次读到BC时,我看不出要点:如果BC与领域模型同构,为什么要引入一个新术语?如果只有最终用户与BC进行交互,那么也许不需要这个术语。但是不同的系统(BC)也彼此交互,发送文件,传递消息,调用API等.如果我们知道有两个BC相互交互,则我们必须注意在一个概念之间进行传换。域或其他域。
在模型周围放置明确的边界还意味着我们可以开始讨论这些BC之间的关系。实际上,DDD标识了BC之间的一整套关系,以便我们可以合理化当我们需要将不同的BC链接在一起应该采取的的措施:

  • 已发布的语言:交互的BC商定一种共同的语言(例如,企业服务总线上的一堆XML模式),通过它们可以彼此交互。
  • 开放的主机服务:BC指定任何其他BC可以使用其他服务协议(例如Restful
    Web服务);
  • 共享内核:两个BC使用通用的代码内核(例如,库)作为通用的通用语言,但其他方式则以自己的特定方式执行;
  • 客户-供应商:一个BC使用另一个服务的服务,并且是另一个BC的利益相关者(客户)。因此,它可以影响该BC提供的服务;
  • 顺从者:一个BC使用另一个服务,但不是该另一个BC的利益相关者。因此,它使用原样(符合)该BC提供的协议或API;
  • 防崩溃层:一个BC使用另一方的服务,而不是利益相关者,但其目的是引入一组适配器将一个BC依赖的BC的变化所产生的影响降至最低,即反腐层。
    可以看到,当我们在列表中单击时,两个BC之间的合作水平逐渐降低。使用已发布的语言,我们从BC开始建立它们可以交互的通用标准。他们都不拥有这种语言,而是由他们所居住的企业拥有(甚至可能是行业标准)。使用开放主机,我们仍然做的不错;BC提供了作为运行时服务的功能,供任何其他BC调用,但随着服务的发展,它将(可能)保持向后兼容性。

图 2:有界上下文关系的频谱

然而,当我们开始循规蹈矩时,我们只是生活在我们身边;一个 BC 显然是从属于另一个的。如果我们必须与以百万美元购买的总系统集成,那很可能就是我们所希望的情况。如果我们使用反腐败层, 那么我们通常会与遗留系统集成,但引入一个额外的一层来尽可能地将我们自己隔离开来。当然,这需要花钱来实施,但它降低了依赖性风险。反腐败层也比重新实施该遗留系统便宜得多,这充其量会分散我们对核心领域的注意力,最坏的情况是以失败告终。

DDD 建议我们绘制一个上下文映射来识别我们的 BC 以及我们依赖或依赖的那些,识别这些依赖的性质。图 3 显示了我过去 5 年左右一直在研究的系统的上下文映射。

图 3:上下文映射示例

所有这些关于上下文映射和 BC 的讨论有时被称为战略 DDD,这是有充分理由的。毕竟,当你想到它时,弄清楚 BC 之间的关系都是非常政治化的:我的系统将依赖哪些上游系统,我是否容易与它们集成,我是否对它们有影响力,我是否信任它们?下游也是如此:哪些系统将使用我的服务,我如何将我的功能公开为服务,他们是否对我有影响力?误解这一点,您的应用程序很容易失败。

层和六边形

现在让我们转向内部,考虑我们自己的 BC(系统)的架构。从根本上说,DDD 只真正关心领域层,实际上,它并没有对其他层有很多话要说:表示层、应用程序或基础设施(或持久层)。但它确实希望它们存在。这就是分层架构模式。

图 4:分层架构

当然,我们多年来一直在构建多层系统,但这并不意味着我们一定很擅长。确实,过去的一些主导技术 - 比如,EJB2,- 对域模型可以作为有意义的层存在的想法产生了积极的危害。所有的业务逻辑似乎都渗入了应用层或(甚至更糟的)表示层,留下了一组贫乏的领域类 [3] 作为数据持有者的空壳。这不是 DDD 的内容。

所以,绝对清楚,应用层不应该有任何域逻辑。相反,应用层负责诸如事务管理和安全性之类的事情。在某些体系结构中,它还可能负责确保从基础结构/持久层检索的域对象在与交互之前正确初始化(尽管我更喜欢基础结构层来代替)。

当表示层在单独的内存空间中运行时,应用层还充当表示层和域层之间的中介。表示层通常处理域对象或域对象(数据传输对象,或 DTO)的可序列化表示,通常每个“视图”一个。如果这些被修改,则表示层将任何更改发送回应用层,应用层又确定已修改的域对象,从持久层加载它们,然后将更改转发到这些域对象。

分层架构的一个缺点是它暗示了依赖关系的线性堆叠,从表示层一直到基础设施层。但是,我们可能希望在表示层和基础设施层中支持不同的实现。如果(我认为我们是!)我们想要测试我们的应用程序,那就肯定是这种情况:

  • 例如,FitNesse [4] 等工具允许我们从最终用户的角度验证系统的行为。但是这些工具一般不经过表示层,而是直接进入下一层,即应用层。所以从某种意义上说,FitNesse 充当了另一种观看者的角色。
  • 同样,我们很可能有多个持久性实现。我们的生产实现可能使用 RDBMS 或类似技术,但对于测试和原型设计,我们可能有一个轻量级实现(甚至可能在内存中),因此我们可以模拟持久性。
    我们可能还想区分“内部”和“外部”层之间的交互,其中内部我的意思是两个层都完全在我们的系统(或 BC)内的交互,而外部交互则跨越 BC。
    因此,与其将我们的应用程序视为一组层,不如将其视为六边形 [5],如图 5 所示。我们最终用户使用的查看器以及 FitNesse 测试使用内部客户端API(或端口),而来自其他 BC 的调用(例如用于开放主机交互的 RESTful,或用于已发布语言交互的 ESB 适配器调用)命中外部客户端端口。对于后端基础设施层,我们可以看到替代对象存储实现的持久端口,此外,我们域层中的对象可以通过外部服务端口调用其他 BC。

图 5:六边形架构

这种大规模的东西已经足够了,让我们多干实事,少扯虚的.

有关领域驱动设计简介的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  3. ruby-on-rails - 如何在 Ruby on Rails 中实现由 JSF 2.0 (Primefaces) 驱动的 UI 魔法 - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道ruby​​onrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim

  4. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  5. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  6. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  7. HBase Region 简介和建议数量&大小 - 2

    Region是HBase数据管理的基本单位,region有一点像关系型数据的分区。region中存储这用户的真实数据,而为了管理这些数据,HBase使用了RegionSever来管理region。Region的结构hbaseregion的大小设置默认情况下,每个Table起初只有一个Region,随着数据的不断写入,Region会自动进行拆分。刚拆分时,两个子Region都位于当前的RegionServer,但处于负载均衡的考虑,HMaster有可能会将某个Region转移给其他的RegionServer。RegionSplit时机:当1个region中的某个Store下所有StoreFile

  8. ruby-on-rails - 设计注册确认 - 2

    我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:

  9. ruby-on-rails - 设计通过 reset_password_token 获取用户 - 2

    我正在尝试创建密码规则来设计可恢复的密码更改。我通过passwords_controller.rb做了一个父类(superclass),但我需要在应用规则之前检查用户角色,但我所拥有的只是reset_password_token。 最佳答案 假设您的模型是用户:User.with_reset_password_token(your_token_here)Source 关于ruby-on-rails-设计通过reset_password_token获取用户,我们在StackOverflow

  10. ruby-on-rails - Rails 5,公寓和设计 : sign in with subdomains are not working - 2

    我已经使用Apartment设置了一个Rails5应用程序(1.2.0)和Devise(4.2.0)。由于某些DDNS问题,应用只能在app.myapp.com下访问(请注意子域app)。myapp.com重定向到app.myapp.com。我的用例是每个注册该应用的用户(租户)都应该通过他们的子域(例如tenant.myapp.com)访问他们的特定数据。用户不应限定在其子域内。基本上应该可以从任何子域登录。重定向到租户的正确子域由ApplicationController处理。根据Devise标准,登录页面位于app.myapp.com/users/sign_in。这就是问题开始的

随机推荐