In " vivo marketing automation technology decryption | opening ", we introduced the business architecture, core business module functions, system architecture and several core technology designs of the vivo marketing automation platform as a whole.
This time is the second article of a series of articles. This article analyzes in detail how design patterns and related applications help marketing automation business improve system scalability, as well as the thinking and summary in the process of practice.
I. Introduction
The marketing business itself is extremely complex and changeable. Especially with the vigorous development of digital marketing, different marketing strategy decisions will be generated in different periods of the market, different stages of company development, facing different user groups, and continuous effect fluctuations. .
When faced with changing business scenarios at any time, the scalability of the system is very important. When talking about the scalability of system design, the design principles and design patterns are always the first thing to think of. But the design pattern is not a silver bullet, and it can’t solve all problems. It is just a trick method refined and summarized by the predecessors. Developers need to make reasonable choices and appropriate adaptations according to the actual business scenarios to truly solve the problems in the actual scenarios. And summarize and form their own methodology.
So let's take a look at how design patterns help us improve system scalability in the marketing strategy engine.
2. Marketing strategy engine
Let's briefly introduce the marketing strategy engine: the strategy engine is to build visual process components, define each process node, and automate the execution of activity business processes, thereby providing different operational activity capabilities. The core activity business process mainly includes three parts: Operational activity configuration -> Operational activity approval -> Operational activity execution .
- Operational activity configuration: Operation personnel configure operation activities in the background of the system. Including event name, event time, trigger conditions, event users and specific push channels (such as SMS, WeChat, push, etc.).
- Operational activity approval: Quality/supervisor approves operation activity configuration. The approval process involves the configuration of activity approval nodes and personnel, and the configuration of related callback operations for approval.
- Operational activity execution: The system automates the process of executing operation activities. That is, specific channels such as SMS, WeChat, push and other push activities are task execution and delivery procedures, including user data preparation, data delivery and push, and data effect recovery.
Third, the specific application of design patterns
3.1 Operational activity configuration
3.1.1 Factory Mode
Specific scene
Under normal circumstances, according to different users and activity scenarios, operations will use data analysis to determine different activity strategies, such as the need to create SMS push strategies, WeChat graphic push strategies, and App Push push strategies. At this point, we can use the factory model to manage the creation of specific push strategies in a unified manner.
Pattern analysis
In GoF "Design Patterns: The Foundation of Reusable Object-Oriented Software": The factory pattern is divided into two types: factory methods and abstract factories, and the simple factory pattern (also known as the static factory pattern) is regarded as a kind of factory method special case. However, because simple factories and factory methods are relatively simpler and easier to understand, and the code is more readable, they are more commonly used in actual projects.
The applicable scenarios of the simple factory:
- a. The factory class is responsible for creating fewer objects, and the creation logic in the factory method is simple.
- b. The client does not need to care about the details of creating a specific object, but only needs to know the type parameters passed into the factory class.
The applicable scenarios of the factory method:
- a. The creation logic of factory objects is relatively complicated, and the instantiation of the factory needs to be delayed to its concrete factory subclass.
- b. Suitable for scenarios with frequent changes in requirements. Different factory implementation classes can be used to support new factory creation schemes, which are more in line with the opening and closing principles and have better scalability.
Typical code example
//抽象产品类
public abstract class Product {
public abstract void method();
}
//具体的产品类
class ProductA extends Product {
@Override
public void method() {
//具体的执行逻辑
}
}
//抽象工厂模板类
abstract class Factory<T> {
abstract Product createProduct(Class<T> c);
}
//具体工厂实现类
class FactoryA extends Factory{
@Override
Product createProduct(Class c) {
Product product = (Product) Class.forName(c.getName()).newInstance();
return product;
}
}
Actual code
/**
* @author chenwangrong
* 活动策略工厂类
*/
@Component
@Slf4j
public class ActivityStrategyFactory {
/**
* 获得渠道类型对应的策略
*
* @param channelType channelType
* @return OperationServiceStrategy
*/
public static ActivityStrategy getActivityStrategy(ChannelTypeEnum channelType) {
ChannelTypeStrategyEnum channelTypeStrategyEnum = ChannelTypeStrategyEnum.getByChannelType(channelType);
Assert.notNull(channelTypeStrategyEnum , "指定的渠道类型[channelType=" + channelType + "]不存在");
String strategyName= channelTypeStrategyEnum.getHandlerName();
Assert.notNull(strategyName, "指定的渠道类型[channelType=" + channelType + "未配置策略");
return (ActivityStrategy)SpringContextHolder.getBean(handlerName);
}
public enum ChannelTypeStrategyEnum {
/**
* 短信渠道
*/
SMS(ChannelTypeEnum.SMS, "smsActivityStrategy"),
/**
* 微信渠道
*/
WX_NEWS(ChannelTypeEnum.WX, "wxActivityStrategy"),
/**
* push渠道
*/
PUSH(ChannelTypeEnum.PUSH, "pushActivityStrategy"),;
private final ChannelTypeEnum channelTypeEnum;
private final String strategyName;
ChannelTypeStrategyEnum (ChannelTypeEnum channelTypeEnum, String strategyName) {
this.channelTypeEnum = channelTypeEnum;
this.strategyName= strategyName;
}
public String getStrategyName() {
return strategyName;
}
public static ChannelTypeStrategyEnum getByChannelType(ChannelTypeEnum channelTypeEnum) {
for (ChannelTypeStrategyEnum channelTypeStrategyEnum : values()) {
if (channelTypeEnum == channelTypeStrategyEnum.channelTypeEnum) {
return channelTypeStrategyEnum ;
}
}
return null;
}
}
}
Practice summary
In the actual project code, we use the simple factory mode (static factory mode), and use enumeration (or mapping configuration table) to save the mapping relationship between channel types and specific strategy implementation classes, and combine with Spring's singleton Mode to create the strategy class.
Compared with the factory method pattern, under the premise of satisfying the business, the number of factory classes is reduced, and the code is simpler and more applicable.
3.1.2 Template method mode
specific scene
When creating strategies for different types of operational activities, you can find that in addition to saving specific activity channel configuration information, many operational procedures are the same during the creation process: for example, saves the basic configuration information of the activity, reports the audit log, and creates the activity approval process. After the creation is completed, the message reminds .
Original practice
/**
* 短信活动类
*
*/
@Service
public class SmsActivityStrategy{
/**
* 执行渠道发送
*
* @param msgParam msgParam
*/
public ProcessResult createActivity(ActParam param) {
//保存活动基础信息
saveActBaseConfig(param);
//保存短信活动配置
createSmsActivity(param);
//审计日志上报 ...
//创建活动审批工单 ...
//消息通知 ...
sendNotification(param);
}
}
/**
* Push活动类
*
*/
@Service
public class PushActivityStrategy{
/**
* 执行渠道发送
*
* @param msgParam msgParam
*/
public ProcessResult createActivity(ActParam param) {
//保存活动基础信息
saveActBaseConfig(param);
//保存Push活动配置
createChannelActivity(param);
//审计日志上报 ...
//创建活动审批工单 ...
//消息通知 ...
sendNotification(param);
}
}
...
For each activity strategy, these operations are necessary and the operation process is fixed, so these operations can be extracted into a common process, at this time the template method mode is considered.
mode analysis
In GoF "Design Patterns: The Foundation of Reusable Object-Oriented Software": The template method pattern is to define an algorithm skeleton in a method and defer certain steps to its subclasses. The template method pattern allows subclasses to redefine certain steps of the algorithm without changing the structure of the algorithm.
The "algorithm" mentioned above can be understood as business logic, and the "algorithm skeleton" is the template, and the method containing the "algorithm skeleton" is the template method, which is also the source of the template method pattern name.
The template method pattern is applicable to scenarios: the business logic is composed of certain steps. If the sequence of these steps is fixed, some methods or implementations may be different between different specific businesses.
In the implementation, a logic template and framework are generally defined through abstract classes, and then the indeterminate parts are abstracted into abstract methods and handed over to subclasses for implementation, and the calling logic is still completed in the abstract class.
Typical code example
//模板类
public abstract class AbstractTemplate {
//业务逻辑1
protected abstract void doStep1();
//业务逻辑2
protected abstract void doStep2();
//模板方法
public void templateMethod(){
this.doStep1();
//公共逻辑
......
this.doStep2();
}
}
//具体实现类1
public class ConcreteClass1 extends AbstractTemplate {
//实现业务逻辑1
protected void doStep1()
{
//业务逻辑处理
}
//实现业务逻辑2
protected void doStep2()
{
//业务逻辑处理
}
}
//具体实现类2
public class ConcreteClass2 extends AbstractTemplate {
//实现业务逻辑1
protected void doStep1()
{
//业务逻辑处理
}
//实现业务逻辑2
protected void doStep2()
{
//业务逻辑处理
}
}
// 调用类
public class Client {
public static void main(String[] args)
{
AbstractTemplate class1=new ConcreteClass1();
AbstractTemplate class2=new ConcreteClass2();
//调用模板方法
class1.templateMethod();
class2.templateMethod();
}
}
Actual code
/**
* 活动创建模板类
*
* @author chenwangrong
*/
@Slf4j
public abstract class AbstractActivityTemplate{
/**
* 保存具体活动配置
*
* @param param 活动参数
* @return ProcessResult 处理结果
*/
protected abstract ProcessResult createChannelActivity(ActParam param);
/**
* 执行活动创建
*
* @param msgParam msgParam
*/
public ProcessResult createActivity(ActParam param) {
//保存活动基础信息
saveActBaseConfig(param);
//保存具体渠道配置
createChannelActivity(param);
//审计日志上报 ...
//消息通知 ...
}
}
/**
* 短信活动类
*
*/
@Service
public class SmsActivityStrategy extends AbstractActivityTemplate{
/**
* 创建短信渠道活动配置
*
* @param msgParam msgParam
*/
public ProcessResult createChannelActivity(ActParam param) {
//仅需要实现:保存短信活动配置
createSmsActivity(param);
}
}
(其他渠道活动类似,此处省略)
// 调用类
public class Client {
public static void main(String[] args)
{
AbstractActivityTemplate smsActivityStrategy=new SmsActivityStrategy();
AbstractActivityTemplate pushActivityStrategy=new PushActivityStrategy();
ActParam param = new ActParam();
//调用具体活动实现类
smsActivityStrategy.createActivity(param);
pushActivityStrategy.createActivity(param);
}
}
Practice summary
The template method pattern has two major functions: reuse and extension . Reuse means that all subclasses can reuse the code of the template method provided in the parent class. Extension means that the framework provides function extension points through the template mode, allowing users to customize the functions of the framework based on the extension points without modifying the framework source code.
The template method is very suitable for scenarios where there is a general business logic processing flow, but there are certain differences in the specific flow. You can extract the process skeleton into the template class and set the variable differences as abstract methods to achieve the same encapsulation. Part, the purpose of extending the variable part.
3.1.3 Strategy Mode
specific scene
We extracted the public process skeleton through the template method pattern above, but there is still a problem here: The calling class still needs to know the specific implementation class, and the can be called after instantiation. That is, every time a new channel activity is added, the caller must modify the calling logic and add a new activity implementation class to initialize the call, which obviously does not take advantage of the scalability of the business.
In the process of creating operational activities, different types of activities correspond to different creation processes, and the caller only needs to distinguish according to the channel type, without having to worry about the specific business logic. At this time, the strategy mode is a better choice.
mode analysis
In GoF "Design Patterns: The Foundation of Reusable Object-Oriented Software": The strategy pattern defines a family of algorithm classes and encapsulates each algorithm so that they can be replaced with each other. Strategy mode can make algorithm changes independent of the caller using them.
Typical code example
//策略接口定义
public interface Strategy {
void doStrategy();
}
//策略具体实现类(多个)
public class StrategyA implements Strategy{
@Override
public void doStrategy() {
}
}
//上下文操作类, 屏蔽高层模块对策略的直接访问
public class Context {
private Strategy strategy = null;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void doStrategy() {
strategy.doStrategy();
}
}
Actual code
/**
* 渠道活动创建策略接口
*
*/
public interface ActivityStrategy {
/**
* 创建渠道活动配置
*
* @param param 活动参数
* @return
*/
void createActivity(ActParam param);
}
/**
* 活动模板类
*
*/
@Slf4j
public abstract class AbstractActivityTemplate implements ActivityStrategy {
/**
* 抽象方法:具体渠道活动创建
*
*/
protected abstract ProcessResult createChannelActivity(ActParam param);
@Override
public ProcessResult createActivity(ActParam param) {
//保存活动基础信息
saveActBaseConfig(param);
//保存具体渠道配置
createChannelActivity(param);
//审计日志上报 ...
//消息通知 ...
}
}
/**
* 短信推送策略具体实现类
*
*/
@Component
public class SmsChannelActivityStrategy extends AbstractActivityTemplate {
@Override
public void createChannelActivity(ActParam param) {
//保存短信配置数据
}
}
(其他渠道活动类似,此处省略)
/**
* 策略调用入口
*
*/
@Slf4j
@Component
public class ActivityContext {
@Resource
private ActivityStrategyFactory activityStrategyFactory ;
public void create(ActParam param) {
//通过前面的工厂模式的代码,获取具体渠道对应的策略类
ActivityStrategy strategy = activityStrategyFactory.getActivityStrategy(param.ChannelType);
//执行策略
strategy.createActivity(param);
}
}
In the actual coding process, we added ChannelActivityStrategy as the channel activity creation strategy interface, and used the template class AbstractActivityTemplate to implement the interface, and combined with the factory mode to create a specific strategy. far, the three modes have been combined .
Practice summary
The strategy mode is often used to eliminate the complex if else logic in the project development process. If there are new channel activities in the future, you only need to add the corresponding channel activity creation logic, which can easily expand the system business.
In the process of project practice, factory mode, template method mode and strategy mode are often used together. template method mode extracts the common skeleton of a business process, the strategy mode implements the implementation and invocation of specific sub-process strategies, and the factory mode allows the creation of sub-process strategies.
The combined use of multiple modes can give full play to the advantages of each mode and achieve the purpose of truly improving the scalability of system design.
3.2 Execution of operational activities
3.2.1 State Mode
specific scene
During the execution of operational activities, there will be changes in the activity status, as well as the condition detection before the change and the operation processing after the change. Correspondingly, we can easily think of state patterns.
mode analysis
In GoF's classic "Design Patterns: The Foundation of Reusable Object-Oriented Software": The state pattern allows an object to change its behavior when its internal state changes.
The function of the state mode is to separate the behavior of the state, and call different functions corresponding to different states by maintaining the changes of the state. Their relationship can be described as: state determines the behavior . Since the state is changed during the runtime, the behavior will also change during the runtime as the state changes.
Typical code example
/**
* 状态模式
* 抽象状态类
* */
interface State {
//状态对应的处理
void handle()
}
//具体状态关现类
public class ConcreteStateA implements State {
@Override
public void handle() {
}
}
public class ConcreteStateB implements State {
@Override
public void handle() {
}
}
//环境类Context,访问入口
public class Context {
//持有一个State类型的对象实例
private State state;
public void setState(State state) {
this.state = state;
}
public void request() {
//转调state来处理
state.handle();
}
}
public class Client {
public static void main(String[] args){
//创建状态
State state = new ConcreteStateB();
//创建环境
Context context = new Context();
//将状态设置到环境中
context.setState(state);
//请求
context.request();
}
}
Practice summary
In actual software project development, scenarios where there are not many business states and simple state transitions can be implemented using the state mode; but if the state transition of the business process involved is complicated, the use of the state mode will introduce a lot of state classes and methods. When the state logic changes, the code will become difficult to maintain. At this time, using the state mode is not very suitable.
And when there are many process states and the business logic involved in event verification and trigger execution actions is more complicated, how to achieve it?
Here we must stop and think: the use of design patterns is only a means to solve practical problems, but design patterns are not a "omnipotent" hammer, we need to clearly understand its advantages and disadvantages. In this problem scenario, the industry already has a more general solution-a finite state machine, which provides more convenient applications for business through higher-level encapsulation.
3.2.2 Application of state mode-finite state machine
Finite-State Machine (Finite-State Machine, abbreviation: FSM ), referred to as state machine in the industry. It is also composed of event , state , action , and the relationship between the three is: the event triggers the transition of the state, and the state transition triggers the execution of subsequent actions. The state machine model may be based on a conventional hard-coded state is achieved, can also database / file configuration or the DSL manner to save state, and configured to implement the transition (recommended).
Many open source state machine frameworks have emerged in the industry. The more commonly used ones are Spring-statemachine (officially provided by Spring), squirrel statemachine, and Alibaba's open source cola-statemachine.
Practical application
In actual project development, we focused on the characteristics of our own business: business process states, but event triggering and state change actions are relatively simple , so we chose a stateless, more lightweight solution-based on open source state machine Realize ideas for development. (The implementation and selection of the state machine will be further analyzed in a follow-up article. Interested children's shoes can visit the official website to understand first).
Practice code
/**
* 状态机工厂类
*/
public class StatusMachineEngine {
private StatusMachineEngine() {
}
private static final Map<OrderTypeEnum, String> STATUS_MACHINE_MAP = new HashMap();
static {
//短信推送状态
STATUS_MACHINE_MAP.put(ChannelTypeEnum.SMS, "smsStateMachine");
//PUSH推送状态
STATUS_MACHINE_MAP.put(ChannelTypeEnum.PUSH, "pushStateMachine");
//......
}
public static String getMachineEngine(ChannelTypeEnum channelTypeEnum) {
return STATUS_MACHINE_MAP.get(channelTypeEnum);
}
/**
* 触发状态转移
* @param channelTypeEnum
* @param status 当前状态
* @param eventType 触发事件
* @param context 上下文参数
*/
public static void fire(ChannelTypeEnum channelTypeEnum, String status, EventType eventType, Context context) {
StateMachine orderStateMachine = StateMachineFactory.get(STATUS_MACHINE_MAP.get(channelTypeEnum));
//推动状态机进行流转,具体介绍本期先省略
orderStateMachine.fireEvent(status, eventType, context);
}
/**
* 短信推送活动状态机初始化
*/
@Component
public class SmsStateMachine implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private StatusAction smsStatusAction;
@Autowired
private StatusCondition smsStatusCondition;
//基于DSL构建状态配置,触发事件转移和后续的动作
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
StateMachineBuilder<String, EventType, Context> builder = StateMachineBuilderFactory.create();
builder.externalTransition()
.from(INIT)
.to(NOT_START)
.on(EventType.TIME_BEGIN)
.when(smsStatusAction.checkNotifyCondition())
.perform(smsStatusAction.doNotifyAction());
builder.externalTransition()
.from(NOT_START)
.to(DATA_PREPARING)
.on(EventType.CAL_DATA)
.when(smsStatusCondition.doNotifyAction())
.perform(smsStatusAction.doNotifyAction());
builder.externalTransition()
.from(DATA_PREPARING)
.to(DATA_PREPARED)
.on(EventType.PREPARED_DATA)
.when(smsStatusCondition.doNotifyAction())
.perform(smsStatusAction.doNotifyAction());
...(省略其他状态)
builder.build(StatusMachineEngine.getMachineEngine(ChannelTypeEnum.SMS));
}
//调用端
public class Client {
public static void main(String[] args){
//构建活动上下文
Context context = new Context(...);
// 触发状态流转
StatusMachineEngine.fire(ChannelTypeEnum.SMS, INIT, EventType.SUBMIT, context);
}
}
}
By pre-defining the state transition process, the ApplicationListener interface is implemented, and the event, state transition conditions, and the process of triggering operations are loaded into the working memory of the state machine when the application is started, and the state machine is driven by the event to automatically flow.
Practice summary
In actual scenarios, it is not necessary to forcefully apply the design pattern, but should fully combine the characteristics of the business, and at the same time, according to the advantages and disadvantages of the design pattern, make more appropriate selection or further expansion.
3.3 Approval of automated operation activities
3.3.1 Comprehensive application of design patterns-workflow engine
specific scene
In order to manage quality and risk, the creation of activities needs to be included in the approval process to control the release and execution of operational activities. At the same time, for different types of operational activities, different business fields and departments may be involved, and different approval and control personnel are required. Configure the corresponding approval relationship.
At this time you need to do:
- a. The approval process is fully configurable, easy to modify and add;
- b. The business process nodes can be freely arranged, and the components are common;
- c. The process data is persistent, and the approval process data needs to be operated and monitored.
In response to this demand, the industry has a set of common business tools- workflow engine . The workflow engine obviously does not belong to the realization of a specific design pattern, it is a component application that covers a variety of design patterns.
Not only the approval function, in fact, the previous automated marketing process engine design also uses the workflow engine build the process component :
State Machine VS Workflow Engine
There seems to be a lot of similarities between the workflow engine and the state machine. Both can complete the business process by defining the process nodes, transition conditions, and correspondingly triggered operations. If you only look at the complexity of the applicable scenarios, the state machine is more suitable for single-dimensional business problems, can clearly depict all possible states and the events that lead to transitions, and is more flexible and lighter; while the workflow engine is more suitable for business Process management can improve the efficiency of the overall business process by solving more complicated process automation problems such as large-scale CRM.
Among the industry's workflow engines, the more famous ones are Activiti and JBPM. (The comparison between state machine and workflow engine, the specific introduction and selection of open source workflow engine, and how to develop and build a basic workflow engine component by yourself will also be further analyzed in a follow-up article. This article is due to The reasons for the theme and length will not be introduced in detail for the time being.)
In the actual development process, we self-developed a simple version of the workflow engine based on the open source Activiti workflow engine, simplifying many related configurations, and leaving only the core process operations and data records.
Workflow engine flowchart:
Practice summary
Workflow engine is an application component that covers a variety of design patterns. It needs to be applied only in complex and changeable business scenarios and needs to be carefully evaluated in conjunction with the business. Use appropriate solutions in , follow the simple, appropriate, and evolvable principles of system architecture design, and do not over-design.
Four, summary
Based on the business practice of automated marketing, this article analyzes and introduces the specific implementation process of the four modes of factory method mode, template method mode, strategy mode and state mode in project development. It also introduces the state machine and workflow engine in addition to the simple mode, which covers a variety of design mode system components, and shares the choices and thinking in the process.
Faced with the complex and changeable needs of business, we need to always pay attention to the reusability and scalability of system design, and design principles and design patterns can give us directional guidance when system design is implemented, and it needs to be carried out according to actual business scenarios. Make reasonable choices, make appropriate adaptations, and constantly improve your own methodology.
In the follow-up, we will bring other content of the series of special articles. Each article will provide a detailed analysis of the technical practices in it, so stay tuned.
Author: vivo Internet server team-Chen Wangrong
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。