# Lottery
基于Springboot,Dubbo 等开发的分布式抽奖系统
## 1. 环境 配置 规范
## 2. 搭建(DDD + RPC)架构
DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。整个过程大概是这样的,开发团队和领域专家一起通过 通用语言(Ubiquitous Language)去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。
依靠领域驱动设计的设计思想,通过事件风暴建立领域模型,合理划分领域逻辑和物理边界,建立领域对象及服务矩阵和服务架构图,定义符合DDD分层架构思想的代码结构模型,保证业务模型与代码模型的一致性。通过上述设计思想、方法和过程,指导团队按照DDD设计思想完成微服务设计和开发。
- 拒绝泥球小单体、拒绝污染功能与服务、拒绝一加功能排期一个月
- 架构出高可用极易符合互联网高速迭代的应用服务
- 物料化、组装化、可编排的服务,提高人效
<img src="README.assets/image-20230314214310765-0102068.png" alt="image-20230314214310765" style="zoom: 33%;" />
- **应用层{application}**
- 应用服务位于应用层。用来表述应用和用户行为,负责服务的组合、编排和转发,负责处理业务用例的执行顺序以及结果的拼装。
- 应用层的服务包括应用服务和领域事件相关服务。
- 应用服务可对微服务内的领域服务以及微服务外的应用服务进行组合和编排,或者对基础层如文件、缓存等数据直接操作形成应用服务,对外提供粗粒度的服务。
- 领域事件服务包括两类:领域事件的发布和订阅。通过事件总线和消息队列实现异步数据传输,实现微服务之间的解耦。
- **领域层{domain}**
- 领域服务位于领域层,为完成领域中跨实体或值对象的操作转换而封装的服务,领域服务以与实体和值对象相同的方式参与实施过程。
- 领域服务对同一个实体的一个或多个方法进行组合和封装,或对多个不同实体的操作进行组合或编排,对外暴露成领域服务。领域服务封装了核心的业务逻辑。实体自身的行为在实体类内部实现,向上封装成领域服务暴露。
- 为隐藏领域层的业务逻辑实现,所有领域方法和服务等均须通过领域服务对外暴露。
- 为实现微服务内聚合之间的解耦,原则上禁止跨聚合的领域服务调用和跨聚合的数据相互关联。
- **基础层{infrastructure}**
- 基础服务位于基础层。为各层提供资源服务(如数据库、缓存等),实现各层的解耦,降低外部资源变化对业务逻辑的影响。
- 基础服务主要为仓储服务,通过依赖反转的方式为各层提供基础资源服务,领域服务和应用服务调用仓储服务接口,利用仓储实现持久化数据对象或直接访问基础资源。
- **接口层{interfaces}**
- 接口服务位于用户接口层,用于处理用户发送的Restful请求和解析用户输入的配置文件等,并将信息传递给应用层。
**DDD是在MVC的基础上可以更加明确了房间的布局**
DDD结构它是一种充血模型结构,所有的服务实现都以领域为核心,应用层定义接口,领域层实现接口,领域层定义数据仓储,基础层实现数据仓储中关于DAO和Redis的操作,但同时几方又有互相的依赖。那么这样的结构再开发独立领域提供 http 接口时候,并不会有什么问题体现出来。但如果这个时候需要引入 RPC 框架,就会暴露问题了,因为使用 RPC 框架的时候,需要对外提供描述接口信息的 Jar 让外部调用方引入才可以通过反射调用到具体的方法提供者,那么这个时候,RPC 需要暴露出来,而 DDD 的系统结构又比较耦合,怎么进行模块化的分离就成了问题点。所以我们本章节在模块系统结构搭建的时候,也是以解决此项问题为核心进行处理的。
**DDD + RPC,模块分离系统搭建**
<img src="README.assets/image-20230314214553484-0102068.png" alt="image-20230314214553484" style="zoom:50%;" />
如果按照模块化拆分,那么会需要做一些处理,包括:
1. 应用层,不再给领域层定义接口,而是自行处理对领域层接口的包装。否则领域层既引入了应用层的Jar,应用层也引入了领域层的Jar,就会出现循环依赖的问题。
2. 基础层中的数据仓储的定义也需要从领域层剥离,否则也会出现循环依赖的问题。
3. RPC 层定义接口描述,包括:入参Req、出参Res、DTO对象,接口信息,这些内容定义出来的Jar给接口层使用,也给外部调用方使用。
<img src="README.assets/image-20230314214617625-0102068.png" alt="image-20230314214617625" style="zoom:50%;" />
## 3. 跑通广播模式RPC过程调用
### 一、创建抽奖活动表
抽奖活动的设计和开发过程中,涉及到的表信息包括:活动表、奖品表、策略表、规则表、用户参与表、中奖信息表等。
首先创建一个活动表,用于实现系统对数据库的CRUD操作,也就可以被RPC接口调用,在后续再进行优化。
**活动表(activity)**
```sql
CREATE TABLE `activity` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`activity_id` bigint(20) NOT NULL COMMENT '活动ID',
`activity_name` varchar(64) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '活动名称',
`activity_desc` varchar(128) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '活动描述',
`begin_date_time` datetime DEFAULT NULL COMMENT '开始时间',
`end_date_time` datetime DEFAULT NULL COMMENT '结束时间',
`stock_count` int(11) DEFAULT NULL COMMENT '库存',
`take_count` int(11) DEFAULT NULL COMMENT '每人可参与次数',
`state` tinyint(2) DEFAULT NULL COMMENT '活动状态:1编辑、2提审、3撤审、4通过、5运行(审核通过后worker扫描状态)、6拒绝、7关闭、8开启',
`creator` varchar(64) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `unique_activity_id` (`activity_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='活动配置';
```
活动表:是一个用于配置抽奖活动的总表,用于存放活动信息,包括:ID、名称、描述、时间、库存、参与次数等。
### 二、POM 文件配置
按照现有工程的结构模块分层,包括:
- lottery-application,应用层,引用:`domain`
- lottery-common,通用包,引用:`无`
- lottery-domain,领域层,引用:`infrastructure`
- lottery-infrastructure,基础层,引用:`无`
- lottery-interfaces,接口层,引用:`application`、`rpc`
- lottery-rpc,RPC接口定义层,引用:`common`
**lottery-rpc配置**
```xml
<parent>
<artifactId>Lottery</artifactId>
<groupId>com.banana69.lottery</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>lottery-rpc</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>cn.itedus.lottery</groupId>
<artifactId>lottery-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<finalName>lottery-rpc</finalName>
<plugins>
<!-- 编译plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupI