javascript:领域驱动设计简介【译文】

领域驱动设计是软件开发的方法,它使我们能够将复杂的问题域转换为丰富,富有表现力和不断发展的软件.当我们的用户需求趋于复杂时我们用以设计软件的一种方式.
-by Khalil Stemmler

原文链接: An Introduction to Domain-Driven Design - DDD w/ TypeScript
在这里插入图片描述

DDD简介

你是否曾经在某个代码库里实现需求,让你感到”我写的代码越多,代码库的复杂性就越不可控?”

你有没有曾经想过,”你自己以往是怎么实现业务逻辑的?”

或者你是否处于一种处境,你特别害怕添加新的代码到已有的工程里,因为你担心你提交的代码会污染到工程里某一处不可知的代码。

面对这种状况,其他企业是怎么做的呢? 他们的代码库如此的庞大,他们是怎样去完成工作的?是怎么管理复杂性的呢? 他们是怎样分解代码,分派给各个团队,又怎样将团队的成果集成到一起的呢?
我很好奇这所有的一切,与此同时,我也在编码一个已存在三年的nodejs项目,这个项目的行数已经将近150K+了.
我恰好遇到了领域驱动设计,那一刻我意识到我特别需要他.

快速回顾

2017年,我开始开发一个名为Univjobs的应用程序,这是一个面向加拿大学生和即将毕业的学生的市场,它可以帮助学生找到的co-op的工作,演出和入门级工作和实习。

这个最小可行性产品(MVP)一开始是特别简单的,学生可以注册,创建他们的简历,申请工作,雇主可以注册,发布工作,浏览学生申请或者邀请他们申请发布的工作。
从2017开始,我们迭代多次,根据学生和雇主的反馈调整和添加新的功能,比如工作推荐,面试和申请人跟踪系统等。

最终,代码库变得非常大,以至于在其上添加一个新的功能所花费的时间几乎是我开始的时候所花费的时间的3倍。

缺乏封装和面向对象的设计是犯错的原因。
贫血模型实例
也就是这个时间点,我开始寻找解决问题的方案.

关于领域驱动设计

领域驱动设计是一种软件开发方法,旨在提供一个框架,用于创建软件来匹配我们正在解决的问题领域的自然模型。
DDD Diagram
最初被概念化的是由DDD圣经(著名的蓝皮书)的作者Eric Evans提出来的.它的主要优势在于,它使您能够编写具有可测试性和可维护性的,富有表现力,充血性且封装良好的软件。

一般来说,它使我们能够通过使用分层架构,领域建模块和通用的语言来实现这一目标。

The Ubiquitous Language是最能描述域模型概念的共同语言.它必须通过花时间与领域专家交流来形团队统一的交流语言.一旦达成一致,这种语言就是一种将软件本身的表现与现实世界中实际发生的场景映射起来的方式。

如果我们正在搭建一个帮助招聘人员招聘人才的应用,我们需要花一些时间从招聘人员的角度来理解这个领域的语言(知识)和流程。

这意味着实际上需要和领域专家交流

分层架构+设计原则+模式

领域驱动设计需要软件设计模式和原理的基础知识。它在敏捷环境中运行良好,并且重视首先提供最简单的事情,然后迭代地改进它的设计。

这挑战在于:您需要了解这些基本设计原则和模式,才能在第一时间做好设计.如果我们弄得一团糟,那么运用DDD会困难得多。

In order to go fast,we must go well.

为了更好地完成DDD,我们需要牢记SOLID原则,在我们的分层架构的核心构造一个中心域层,并通过接口的方式来适配持久性,Web和外部技术。我们不希望这些东西污染了我们的域模型。(译者注: uncle Bob是SOLID原则和清晰架构的提出者,一位世界级的软件开发大神,赶紧膜拜!!!)

名称解释翻译
SRPThe Single Responsibility Principle单一责任原则
OCPThe Open Closed Principle开放封闭原则
LSPThe Liskov Substitution Principle里氏替换原则
DIPThe Dependency Inversion Principle依赖倒置原则
ISPThe Interface Segregation Principle接口分离原则

我们希望将它们保持在一定距离,以便我们可以隔离我们的域并快速实现单元测试。
“清晰架构”.来自鲍勃叔叔(uncle Bob)的文档.也称为分层架构,端口和适配器,Hexigonal等.

构建的模块

简而言之,这些是实施DDD所涉及的主要技术工件.

实体

这些是我们唯一确定的对象.它们具有生命周期,可以在其中创建,更新,持久化,从持久性,存档和删除中检索它们.
实体通过其唯一标识符(通常是某种UUID或主键)进行比较.
在这里插入图片描述

值对象

值对象没有标识.它们属于实体的属性.比如Name是User实体上的值对象.
他们在结构层间上进行比较.

聚合

这些是由聚合源绑定在一起的实体集合.聚合源是我们引用查找的东西.集合边界内的任何成员都不能直接从集合外部的任何内容引用.这是聚合保持一致性的方式.
(个人理解是,完成某个领域的业务逻辑所需的实体的集合)
聚合最强大的部分是,他们分发领域事件,这能够定位到子领域的业务逻辑上.

域服务

这是我们在概念上定位不属于任何一个对象的域逻辑的地方.
(个人理解,是封装跨聚合的业务逻辑的地方,提供服务能力的模块)

仓储

我们使用资源库来从持久性技术中引用域对象.使用里氏代换原则和分层架构等软件设计原则,我们可以通过某种方式进行设计,以便我们可以轻松地做出架构决策,以便在内存存储库之间进行切.以进行测试,例如如今的MySQL实现,以及2年后基于MongoDB的实现.

工厂

我们想要以多种不同的方式创建域对象.我们使用工厂,通过原始的sql语句,json或从ORM工具返回的Active Record(如Sequelize或TypeORM)映射到域对象。

我们还可能想使用原型模式或通过使用抽象工厂模式从模板中创建域对象

域事件

领域驱动设计的最佳部分.(个人理解,也是最核心的部分,他能将各种业务场景的实现进行松耦合化,服务里,事件应该是一等公民,他可以成为不同服务之间的”桥梁”)

域事件只需要定义域专家关心的域中发生的某种事件的对象。

通常,当我们处理CRUD应用程序时,我们会添加新的域逻辑,我们通过添加更多if/else语句来识别这些逻辑。
然而,在复杂的应用程序中,这会让服务可能变得非常麻烦
使用域事件,而不是像这样添加越来越多的if/else块:

class UserController extends BaseController {  
	public createUser () {    
	...    
	await User.save(user);   
 // After creating a user,we handle both:   
  // 1. Recording a referral (if one was made)    
  	if (user.referred_by_referral_code) {      
  	// calculate payouts      
  	// .. there could be a lot more logic here      
  		await Referral.create({ code:this.req.body.referralCode,user_id: user.user_id });    
 	}    
  // 2. Sending an email verification email    		  
  	EmailToken.createToken();   
  	await EmailService.sendEmailVerificationEmail(user.user_email);    
 // mind you,neither of these 2 additonal things that need to get    
 // done are particularly the responsibility of the "user" subdomain    this.ok();  
 }}

处理域逻辑的示例(事务脚本样式)
我们可以像这样子很优雅地实现事务:
在这里插入图片描述

使用域服务(例如ReferralFactoryService)和应用服务(例如EmailService),域事件可用于将域逻辑的关注点从它所属的子域中分离出来。
域事件是解耦和链接真正复杂的业务逻辑的绝佳方式。

技术优势

编写出可测试的业务逻辑层
花费更少的时间去修bug
随着代码量的增加,代码库实际上是得到改进的,而不是降低质量
能够创建复杂领域的持久化软件

技术劣势

领域建模一开始是耗时的,也这是一种需要学习的技术。
因为它涉及域模型的大量封装和隔离,所以可能需要一些时间来实现.
根据项目的不同,继续构建贫血领域模型可能更值得

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值