什么是领域驱动?
领域驱动设计 (domain-driver-design) 是有别于MVC开发模式的一种思想,它是面向对象编程的一种表现形式,请记住:领域驱动是一种思想,而不是技术!
领域驱动核心是通过对模型抽象出属性和行为进行开发。
如果没有数据库?
为什么使用领域驱动?
1、建立统一语言,为了团队更好的沟通。
2、通过模型来表达需求,模型是模块化、可扩展、易于维护的。
3、业务实现和技术实现隔离。
4、通过建模达到解耦的目的
领域驱动中的一些概念
1、统一语言
2、领域实体(聚合根)
3、值对象(value-object)
4、模型(失血模型、充血模型、胀血模型)
5、边界上下文
6、领域知识
7、领域事件
层级划分
1、表现层
2、应用服务层
3、领域层(领域实体和领域服务)
4、基础设施层(仓储、工具、rpc、第三方api)
通过一个 用例来展现领域驱动
一个登陆业务:
1、用户(User)输入账号(account)和密码(password)。
2、需要校验account和password正确性。
3、“7天未登陆” 或者 “更换设备”需要手机(phone)验证码(captha)校验。
4、登陆成功后做一些操作(比如:发个推送消息、做一条记录)
public UserLoginInfoRespDto login(UserLoginReqDto userLoginReqDto) {User user = userService.doLogin(userLoginReqDto);userRepository.updateUserData(user);String token = authService.createToken(user.getUserId(), user.getUserData().getDeviceId());//查询更新后的用户信息UserLoginInfoRespDto userLoginInfoRespDto = userAssembler.assemblerUserLoginInfo(userService.getWithFormsAndNotNull(user.getUserId()));userLoginInfoRespDto.setLoginToken(token);domainEventPublisher.publish(new UpdateResourcesEvent(this,Lists.newArrayList(user.getUserData().getUserName()),userLoginInfoRespDto.getUserId(),ResourceModeType.LOGIN.getValue()));return userLoginInfoRespDto;}
public User doLogin(UserLoginReqDto loginReqDto) {String userName = loginReqDto.getUserName();String deviceId = loginReqDto.getDeviceId();String captcha = loginReqDto.getCaptcha();String regChatId = baiduAIApiService.faceQuery(userName);if (StringUtil.isEmpty(regChatId)) {throw new BusinessException(ResponseStatus.USER_IS_NOT_REG);}User regUser = Optional.ofNullable(userRepository.getByChatId(regChatId)).orElseThrow(() -> new BusinessException(ResponseStatus.USER_IS_NOT_REG));regUser.validateNotLoginForLongTime();regUser.validateChangeDevice(deviceId,captcha);regUser.updateLoginData(loginReqDto);return regUser;}
让你的getter/setter丰满起来
领域驱动中,模型的getter/setter是可以具有逻辑的
public List<UserForm> getUserForms() {if (this.userForms == null) {if (this.userId == null) {this.userForms = new ArrayList<>();} else {this.userForms = SpringBeanUtils.getBean(IUserRepository.class).getForms(this.userId);}}return this.userForms;}
让应用层参与协调
应用层是很薄的一层,不包含任何逻辑,仅用于协调和发布命令
public Object prePay(PayReqDto payReqDto) {Fund fund = new Fund(payReqDto);payBizFactory.handleBiz(fund);Object prePayResult = payTypeFactory.prePay(fund);//保存日志时保存为负数fund.setUserFund(-fund.getUserFund());fundRepository.add(fund);return prePayResult;}
领域实体和领域服务
整个domain层都是逻辑的封装,domain是独立的,不能被其他层侵入,持久化通过接口隔离。
何时使用领域服务?
- 领域行为需要多个领域实体参与协作
- 领域行为与状态无关
- 领域行为需要与外部资源(尤其是DB)协作
面向资源库
把数据库(文件、nosql或者是其他持久化库)看作是一个集合,领域实体是其中的一个元素,整体持久化
@Overridepublic void add(User user) {UserDataEntity dataEntity = userConverter.convertUserDataEntityFromUser(user);userDataJpaRepo.save(dataEntity);user.setUserId(dataEntity.getUserId());UserInfoEntity infoEntity = userConverter.convertUserInfoFromUser(user);userInfoJpaRepo.save(infoEntity);UserAppendEntity appendEntity = new UserAppendEntity();appendEntity.setUserId(user.getUserId());appendEntity.setPhone(infoEntity.getPhone());if (dataEntity.getRegType() == RegisterType.WX.getValue()) {appendEntity.setPhone(dataEntity.getUserName());}userAppendJpaRepo.save(appendEntity);UserEngagementAwardEntity awardEntity = userConverter.convertEngagementAwardFromUser(user);userEngagementAwardJpaRepo.save(awardEntity);}
领域层隔离带来的问题
领域对象展示和持久化需要进行转换,Assembler 和 Translater
通过测试来快速验证模型
@Testpublic void testLogin(){User user = userService.getWithFormsAndNotNull(68L);user.validateNotLoginForLongTime();}
领域驱动是银弹吗?
如果你只是做一个简单的CRUD系统,我并不推荐。用这个思想开发起来并不快,而且具有一定的门槛。
不要生搬硬套其中的概念,灵活运用。
领域驱动的乐趣在于领域划分、建模,应对复杂业务变化。
题外话
设计模式、微服务划分