Git Product home page Git Product logo

eas-ddd's People

Contributors

agiledon avatar iamzhangyi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

eas-ddd's Issues

TrainingContext - 确定候选人是否已经参加过该课程的培训

组合任务,由LearningService承担,分解的任务为:

  • 确定候选人是否已经参加过该课程
    • 获取该培训对应的课程
    • 确定课程学习记录是否有该候选人

该组合任务的两个原子任务都会访问数据库,由Repository完成,故而无需针对这两个原子任务进行测试驱动开发。

时序图脚本如下:

LearningService.beLearned(candidateId, trainingId) {
    TrainingRepository.trainingOf(trainingId);
    LearningRepository.isExist(candidateId, courseId);
}

EmployeeContext - 按照规则生成员工ID

为新员工生成一个唯一的员工号。员工号的生成规则为:
{入职日期}-{4位顺序号}

说明:

  • 入职日期为员工入职当天的日期,格式为{yyyyMMdd}

例如入职日期为2019年12月24日,最近入职员工的顺序号为0101,则新生成的员工号为:201912240102

EmployeeContext - 员工入职

作为一名HR,
我希望办理员工入职,
以便于提供更好的员工管理与服务。

任务分解为:

  • 入职 (#1 )
    • 验证员工信息 (#2 )
    • 生成员工号 (#5 )
      • 获取最后入职员工的顺序号
      • 按照规则生成员工号
    • 根据身份证号或手机号确认该员工是否已经存在 (#6 )
    • 添加员工 (#6 )

时序图伪代码为:

EmployeeAppService.onboarding(OnboardingRequest) {
    OnboardingRequestAssembler.composeEmployee() {
        OnboardingRequest.toEmployee();
        EmployeeIdGenerator.generate() {
            EmployeeRepository.latestEmployee();
            employee.idFrom(sequenceCode);
        }
    }
    EmployeeService.onboarding(employee) {
        EmployeeRepository.isExist(idNumber, mobilePhone);
        EmployeeRepository.save(employee);
    }
}

**说明:**需求分析与领域模型参见本项目的Wiki

AttendanceContext - 确定出勤状态

根据员工考勤记录、请假情况、假日设置确定员工的出勤状况。测试用例包括:

  • 日期为假期,员工无考勤记录或为无效考勤记录,出勤状态为Holiday
  • 日期为假期,员工有有效考勤记录,出勤状态为Overtime
  • 日期为工作日,员工有考勤记录,上下班打卡时间满足工作时间规则定义,出勤状态为Normal
  • 日期为工作日,员工有考勤记录,上班打卡事件晚于上班时间定义范围,出勤状态为Late
  • 日期为工作日,员工有考勤记录,下班打卡事件早于上班时间定义范围,出勤状态为LeaveEarly
  • 日期为工作日,员工有考勤记录,上班打卡事件晚于上班时间定义范围,下班打卡事件早于上班时间定义范围,出勤状态为LateAndLeaveEarly
  • 日期为工作日,员工无考勤记录,无请假记录,出勤状态为Absence
  • 日期为工作日,员工无考勤记录,有请假记录,出勤状态为请假类型

ProjectContext - 分配问题

IssueService领域服务承担职责,其子任务包括:

  • 获得问题
  • 分配问题给经办人
  • 更新问题
  • 创建问题的变更记录

伪代码:

    IssueService.assign(issueId, owner) {
        Issue issue = IssueRepository.issueOf(issueId);
        issue.assignTo(ownerId);
        IssueRepository.update(issue);
        ChangeHistoryRepository.add(changeHistory);
    }

对于IssueassignTo方法,需要编写测试用例覆盖:

  • 状态为ResolvedClosed的Issue不能再分配
  • 如果Issue已有Owner,不能将该Issue分配给同一个Owner

AttendanceContext - 生成出勤记录

作为一名人事管理专员,
我希望系统能够生成出勤记录,
以便于及时对员工进行考勤。

任务分解为:

  • 生成出勤记录
    • 获取员工信息
    • 生成员工出勤记录 (#9 )
      • 获取员工工作时间 (#10 )
        • 根据日期获取员工的打卡记录
        • 生成员工工作时间
      • 获取工作时间规则
      • 确定是否节假日
      • 获取员工请假信息
      • 确定出勤状态 (#8 )
    • 保存出勤记录

时序图脚本为:

AttendanceAppService.generate(day) {
    AttendancesGenerator.generate(day) {
        List<String> eployeeIds = EmployeeClient.allEmployeeIds();
        for (String empId : employeeIds) {
            AttendanceGenerator.generate(empId, day) {
                TimeCardGenerator.generate(empId, day) {
                    PunchedCardRepository.punchedCardsOf(empId, day);
                    TimeCard.createFrom(List<PunchedCard>);
                }    
                WorktimeRule worktimeRule = WorktimeRuleRepository.worktimeRule();
                boolean isHoliday = HolidayRepository.isHoliday(day);
                Leave leave = LeaveRepository.leaveOf(empId, day);
                Attendance.assureStatus(timeCard, worktimeRule, leave, isHoliday);
            }
        }
        AttendanceRepository.saveAll(attendances);
    }
}

**说明:**该Story的需求说明与领域模型参见本项目的Wiki页面

TrainingContext - 培训签到

As 一名培训学员
I want to 签到
So that 记录我已正常出勤

场景1:学员签到
Given:拥有Confirmed状态的培训票的学员
And: 培训已经开始
When: 签到
Then: 记录学员的出勤信息
And: 培训票被设置为Closed状态
And: 记录学员的学习信息

时序图脚本:

TrainingAppService.checkIn(checkInRequest) {
    CheckInService.checkIn(traineeId, trainingId) {
        TicketRepository.ticketOf(traineeId, trainingId, ticketStatus);
        Ticket.checkIn();
        TicketRepository.update(ticket);
        AttendanceRepository.add(attendance);
        LearningService.append(trainee, trainingId) {
            CourseRepository.courseOf(trainingId);
            LearningRepository.add(learning);
        }
    }
}

TrainingContext - 将票提名给候选人

组合任务,由TicketService领域服务承担,任务分解为:

  • 提名候选人
    • 获得培训票
    • 提名
    • 保存票的状态
    • 添加票的历史记录
    • 将被提名人移除候选人名单

“提名”原子任务,由Ticket聚合根实体承担。该任务的测试用例包括:

  • 验证票的状态必须为“Available”
  • 提名给候选人后,票的状态更改为“WatiForConfirm”
  • 为票生成提名历史记录

TrainingContext - 提名候选人

As 一名协调者
I want to 提名候选人参加培训
So that 部门的员工得到技能培训的机会

场景1:候选人获得提名
Given:从候选人名单中选择要提名的候选人
And: 选择要提名的培训票
When: 提名候选人
Then: 培训票被设置为WaitForConfirm状态
And: 该培训票不可再被提名
And: 候选人将收到培训提名的邮件通知

场景2:候选人参加过该课程
Given:从候选人名单中选择要提名的候选人
And: 该候选人已经参加过该培训要学习的课程
And: 选择要提名的培训票
When: 提名候选人
Then: 提示该候选人已经参加过该课程
And: 提名失败
And: 培训票仍然处于Available状态

时序图脚本为:

NominationAppService.nominate(nominationRequest) {
    NominationService.nominate(ticketId, candidate) {
        LearningService.beLearned(candidateId, trainingId) {
            TrainingRepository.trainingOf(trainingId);
            LearningRepository.isExist(candidateId, courseId);
        }
        TicketService.nominate(ticketId, candidate) {
            TicketRepository.ticketOf(ticketId);
            Ticket.nominate(candidate);
            TicketRepository.update(ticket);
        }
        NotificationService.notifyNominee(ticket, nominee) {
            MailTemplateRepository.templateOf(templateType);
            MailTemplate.compose(ticket, nominee);
            NotificationClient.notify(notificationRequest);
        }
    }
}

ProjectContext - 发送问题分配通知

当Issue被分配给Owner后,需要发送通知给该Issue的Owner和Reporter。任务为:

  • 通知报告人
    • 生成报告人通知
    • 发送通知
  • 通知经办人
    • 获取经办人信息
    • 生成经办人通知
    • 发送通知

伪代码:

        NotificationService.notifyOwner(issue, owner) {
            AssignmentNotification notification = AssignmentNotification.forOwner(issue, owner);
            NotificationClient.notify(NotificationRequest.from(notification));
        }
        NotificationService.notifyReporter(issue) {
            EmployeeResponse empResponse = EmployeeClient.employeeOf(reporterId);
            AssignmentNotification notification = AssignmentNotification.forReporter(issue, empResponse.toReporter());
            NotificationClient.notify(NotificationRequest.from(notification));
        }
``

AttendanceContext - 生成员工出勤记录

由领域服务AttendanceGenerator承担,其子任务包括:

  • 根据日期获取员工的打卡记录
  • 获取工作时间
  • 确定是否节假日
  • 获取员工请假信息
  • 确定出勤状态 (#8 )

伪代码为:

            AttendanceGenerator.generate(empId, day) {
                TimeCard timeCard = TimeCardRepository.timeCardOf(empId, day);
                Worktime worktime = Worktime.worktime();
                boolean isHoliday = HolidayRepository.isHoliday(day);
                Leave leave = LeaveRepository.leaveOf(empId, day);
                Attendance.assureStatus(timeCard, worktime, leave, isHoliday);
            }

TrainingContext - 发送提名通知

组合任务,由NotificationService领域服务履行职责。分解的任务为:

  • 发送提名通知
    • 获取通知邮件模板
    • 组装提名通知内容
    • 发送通知

“组装提名通知内容”原子任务由MailTemplate聚合履行,时序图脚本如下:

NotificationService.notifyNominee(ticket, nominee) {
    MailTemplateRepository.templateOf(templateType);
    MailTemplate.compose(ticket, nominee);
    NotificationClient.notify(notificationRequest);
}

提名邮件的类型为MeetingRequest,邮件模板内容为:

Hi $nomineeName$:
We are glad to notify that you are nominated by $nominatorName$ to attend the training. Please click the link as below to confirm this nomination before the deadline $deadline$:
$url$

Here is the basic information of training:
Title: $title$
Description: $description$
Begin time: $dbeginTime$
End time: $dendTime$
Place: $dplace$

Thanks! We're excited to have you in the training.
EAS Team

EmployeeContext - 办理员工入职

办理员工入职由EmployeeService领域服务承担,子任务为:

  • 根据身份证号或手机号确认该员工是否已经存在
  • 添加员工

伪代码:

    EmployeeService.onboarding(employee) {
        EmployeeRepository.isExist(idNumber, mobilePhone);
        EmployeeRepository.save(employee);
    }

ProjectContext - 分配问题给项目成员

作为一名项目管理成员
我希望将问题分配给指定项目成员
以便于确定问题的负责人进行进度跟踪

分解的任务为:

  • 分配问题给项目成员 (#12 )
    • 获得问题
    • 分配问题给经办人
    • 更新问题
    • 创建问题的变更记录
    • 通知报告人 (#13 )
      • 生成报告人通知
      • 发送通知
    • 通知经办人 (#13 )
      • 获取经办人信息
      • 生成经办人通知
      • 发送通知

领域场景的时序图伪代码为:

IssueAppService.assign(issueId, owner) {
    IssueService.assign(issueId, owner) {
        Issue issue = IssueRepository.issueOf(issueId);
        issue.assignTo(ownerId);
        IssueRepository.update(issue);
        ChangeHistoryRepository.add(changeHistory);
        NotificationService.notifyOwner(issue, owner) {
            AssignmentNotification notification = AssignmentNotification.forOwner(issue, owner);
            NotificationClient.notify(NotificationRequest.from(notification));
        }
        NotificationService.notifyReporter(issue) {
            EmployeeResponse empResponse = EmployeeClient.employeeOf(reporterId);
            AssignmentNotification notification = AssignmentNotification.forReporter(issue, empResponse.toReporter());
            NotificationClient.notify(NotificationRequest.from(notification));
        }
    }
}

EmployeeContext - 转换OnBoardingRequest为Employee领域对象

将应用服务接收的消息OnBoardingRequest转换为Employee领域对象,由OnboardingRequestAssembler装配器承担。子任务包括:

  • 创建Employee对象
  • 生成员工ID(#5 )
  • 装配Employee

伪代码为:

    OnboardingRequestAssembler.composeEmployee() {
        OnboardingRequest.toEmployee(employeeId);
        EmployeeIdGenerator.generate() {
            EmployeeRepository.latestEmployee();
            employee.idFrom(sequenceCode);
        }
    }

EmployeeContext - 生成员工号

生成员工号由领域服务EmployeeIdGenerator承担,它的子任务为:

  • 获取最近入职员工的顺序号
  • 按照规则生成员工号(#3 )

AttendanceContext - 生成员工工作时间

根据员工的打卡记录生成员工的工作时间(TimeCard)。需要考虑多种情况(这里规则做了一定程度简化):

  • 正常打卡,则员工每天会有两条打卡记录,以PunchedTime早的为上班时间,晚的为下班时间
  • 只有一次打卡,若PunchedTime为上午12点之前,记录为上班时间,下班时间缺失,PunchedTime为上午12点之后,记录为下班时间,上班时间缺失
  • 无打卡,则返回null

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.