# archetype
**Repository Path**: JayEinstein/archetype
## Basic Information
- **Project Name**: archetype
- **Description**: 一个支持快速迭代开发、防腐烂、且可单体和微服务之间动态切换的落地代码项目架构方案。
- **Primary Language**: Java
- **License**: MulanPSL-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 4
- **Forks**: 0
- **Created**: 2022-07-17
- **Last Updated**: 2025-01-08
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
### 简介
从对`Spring`源码的思考,以及吸收了`DDD`的部分思想,再接借鉴了一下`alibaba COLA`架构和`Vue`组件的开发模式,最后再结合一下自己从业中遇到的问题和经验,从而总结出来的一个支持快速迭代开发、防腐烂、且可**单体**和**微服务**之间动态切换的落地代码项目架构方案。
> 具体服务的动态拆分参考:[https://gitee.com/JayEinstein/archetype-mvc](https://gitee.com/JayEinstein/archetype-mvc)
### 解决的问题
面对变化多端、既要又要且还要的需求,传统中的mvc三层架构(就算是三十层!)也明显是招架不住的,需求的不确定性以及随时可能面临的修改,再加了历史沉淀的堆砌,代码的下场往往都是庞大又臃肿,羞涩又难懂,让我对这行是又爱又恨。
面对如此变化多端的需求,就没有办法改变现状了吗?我坚信着,只有用魔法才能打败魔法。所以说,有没有这么一种可能,如果代码本身就比需求还要灵活,那么要解决这个需求还不是分分钟的事?
### 理论基础
我最近了解了一下DDD(Domain-driven design)领域驱动设计,但它只是一个理论框架,没有具体可参考的落地实战方案,如果说非要我挑一个它最大的缺点,那么我的答案就是它的概念太过庞大,传播和学习的成本过高。
我特别喜欢三体里的叶文洁和罗辑的一段对话
> ……
> “我倒是有个建议:你为什么不去研究宇宙社会学呢?”
> “但,叶老师,您说的宇宙社会学没有任何可供研究的实际资料,也不太可能进行调查和实验。”
> “所以你最后的成果就是纯理论的,就像欧氏几何一样,先设定几条简单的不证自明的公理,再在这些公理的基础上推导出整个理论体系。”
> “叶老师,这......真是太有意思了,可是宇宙社会学的公理是什么呢?”
> "第一,生存是文明的第一需要;第二,文明不断增长和扩张,但宇宙中的物质总量保持不变。”
> ……
受到了这个启发,所以在这里,我也要进行设定了……
```
1. 假设一个需要外力才能改变驱动的对象,称之为“因子(Factor)”;驱动“因子”的外力,称之为“行为(behavior)”。
2. “行为”可以使“因子”发生改变、使别的“行为”启动,“因子”的改变也会触发上述效果。
3. 同一环境下,“行为”执行同一策略所得的结果要一致。
```
基于这几条设定,对于一个软件系统来说,“行为”的主体我这里只考虑人的因素,以商城系统来举例,从用户的角度,“因子”对应的就是产品、订单,“行为”对应的就是下单、付款。如果执行相同的“下单行为”,却产生了不一样的结果,那么就说明了同一行为下执行了不一样的策略。
把这一现象映射到人的身上,那么就是一个人的想法时刻都可能会发生变化,而需求的变化性正是来源于人的想法上的层出不穷,这也可以用来解释了为什么需求总是变化的。想法的快速变化和代码的固定性上,就存在着一个根本的矛盾。
所以现在再次回到那个问题,有没有这么一种可能,代码本身就比需求还要灵活?
### 架构设计
#### 一、基本概念
##### 1. 因子(Factor)
Factor的概念借鉴于DDD的充血模型,Factor本身不做任何的持久操作,但是每一个Factor持有一个FactorRepository,FactorRepository是啥?
##### 2. 因子资源库(FactorRepository)
FactorRepository是一个因子操作的持久层的接口,Factor在实例化的时候必须设置一个具体实现的FactorRepository,当实现类是Msql时,因子操作Mysql持久层,当实现类是RPC时,因子则调用了别的微服务接口。**FactorRepository是实现单体和微服务动态拆分的关键。**
##### 3. 行为(Behavior)
所有的因子只能由Behavior操作。
##### 4. 因子观测器(FactorObserver)
当因子发生变动时执行对应的FactorObserver。FactorObserver在FactorFactory构造因子时加入监听。
##### 5. 因子工厂(FactorFactory)
Behavior的因子由FactorFactory获取,为了保证因子功能的完整性,因子的实例化过程由FactorFactory统一创建。
FactorFactory持有FactorRepository、FactorObserver,创建步骤为:实例化因子 -> 因子属性填充 -> 因子初始化。
主要功能:1. 获得因子。2. 创建、修改、删除因子。3. 获得因子回响。
**回响概念**:有时候我们并不要获得因子,只需要根据因子的部分信息对因子进行操作的行为,称之为“回响”。
如:根据订单编码修改订单状态,订单作为一个大对象,此时我们并不需要获取整个订单因子信息。
##### 6. 因子描述(FactorDescriber)
提交于FactorFactory,进行创建和修改因子。
##### 其它
- PersistentFactor 拥有完整的因子信息和功能
- EchoFactor 回响型因子,根据部分的因子信息对因子进行操作
- FactorEcho 因子回响
- FactorHandle 因子手柄
- FactorSource 因子资源
一整个Behavior执行下来,当一个因子触发了另一个因子发生变化的时候,这一过程有点像核裂变,于是我贴心的为它起了一个名字…… ~~二狗架构(bushi)~~ 因子裂变架构。
#### 二、优雅的加入SpringMVC
总得来说,mvc虽然有它的缺点,但奈何它开发速度就是快,长处也是特别明显,小孩子才做选择,大人全都要。
以商城项目为例,
##### 注册一个“下单行为”
下单PaymentBehavior需要用到产品(Product)、订单(Order)、会员(Member)因子。
而构建一个因子需要用到FactorFactory,则在Spring中分别进行注册。
1. 注册FactorFactory,并填充因子的FactorRepository
```java
/**
* 组件内因子工厂注册
* @author JayEinstein
*/
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class FactorFactoryRegister {
@Bean
public OrderFactory orderBuilder(OrderRepository orderRepository) {
// 注册订单工厂
return new OrderFactory(orderRepository);
}
@Bean
public ProductFactory productBuilder(ProductRepository productRepository) {
// 注册产品工厂
return new ProductFactory(productRepository);
}
@Bean
public MemberFactory memberBuilder(MemberRepository memberRepository) {
// 注册会员工厂
return new MemberFactory(memberRepository);
}
}
```
2. 注册行为,把上面的FactorFactory注入
```java
/**
* 行为注册
* @author JayEinstein
*/
@Configuration
public class BehaviorRegister {
/**
* 注册行为
* @param productFactory 产品工厂
* @param orderFactory 订单工厂
* @param memberFactory 会员工厂
* @return
*/
@Bean
public PaymentBehavior paymentBehavior(
ProductFactory productFactory,
OrderFactory orderFactory,
MemberFactory memberFactory
) {
return new PaymentBehavior(productFactory, orderFactory, memberFactory);
}
}
```
3. Behavior放入Controller
```java
@RestController
@RequestMapping("/shop/order")
public class OrderController {
@Autowired
private PaymentBehavior paymentBehavior;
/**
* 创建订单
* @param createOrder
* @return
*/
@PostMapping("/create")
public Object createOrder(@RequestBody CreateOrderDto createOrder) {
Order order = paymentBehavior.createOrder(createOrder);
return order;
}
}
```
##### 加入一个需求
当一个产品库存为0时,通知运营进行补货。
1. 实现一个FactorObserver
```java
/**
* 产品售罄通知
* @author JayEinstein
*/
@Component
@Slf4j
public class OutOfStock implements OutOfStockObserver {
@Override
public void receive(Product factor) {
log.info("产品售罄通知:{} 已售罄", factor.getName());
}
}
```
2. 在FactorFactory中加入监听
```java
/**
* 组件内因子工厂注册
* @author JayEinstein
*/
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class FactorFactoryRegister {
@Bean
public OrderFactory orderBuilder(OrderRepository orderRepository) {
// 注册订单工厂
return new OrderFactory(orderRepository);
}
@Bean
public ProductFactory productBuilder(ProductRepository productRepository,
List> observers) {
// 注册产品工厂
ProductFactory productFactory = new ProductFactory(productRepository);
// 在factory配置监听器
productFactory.addObservers(observers);
return productFactory;
}
@Bean
public MemberFactory memberBuilder(MemberRepository memberRepository) {
// 注册会员工厂
return new MemberFactory(memberRepository);
}
}
```
**这就是想象力的空间,当在行为和因子足够丰富的情况下,是不是就可以是在开闭的原则下实现指哪打哪的快速开发?**
#### 三、单体到微服务的动态演变
> 更详细的服务动态拆分参考:[https://gitee.com/JayEinstein/archetype-mvc](https://gitee.com/JayEinstein/archetype-mvc)
这里借用了Vue component 模式,涉及到了几个模块的概念,简单的来说是用到了`component`、`FactorRepository`、`start`模块。
component是功能的实现,依赖FactorRepository进行持久操作,start本身没有任何东西,是component和FactorRepository的集合。
如果start拥有了所有的component和FactorRepository那它成了一个单体的服务,通过这个模式,我们就可以巧妙的把*微服务怎么划分*的问题转化成了*微服务怎么组装*的问题。
回归项目中,`start-all`是所有的component集合,是一个单体,如果我们要把行为中product因子操作使用RPC模式,我们只需要把`start-all`的pom中从
```xml
com.shop.factor.repo
product-mysql
```
改为
```xml
com.shop.factor.repo
product-rpc
```
然后再搭建一个`start-product`服务。
修改**start/start-all**的pom,启动**start/start-product**,服务的拆分就完成了。
**这又是另一个想象力的空间。同样的,当在功能组件足够丰富的情况下,我们是不是就可以通过简单排列组合就完成了系统的搭建?**