DDD- 领域驱动设计入门

简介: 云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 概述 去年4月份入职后第一次了解到DDD,当时觉得很晦涩(现在来看应该是红皮书将战略设计放在前半部分的缘由)。

云栖号资讯:【点击查看更多行业资讯
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!


概述

去年4月份入职后第一次了解到DDD,当时觉得很晦涩(现在来看应该是红皮书将战略设计放在前半部分的缘由)。今年4月份的时候想学学系统设计方面的知识,便开始静下心来好好研究了两个月,感觉收益颇多。

DDD是什么

DDD是一种'面向对象'的软件设计思想。

领域驱动设计DDD作为一种软件设计方式, 有利于创造一个可测试的、可伸缩的、组织良好的软件模型

贫血症与失忆症

写Java差不多刚好两年, 接触过的项目中,几乎所有的业务逻辑都是写在service里面,对象都只是数据持有器(POJO),这些没有任何业务方法的对象就是贫血对象。

在这样的系统中,代码里面往往充斥者大量与业务无关的getter、setter方法,开发人员会将将大量的精力放在数据映射上面,而不是真正有价值的对象行为。

下面是一个更新用户的例子:

  public void updateUser(String userId, String firstName, String lastName, String mobile,String address, Integer sex, String avator, ...) {
        User user = new User();
        if(userId != null) {
            user.setUserId(userId);
        }
        if(firstName != null){
            user.setFirstName(firstName);
        }
        if(lastName != null){
            user.setLastName(lastName);
        }
        if(mobile != null){
            user.setMobile(mobile);
        }
        if(address != null){
            user.setAddress(address);
        }
        if(sex != null){
            user.setSex(sex);
        }
        if(avator != null){
            user.setAvator(avator);
        }
        ...
        userDao.updateUser(user);
    }

首先:这个方法的业务意图不明确。应用层应该一个方法对应一个明确的业务用例,如更新用户手机号, 更新居住地址等。

其次:方法的实现增加了潜在的复杂性。 我们应该怎么测试这段代码呐?这么多if, 你确定你能愉快的修改代码吗,内心是不是很排斥?

最后:User对象只是一个数据持有器。

这种情况也被称为由贫血症导致的失忆症。

如何DDD

通用语言

通用语言是团队之间用于交流的语言,一般指当前业务场景提炼出来的领域术语、词组和句子。比如绩效系统里"271排名"就是一种领域术语。

限界上下文

限界上下文可以看成是整个应用程序之内的一个概念性边界。这个边界之内的每种领域术语、词组或句子 --- 也即通用语言,都有确定的上下文含义。而在边界之外,这些术语可能有不同的意思。

比如盘点,当HR说盘点时是对员工进行盘点;而IT说盘点的时候,是对设备进行盘点。两者的上下文是完全不一样的,盘点的语义自然不同。

以日程管理系统开始DDD学习

部分用例

  • 企业员工发起一个日程,包含日程的主题、开始时间、结束时间、参与人员。
  • 发起人修改日程时间
  • 参与人确认日程
  • 员工查看自己当天的所有日程

传统面向过程的写法

/**
  创建日程:1.日程表中插入一条记录, 2.人关联日程
**/
@Transactional
public void createSchedule(String title, Date startTime, Date endTime, List<Long> participantIds){
  Long sponsorId = loginUser.get().getUserId();
  //创建日程
  Schedule schedule = new Schedule();
  schedule.setTitle(title);
  schedule.setStartTime(startTime);
  schedule.setEndTime(endTime);
  schedule.setSponsorId(sponsorId);
  Long scheduleId = scheduelDao.insert(schedule);
  
  //人关联日程
  List<UserSchedule> userSchedules = new LinkedList();
  for(participantId : participantIds){
      userSchedules.add(new UserSchedule(participantId, scheduleId, 0));
  }
  if(!userSchedules.isEmpty()){
      userScheduleDao.batchInsert(userSchedules);
  }
}

这样写其实也暴露了底层数据结构

DDD写法

日程对象

public class Schedule{
    private String title;
    private Date startTime;
    private Date endTime;
    private User sponsor;
    Set<User> participants;
    Set<User> confirmUsers;
    Set<User> refuseUsers;
    /**
        通过构造函数创建一个日程
    **/
    pubulic Schedule(String title, Date startTime, Date endTime, User sponsor, Set<User> participants){
        if(title == null || title == ""){
            throw new IllegalArgumentException("schedule title empty");
        }
        if(startTime == null || endTime == null || !startTime.before(endTime)){
            throw new IllegalArgumentException("schedule time range invalid.");
        }
        if(sponsor == null){
            throw new IllegalArgumentException("schedule sponsor not found");
        }

        //remove duplicate
        participants = participants.strem().filter(participant -> !participant.equals(sponsor)).collecot(Collectors.toSet());
        this.title = title;
        this.startTime = startTime;
        this.endTime = endTime;
        this.participants = participants;
    }
    /**
        更新日程时间
    **/
    void updateTimeRange(Date startTime, Date endTime){
        if(startTime == null || endTime == null || !startTime.before(endTime)){
            throw new IllegalArgumentException("schedule time range invalid.");
        }
        this.startTime = startTime;
        this.endTime = endTime;
    }

    /**
        日程确认
    **/
    void confirm(User participant){
        if(!this.participants.contains(participant)){
            throw new IllegalArgumentException("you are not invited, can`t confirm");
        }
        if(this.confirmUsers.contains(participant)){
            throw new IllegalArgumentException("you have confirmed");
        }
        this.confirmUsers.add(participant);
    }
}

应用层 -- ApplicationService

/**
    创建日程
**/
@Transactional
public void createSchedule(String title, Date startTime, Date endTime, List<Long> participantIds){
    Long sponsorId = loginUser.get().getUserId();
    User sponsor = useRepository.userOfId(sponsorId);
    List<User> participants = useRepository.usersOfIds(participantIds);
    Schedule schedule = new Schedule(title, startTime, endTime, sponsor, participants);

    scheduleRepository.add(schedule);
}

/**
    更新日程时间
**/
@Transactional
public void updateScheduleTime(Long scheduleId, Date startTime, Date endTime){
    Schedule schedule = scheduleRepository.scheduleOfId(scheduleId);
    schedule.updateTimeRange(startTime, endTime);

    scheduleRepository.save(schedule);
}


/**
    确定日程
**/
@Transactional
public void confirmSchedule(Long scheduleId){
    Long userId = loginUser.get().getUserId();
    User user = useRepository.userOfId(userId);
    Schedule schedule = scheduleRepository.scheduleOfId(scheduleId);
    
    schedule.confirm(user);

    scheduleRepository.save(schedule);
}

The end

通过上面日程管理的demo,你是否可以重构updateUser方法呐?

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/live

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-06-15
本文作者:油多坏不了菜
本文来自:“掘金”,了解相关信息可以关注“掘金”

相关文章
|
6月前
|
消息中间件 测试技术 领域建模
DDD - 一文读懂DDD领域驱动设计
DDD - 一文读懂DDD领域驱动设计
9747 3
|
消息中间件 开发者
DDD领域驱动设计实战(六)-领域服务(上)
DDD领域驱动设计实战(六)-领域服务
452 0
DDD领域驱动设计实战(六)-领域服务(上)
|
前端开发 架构师 Java
领域驱动设计DDD从入门到代码实践
在本文中,作者将借鉴《实现领域驱动设计》的做法,介绍领域驱动设计的基本概念的同时,用一个虚拟的公司和一个虚拟的项目,把领域驱动设计进行落地实践。
13221 9
领域驱动设计DDD从入门到代码实践
|
存储 设计模式 前端开发
浅析 DDD 领域驱动设计(1)
浅析 DDD 领域驱动设计
350 0
浅析 DDD 领域驱动设计(1)
|
存储 Java 数据建模
DDD的精髓(3)
DDD的精髓(3)
295 0
DDD的精髓(3)
|
设计模式 领域建模 数据库
DDD领域驱动设计落地实践系列:初识DDD
笔者在经历的很多项目中都使用了DDD领域驱动设计进行架构设计,尤其是在业务梳理、中台规划以及微服务划分等方面,DDD是重要的架构设计方法论,对平时的架构设计有非常好的指导作用。从本文开始笔者将通过一系列的文章阐述自己对于DDD的理解以及如何在项目实战中落地实践DDD。本文作为系列文章的开端,主要和大家聊聊DDD的一些基本概念以及常用方法。
DDD领域驱动设计落地实践系列:初识DDD
|
缓存 数据可视化 Java
浅析 DDD 领域驱动设计(2)
浅析 DDD 领域驱动设计
297 0
浅析 DDD 领域驱动设计(2)
|
设计模式 SQL 测试技术
一文理解 DDD 领域驱动设计!
以一种领域专家、设计人员、开发人员都能理解的通用语言作为相互交流的工具,在交流的过程中发现领域概念,然
一文理解 DDD 领域驱动设计!
|
数据库
DDD的精髓(2)
DDD的精髓(2)
205 0
DDD的精髓(2)
|
安全 数据安全/隐私保护
DDD领域驱动设计实战(六)-领域服务(中)
DDD领域驱动设计实战(六)-领域服务
242 0
DDD领域驱动设计实战(六)-领域服务(中)