从对Spring
源码的思考,以及吸收了DDD
的部分思想,再接借鉴了一下alibaba COLA
架构和Vue
组件的开发模式,最后再结合一下自己从业中遇到的问题和经验,从而总结出来的一个支持快速迭代开发、防腐烂、且可单体和微服务之间动态切换的落地代码项目架构方案。
具体服务的动态拆分参考:https://gitee.com/JayEinstein/archetype-mvc
面对变化多端、既要又要且还要的需求,传统中的mvc三层架构(就算是三十层!)也明显是招架不住的,需求的不确定性以及随时可能面临的修改,再加了历史沉淀的堆砌,代码的下场往往都是庞大又臃肿,羞涩又难懂,让我对这行是又爱又恨。
面对如此变化多端的需求,就没有办法改变现状了吗?我坚信着,只有用魔法才能打败魔法。所以说,有没有这么一种可能,如果代码本身就比需求还要灵活,那么要解决这个需求还不是分分钟的事?
我最近了解了一下DDD(Domain-driven design)领域驱动设计,但它只是一个理论框架,没有具体可参考的落地实战方案,如果说非要我挑一个它最大的缺点,那么我的答案就是它的概念太过庞大,传播和学习的成本过高。
我特别喜欢三体里的叶文洁和罗辑的一段对话
……
“我倒是有个建议:你为什么不去研究宇宙社会学呢?”
“但,叶老师,您说的宇宙社会学没有任何可供研究的实际资料,也不太可能进行调查和实验。”
“所以你最后的成果就是纯理论的,就像欧氏几何一样,先设定几条简单的不证自明的公理,再在这些公理的基础上推导出整个理论体系。”
“叶老师,这......真是太有意思了,可是宇宙社会学的公理是什么呢?”
"第一,生存是文明的第一需要;第二,文明不断增长和扩张,但宇宙中的物质总量保持不变。”
……
受到了这个启发,所以在这里,我也要进行设定了……
1. 假设一个需要外力才能改变驱动的对象,称之为“因子(Factor)”;驱动“因子”的外力,称之为“行为(behavior)”。
2. “行为”可以使“因子”发生改变、使别的“行为”启动,“因子”的改变也会触发上述效果。
3. 同一环境下,“行为”执行同一策略所得的结果要一致。
基于这几条设定,对于一个软件系统来说,“行为”的主体我这里只考虑人的因素,以商城系统来举例,从用户的角度,“因子”对应的就是产品、订单,“行为”对应的就是下单、付款。如果执行相同的“下单行为”,却产生了不一样的结果,那么就说明了同一行为下执行了不一样的策略。 把这一现象映射到人的身上,那么就是一个人的想法时刻都可能会发生变化,而需求的变化性正是来源于人的想法上的层出不穷,这也可以用来解释了为什么需求总是变化的。想法的快速变化和代码的固定性上,就存在着一个根本的矛盾。
所以现在再次回到那个问题,有没有这么一种可能,代码本身就比需求还要灵活?
Factor的概念借鉴于DDD的充血模型,Factor本身不做任何的持久操作,但是每一个Factor持有一个FactorRepository,FactorRepository是啥?
FactorRepository是一个因子操作的持久层的接口,Factor在实例化的时候必须设置一个具体实现的FactorRepository,当实现类是Msql时,因子操作Mysql持久层,当实现类是RPC时,因子则调用了别的微服务接口。FactorRepository是实现单体和微服务动态拆分的关键。
所有的因子只能由Behavior操作。
当因子发生变动时执行对应的FactorObserver。FactorObserver在FactorFactory构造因子时加入监听。
Behavior的因子由FactorFactory获取,为了保证因子功能的完整性,因子的实例化过程由FactorFactory统一创建。 FactorFactory持有FactorRepository、FactorObserver,创建步骤为:实例化因子 -> 因子属性填充 -> 因子初始化。
主要功能:1. 获得因子。2. 创建、修改、删除因子。3. 获得因子回响。
回响概念:有时候我们并不要获得因子,只需要根据因子的部分信息对因子进行操作的行为,称之为“回响”。 如:根据订单编码修改订单状态,订单作为一个大对象,此时我们并不需要获取整个订单因子信息。
提交于FactorFactory,进行创建和修改因子。
一整个Behavior执行下来,当一个因子触发了另一个因子发生变化的时候,这一过程有点像核裂变,于是我贴心的为它起了一个名字…… 二狗架构(bushi) 因子裂变架构。
总得来说,mvc虽然有它的缺点,但奈何它开发速度就是快,长处也是特别明显,小孩子才做选择,大人全都要。
以商城项目为例,
下单PaymentBehavior需要用到产品(Product)、订单(Order)、会员(Member)因子。 而构建一个因子需要用到FactorFactory,则在Spring中分别进行注册。
/**
* 组件内因子工厂注册
* @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);
}
}
/**
* 行为注册
* @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);
}
}
@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时,通知运营进行补货。
/**
* 产品售罄通知
* @author JayEinstein
*/
@Component
@Slf4j
public class OutOfStock implements OutOfStockObserver {
@Override
public void receive(Product factor) {
log.info("产品售罄通知:{} 已售罄", factor.getName());
}
}
/**
* 组件内因子工厂注册
* @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<FactorObserver<Product>> 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
这里借用了Vue component 模式,涉及到了几个模块的概念,简单的来说是用到了component
、FactorRepository
、start
模块。
component是功能的实现,依赖FactorRepository进行持久操作,start本身没有任何东西,是component和FactorRepository的集合。
如果start拥有了所有的component和FactorRepository那它成了一个单体的服务,通过这个模式,我们就可以巧妙的把微服务怎么划分的问题转化成了微服务怎么组装的问题。
回归项目中,start-all
是所有的component集合,是一个单体,如果我们要把行为中product因子操作使用RPC模式,我们只需要把start-all
的pom中从
<dependency>
<groupId>com.shop.factor.repo</groupId>
<artifactId>product-mysql</artifactId>
</dependency>
改为
<dependency>
<groupId>com.shop.factor.repo</groupId>
<artifactId>product-rpc</artifactId>
</dependency>
然后再搭建一个start-product
服务。
修改start/start-all的pom,启动start/start-product,服务的拆分就完成了。
这又是另一个想象力的空间。同样的,当在功能组件足够丰富的情况下,我们是不是就可以通过简单排列组合就完成了系统的搭建?
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。