• 应用分层


    DDD 代码架构

    大的层次上分为四层

    api 层

    app 层

    domain 层

    infra 层

    最简单的DDD架构

    至少需要包含如下的结构,将业务和流程分开,应用服务专注用例调度,反应用户故事;领域对象/服务专注核心业务。整个模块通用的放到基础设施层,资源库和外部服务实现也放到基础设施层,屏蔽实现细节。

    └─src
        ├─main
        │  ├─java
        │  │  └─com
        │  │      └─hand
        │  │          └─<module>
        │  │              ├─api
        │  │              │  ├─controller
        │  │              │  │   └─v1
        │  │              │  │      └─XxxController.java
        │  │              │  └─dto
        │  │              │      └─XxxDTO.java
        │  │              │
        │  │              ├─app
        │  │              │  └─service
        │  │              │      ├─XxxService.java
        │  │              │      └─impl
        │  │              │          └─XxxServiceImpl.java
        │  │              │
        │  │              ├─domain
        │  │              │  ├─entity
        │  │              │  │   └─Xxx.java
        │  │              │  └─repository
        │  │              │      └─XxxRepository.java
        │  │              │
        │  │              └─infra
        │  │                 ├─mapper
        │  │                 │   └─XxxMapper.java
        │  │                 └─repository
        │  │                     └─impl
        │  │                         └─XxxRepositoryImpl.java
        │  │
        │  └─resources
        │
        └─test
    
    

    开发步骤

    1.实体

    package org.hzero.platform.domain.entity;
    
    import ...;
    
    /**
     * 事件
     *
     * @author jiangzhou.bo@hand-china.com 2018/06/08 15:53
     */
    @ApiModel("事件")
    @VersionAudit
    @ModifyAudit
    @Table(name = "hpfm_event")
    public class Event extends AuditDomain {
    
        public static final String EVENT_ID = "eventId";
        public static final String EVENT_EVENT_CODE = "eventCode";
    
        /**
         * 比较两个事件的事件编码是否一致
         *
         * @param anotherEvent 事件
         * @throws CommonException 如果两个事件的编码不一致
         */
        public void equalsEventCode(Event anotherEvent) {
            if (StringUtils.isNotBlank(anotherEvent.getEventCode())
                            && !StringUtils.equals(this.eventCode, anotherEvent.getEventCode())) {
                throw new CommonException(BaseConstants.ErrorCode.DATA_INVALID);
            }
        }
    
        
    
        @Id
        @GeneratedValue
        private Long eventId;
        @Length(max = 30)
        @Pattern(regexp = "^[A-Za-z0-9]*$")
        @ApiModelProperty("事件编码")
        private String eventCode;
        @ApiModelProperty("是否启用")
        private Integer enabledFlag;
        @ApiModelProperty("事件描述")
        private String eventDescription;
    
        @Transient
        @ApiModelProperty("事件规则列表")
        private List<EventRule> ruleList;
    
        //
        // getter/setter
        // ------------------------------------------------------------------------------
    
        /**
         * @return 事件ID
         */
        public Long getEventId() {
            return eventId;
        }
    
        /**
         * @param eventId 事件ID
         */
        public void setEventId(Long eventId) {
            this.eventId = eventId;
        }
    
        /**
         * @return 事件编码
         */
        public String getEventCode() {
            return eventCode;
        }
    
        /**
         * @param eventCode 事件编码
         */
        public void setEventCode(String eventCode) {
            this.eventCode = eventCode;
        }
    
        /**
         *
         * @return 是否启用 <br/>
         *         <ul>
         *         <li>1 - 启用</li>
         *         <li>0 - 禁用</li>
         *         </ul>
         */
        public Integer getEnabledFlag() {
            return enabledFlag;
        }
    
        /**
         * @param enabledFlag 是否启用 <br/>
         *        <ul>
         *        <li>1 - 启用</li>
         *        <li>0 - 禁用</li>
         *        </ul>
         */
        public void setEnabledFlag(Integer enabledFlag) {
            this.enabledFlag = enabledFlag;
        }
    
        /**
         * @return 事件说明
         */
        public String getEventDescription() {
            return eventDescription;
        }
    
        /**
         * @param eventDescription 事件说明
         */
        public void setEventDescription(String eventDescription) {
            this.eventDescription = eventDescription;
        }
    
        @ApiIgnore
        public List<EventRule> getRuleList() {
            return ruleList;
        }
    
        public void setRuleList(List<EventRule> ruleList) {
            this.ruleList = ruleList;
        }
    }
    

    2.资源库

    package org.hzero.platform.domain.repository;
    
    import ...;
    
    /**
     * 事件资源库
     *
     * @author jiangzhou.bo@hand-china.com 2018/06/11 16:55
     */
    public interface EventRepository extends BaseRepository<Event> {
    
        /**
         * 分页查询
         */
        Page<Event> page(Event event, int page, int size);
    
        /**
         * 获取事件规则
         * 
         * @param eventId 事件ID
         * @return Event detail
         */
        Event get(Long eventId);
    
        /**
         * 删除事件
         * 
         * @param eventId 事件ID
         */
        void remove(Long eventId);
    
        /**
         * 导出数据
         */
        List<Event> export(Event event, ExportParam exportParam, PageRequest pageRequest);
    
        /**
         * 缓存key为事件编码,value为事件规则列表<br/>
         * 如果为启用状态,查询出事件规则,并刷新到缓存中<br/>
         * 如果为禁用状态,清除该事件缓存
         */
        void refreshCache(Event event);
    
        /**
         * 清除缓存
         *
         * @param redisHelper RedisHelper
         */
        void clearCache(Event event);
    }
    

    3.应用服务

    package org.hzero.platform.app.service;
    
    import ...;
    
    /**
     * 事件应用服务
     *
     * @author jiangzhou.bo@hand-china.com 2018/06/11 16:53
     */
    public interface EventService {
    
        /**
         * 创建事件
         * 
         * @param event 事件
         * @return
         */
        Event create(Event event);
    
        /**
         * 更新事件
         *
         * @param event 事件
         * @return
         */
        Event update(Event event);
    
        /**
         * 删除事件
         *
         * @param eventId 事件ID not null.
         */
        void remove(Long eventId);
    
        /**
         * 批量删除事件
         * 
         * @param eventIds 事件ID
         */
        void batchRemove(Long[] eventIds);
    
        /**
         * 创建事件规则
         * 
         * @param eventId 事件ID
         * @param eventRule 事件规则
         * @return 事件规则
         */
        EventRule createEventRule(Long eventId, EventRule eventRule);
    
        /**
         * 修改事件规则
         *
         * @param eventId 事件ID
         * @param eventRule 事件规则
         * @return 事件规则
         */
        EventRule updateEventRule(Long eventId, EventRule eventRule);
    
        /**
         * 批量删除事件规则
         * 
         * @param eventId 事件ID
         * @param eventRuleIds 事件规则ID集合
         */
        void batchRemoveEventRule(Long eventId, Long[] eventRuleIds);
    
    }
    

    4.用户接口

    package org.hzero.platform.api.controller.v1;
    
    import ...;
    
    /**
     * 事件管理 API
     *
     * @author jiangzhou.bo@hand-china.com 2018/06/11 16:29
     */
    @Api(tags = {SwaggerApiConfig.EVENT})
    @RestController("eventController.v1")
    @RequestMapping("/v1/{organizationId}/events")
    public class EventController extends BaseController {
    
        @Autowired
        private EventRepository eventRepository;
        @Autowired
        private EventRuleRepository eventRuleRepository;
        @Autowired
        private EventService eventService;
    
        @ApiOperation(value = "查询事件列表")
        @ApiImplicitParams({
            @ApiImplicitParam(name = "eventCode", value = "事件编码", paramType = "query"),
            @ApiImplicitParam(name = "eventDescription", value = "事件描述", paramType = "query")
        })
        @Permission(level = ResourceLevel.ORGANIZATION)
        @GetMapping
        public ResponseEntity<Page<Event> list(String eventCode, String eventDescription, PageRequest pageRequest) {
            Event event = new Event();
            event.setEventCode(eventCode);
            event.setEventDescription(eventDescription);
            return Results.success(eventRepository.page(event, pageRequest.getPage(), pageRequest.getSize()));
        }
    
        @ApiOperation(value = "事件及规则列表")
        @ApiImplicitParams({@ApiImplicitParam(name = "eventId", value = "事件ID", paramType = "query"),})
        @Permission(level = ResourceLevel.ORGANIZATION)
        @GetMapping("/{eventId}")
        public ResponseEntity<Event> listEventRule(@PathVariable Long eventId) {
            Event event = eventRepository.get(eventId);
            return Results.success(event);
        }
    
        @ApiOperation(value = "创建事件")
        @Permission(level = ResourceLevel.ORGANIZATION)
        @PostMapping
        public ResponseEntity<Event> create(@RequestBody Event event) {
            return Results.success(eventService.create(event));
        }
    
        @ApiOperation(value = "修改事件")
        @Permission(level = ResourceLevel.ORGANIZATION)
        @PutMapping
        public ResponseEntity<Event> update(@RequestBody Event event) {
            SecurityTokenHelper.validToken(event, false);
            return Results.success(eventService.update(event));
        }
    
        @ApiOperation(value = "批量删除事件")
        @ApiImplicitParams({@ApiImplicitParam(name = "eventId", value = "事件ID集合", paramType = "query"),})
        @Permission(level = ResourceLevel.ORGANIZATION)
        @DeleteMapping
        public ResponseEntity batchRemove(@RequestBody List<Event> events) {
            SecurityTokenHelper.validToken(events, false);
            Long[] eventIds = events.stream().map(Event::getEventId).collect(Collectors.toList())
                            .toArray(ArrayUtils.EMPTY_LONG_OBJECT_ARRAY);
            eventService.batchRemove(eventIds);
            return Results.success();
        }
    
        @ApiOperation(value = "查询事件规则")
        @ApiImplicitParams({@ApiImplicitParam(name = "eventId", value = "事件ID", paramType = "query"),
                @ApiImplicitParam(name = "eventRuleId", value = "事件规则ID", paramType = "query"),})
        @Permission(level = ResourceLevel.ORGANIZATION)
        @GetMapping("/{eventId}/rules/{eventRuleId}")
        public ResponseEntity<EventRule> getEventRule(@PathVariable Long eventId, @PathVariable Long eventRuleId) {
            EventRule eventRule = new EventRule();
            eventRule.setEventId(eventId);
            eventRule.setEventRuleId(eventRuleId);
            return Results.success(eventRuleRepository.get(eventRule));
        }
    
        @ApiOperation(value = "创建事件规则")
        @Permission(level = ResourceLevel.ORGANIZATION)
        @PostMapping("/{eventId}/rules")
        public ResponseEntity<EventRule> createEventRule(@PathVariable Long eventId, @RequestBody EventRule eventRule) {
            return Results.success(eventService.createEventRule(eventId, eventRule));
        }
    
        @ApiOperation(value = "修改事件规则")
        @Permission(level = ResourceLevel.ORGANIZATION)
        @PutMapping("/{eventId}/rules")
        public ResponseEntity<EventRule> updateEventRule(@PathVariable Long eventId, @RequestBody EventRule eventRule) {
            SecurityTokenHelper.validToken(eventRule);
            return Results.success(eventService.updateEventRule(eventId, eventRule));
        }
    
        @ApiOperation(value = "批量删除事件规则")
        @Permission(level = ResourceLevel.ORGANIZATION)
        @DeleteMapping("/{eventId}/rules")
        public ResponseEntity batchRemoveEventRule(@PathVariable Long eventId, @RequestBody List<EventRule> eventRules) {
            SecurityTokenHelper.validToken(eventRules);
            eventService.batchRemoveEventRule(eventId, eventRules);
            return Results.success();
        }
    
        @ApiOperation(value = "导出事件规则")
        @Permission(level = ResourceLevel.ORGANIZATION)
        @GetMapping("/export")
        @ExcelExport(Event.class)
        public ResponseEntity export(Event event, ExportParam exportParam, HttpServletResponse response,
                        PageRequest pageRequest) {
            return Results.success(eventRepository.export(event, exportParam, pageRequest));
        }
    
    }
    

    5.Mapper

    package org.hzero.platform.infra.mapper;
    
    import ...;
    
    /**
     * 事件Mapper
     *
     * @author jiangzhou.bo@hand-china.com 2018/06/11 17:01
     */
    public interface EventMapper extends BaseMapper<Event> {
    
        /**
         * 查询事件列表
         * 
         * @param event Event
         * @return List<Event>
         */
        List<Event> selectEvent(Event event);
    
    }
    

    6.资源库实现

    package org.hzero.platform.infra.repository.impl;
    
    import ...;
    
    /**
     * 事件 资源库实现
     *
     * @author jiangzhou.bo@hand-china.com 2018/06/11 21:11
     */
    @Component
    public class EventRepositoryImpl extends BaseRepositoryImpl<Event> implements EventRepository  {
    
        @Autowired
        private EventRuleRepository eventRuleRepository;
        @Autowired
        private EventMapper eventMapper;
        @Autowired
        private EventRuleMapper eventRuleMapper;
        @Autowired
        private RedisHelper redisHelper;
        @Autowired
        private ObjectMapper objectMapper;
    
        @Override
        public Page<Event> page(Event event, int page, int size) {
            return PageHelper.doPage(page, size, () -> eventMapper.selectEvent(event));
        }
    
        @Override
        public Event get(Long eventId) {
            Event dto = new Event();
            dto.setEventId(eventId);
            List<Event> events = eventMapper.selectEvent(dto);
            if (CollectionUtils.isNotEmpty(events)) {
                dto = events.get(0);
                EventRule eventRule = new EventRule();
                eventRule.setEventId(eventId);
                dto.setRuleList(eventRuleMapper.selectEventRule(eventRule));
                return dto;
            }
            return null;
        }
    
        @Override
        public void remove(Long eventId) {
            // 删除事件规则
            eventRuleRepository.removeByEventId(eventId);
            // 删除事件
            this.deleteByPrimaryKey(eventId);
        }
    
        @Override
        public List<Event> export(Event event, ExportParam exportParam, PageRequest pageRequest) {
            List<Event> list = PageHelper.doPageAndSort(pageRequest, () -> eventMapper.selectEvent(event));
            if (exportParam.getSelection().contains(Event.EVENT_EVENT_RULE_LIST)) {
                EventRule eventRule = new EventRule();
                for (Event dto : list) {
                    eventRule.setEventId(dto.getEventId());
                    dto.setRuleList(eventRuleMapper.selectEventRule(eventRule));
                }
            }
            return list;
        }
    
         @Override
        public void refreshCache(Event event) {
            clearCache(event);
            if (BaseConstants.Flag.YES.equals(event.getEnabledFlag())) {
                List<EventRule> ruleList = eventRuleRepository.select(Event.EVENT_ID, event.getEventId());
                if (CollectionUtils.isEmpty(ruleList)) {
                    return;
                }
                List<EventRuleVO> voList = createEventRuleVO(ruleList);
                voList.forEach(vo -> {
                    try {
                        redisHelper.lstRightPush(FndConstants.CacheKey.EVENT_KEY + ":" + event.getEventCode(), 
                            objectMapper.writeValueAsString(vo));
                    } catch (JsonProcessingException e) {
                        e.printStackTrace();
                    }
                });
            }
        }
    
        @Override
        public void clearCache(Event event) {
            String key = FndConstants.CacheKey.EVENT_KEY + ":" + event.getEventCode();
            redisHelper.delKey(key);
        }
    }
    

    7.应用服务实现

    package org.hzero.platform.app.service.impl;
    
    import ...;
    
    /**
     * 事件应用服务默认实现
     *
     * @author jiangzhou.bo@hand-china.com 2018/06/11 16:54
     */
    @SuppressWarnings("rawtypes")
    @Service
    public class EventServiceImpl implements EventService {
    
        @Autowired
        private EventRepository eventRepository;
        @Autowired
        private EventRuleRepository eventRuleRepository;
        @Autowired
        private RedisHelper redisHelper;
        @Autowired
        private ObjectMapper objectMapper;
    
        @Override
        @Transactional(rollbackFor = Exception.class)
        public Event create(Event event) {
            eventRepository.insertSelective(event);
            eventRepository.refreshCache(event);
            return event;
        }
    
        @Override
        @Transactional(rollbackFor = Exception.class)
        public Event update(Event event) {
            Assert.notNull(event.getEventId(), BaseConstants.ErrorCode.DATA_INVALID);
            Event entity = eventRepository.selectByPrimaryKey(event.getEventId());
            Assert.notNull(entity, BaseConstants.ErrorCode.DATA_NOT_EXISTS);
            entity.equalsEventCode(event);
            eventRepository.updateByPrimaryKeySelective(event);
            eventRepository.refreshCache(event);
            return event;
        }
    
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void remove(Long eventId) {
            Event event = eventRepository.selectByPrimaryKey(eventId);
            Assert.notNull(event, BaseConstants.ErrorCode.DATA_NOT_EXISTS);
            // 删除事件
            eventRepository.remove(eventId);
            // 清除缓存
            eventRepository.clearCache(event);
        }
    
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void batchRemove(Long[] eventIds) {
            if (ArrayUtils.isNotEmpty(eventIds)) {
                for (int i = 0; i < eventIds.length; i++) {
                    remove(eventIds[i]);
                }
            }
        }
    
        @Override
        @Transactional(rollbackFor = Exception.class)
        public EventRule createEventRule(Long eventId, EventRule eventRule) {
            Event event = eventRepository.selectByPrimaryKey(eventId);
            Assert.notNull(event, BaseConstants.ErrorCode.DATA_NOT_EXISTS);
            eventRule.setEventId(eventId);
            eventRuleRepository.insertSelective(eventRule);
            eventRepository.refreshCache(event);
            return eventRule;
        }
    
        @Override
        @Transactional(rollbackFor = Exception.class)
        public EventRule updateEventRule(Long eventId, EventRule eventRule) {
            Event event = eventRepository.selectByPrimaryKey(eventId);
            Assert.notNull(event, BaseConstants.ErrorCode.DATA_NOT_EXISTS);
            eventRule.setEventId(eventId);
            eventRuleRepository.updateByPrimaryKeySelective(eventRule);
            eventRepository.refreshCache(event);
            return eventRule;
        }
    
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void removeEventRule(Long eventId, Long eventRuleId) {
            Event event = eventRepository.selectByPrimaryKey(eventId);
            Assert.notNull(event, BaseConstants.ErrorCode.DATA_NOT_EXISTS);
            eventRuleRepository.deleteByPrimaryKey(eventRuleId);
            eventRepository.refreshCache(event);
        }
    
        @Override
        @Transactional(rollbackFor = Exception.class)
        public void batchRemoveEventRule(Long eventId, Long[] eventRuleIds) {
            Event event = eventRepository.selectByPrimaryKey(eventId);
            Assert.notNull(event, BaseConstants.ErrorCode.DATA_NOT_EXISTS);
            // 校验事件ID与事件规则行中事件ID是否一致
            eventRules.forEach(eventRule -> {
                Assert.isTrue(eventRule.getEventId() == eventId, HpfmMsgCodeConstants.ERROR_EVENT_NOT_MATCH);
            });
            eventRuleRepository.batchDelete(eventRules);
            eventRepository.refreshCache(event);
        }
    
    }
    


    三层架构

    DDD架构