领域驱动设计中国峰会 2017

吴雪峰 - 当函数式遇上DDD

函数式编程和DDD都是最近热门的趋势,并且都不容易理解。通过一个小例子讨论用两个热门趋势的概念碰撞产生火花,使得参会快速理解函数式和DDD思想的精髓。

1. 函数式编程实现DDD 管理理复杂度
2. 聚焦领域 管理理复杂度 管理理复杂度: 分块 管理理复杂度: 分段 管理理复杂度: 分层
3. 举个例例⼦子 注册⽤用户
4. 业务流程 提炼 业务术语/对象 User
5. 建模 管理理复杂度: 分块 ⽤用类型建模
6. 领域建模 业务: User name password 实现: public class User { private String name; private String password; } ⾯面向对象: 类 CREATE TABLE User ( id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, username VARCHAR(30) NOT NULL, pwd VARCHAR(30) NOT NULL reg_date TIMESTAMP ) 数据库表
7. 领域建模 业务: User name password 实现: public class User { private String name; private String password; } ⾯面向对象: 类 CREATE TABLE User ( id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, username VARCHAR(30) NOT NULL, pwd VARCHAR(30) NOT NULL reg_date TIMESTAMP ) 数据库表 掺⼊入了了技术实现细节,并且模糊了了业务含义
8. 业务: 实现: 领域建模 User name password case class User(name: UserName, pwd: Password) 保留留业务 type UserName = String type Password = String 剥离技术实现
9. 当业务变得复杂 ⽤用户名种类多 注册途径多 密码⽅方式多 登录途径多
10. 领域建模 业务: User name password 实现: case class User(name: UserName, pwd: Password) 保持业务代码稳定 type UserName = String type Password = String 修改实现,⽀支持多种类⽤用户名,验证⽅方式 case class UserName(uid: Identification, typ: IdentificationType) case class Password(auth: AuthenticationWord,authType: AuthenticationType)
11. 注册⽤用户 开始 检查⽤用户名 合规 检查⽤用户名 是否存在 检查密码 是否合规 密码⽂文本 加密 创建⽤用户 返回 成功 返回 错误 清理理 垃圾数据
12. 注册⽤用户 public User register(String name, String password) throws Exception { if(!isUserNameValidate(name)) { ⾮非业务术语 throw new RegisterException("User name is invalidate"); // 缺乏具体的错误信息 } if(!isUserNameExist(name)) { throw new RegisterException("User name is exist"); } if(!isPasswordValidate(password)) { throw new RegisterException("Password is invalidate"); // 缺乏具体的错误信息 } String encodedPwd = encodePwd(password); User newUser = createUser(name, password); return newUser; }
13. 改进 注册⽤用户 public User register(String name, String password) throws Exception { checkUserNameValidate(name) checkUserNameExist(name)) checkPasswordValidate(password) String encodedPwd = encodePwd(password); User newUser = createUser(name, password); return newUser; } private int USERNAME_MIN_LENGTH = 3 private void checkUserNameValidate(String name) throws UserNameValidationException { if(StingUtils.isEmpty(name)) { throw new UserNameValidationException("User name can not be empty"); } if(name.length < USERNAME_MIN_LENGTH) { throw new UserNameValidationException("length of User name must more then " + USERNAME_MIN_LENGTH); } if(isContainInvalidateCharacter(name)) { throw new UserNameValidationException("User name contains invalidate character"); } ...... } private void checkUserNameExist(String name) ......
14. 业务流程 管道建模 管理理复杂度: 分段 开始 检查⽤用户名 合规 检查⽤用户名 是否存在 检查密码 是否合规 密码⽂文本 加密 创建⽤用户 返回 成功 返回 错误 业务流程: 管道 清理理 垃圾数据
15. 开始 UserInfo 检查合⽤用规户名validatedNam检e是查否⽤存用户在名notExistedNam检是e 查否密合v码规alidatedPassw密or码加d ⽂密文本encodedPwd创建⽤用户encodedPwd 返回 成功 FailedResult FailedResult 返回 错误 清理理 垃圾数据 管道 def registerUser(userInfo: UserInfo): BusinessResult[User] = for { validatedName <- checkUserNameValidate(userInfo.name) notExistedName <- checkUserNameExist(validatedName) validatedPassword <- checkPasswordValidate(userInfo.pwd) encodedPwd <- encodePwd(validatedPassword) newUser <- createUser(notExistedName, encodedPwd) } yield newUser
16. 实现: ⾮非业务 管道 sealed trait BusinessResult[+A] { def map[B](f: A => B): BusinessResult[B] def flatMap[B](f: A => BusinessResult[B]): BusinessResult[B] } case class OKResult[+A](result: A) extends BusinessResult[A] { def map[B](f: A => B): BusinessResult[B] = OKResult(f(result)) def flatMap[B](f: A => BusinessResult[B]): BusinessResult[B] = f(result) } case class FailedResult(errorCode: ErrorCode, messages: String*) extends BusinessResult[Nothing] { def map[B](f: Nothing => B): BusinessResult[B] = this def flatMap[B](f: Nothing => BusinessResult[B]): BusinessResult[B] = this }
17. UI Form 业务流程 管道建模 管理理复杂度: 分层 UI Message Http Request Http Response View Model:Request Json View Model:Result Json Domain Pipeline Domain Model DAO/Persist Model DB
18. 关注点分离 DDD&FP 根本指导原则 DDD 构建业务和技术都懂的通⽤用语⾔言 去除业务中的⾏行行话 去除技术中的⾏行行话 FP ⼀一切都是不不可变,⽆无副作⽤用 ⼀一个函数只关注输⼊入和输出 函数和函数可以组合
19. 管理理复杂度

相关幻灯片