# 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**,服务的拆分就完成了。 **这又是另一个想象力的空间。同样的,当在功能组件足够丰富的情况下,我们是不是就可以通过简单排列组合就完成了系统的搭建?**