With the continuous iterative development of the company’s business requirements, the amount of code and business logic of the project is also increasing, and the original development mode and architecture can no longer meet the speed of our business development. At this time, we need to carry out a major reconstruction operation of the original project. At this point, it should be clear where the cutting edge of this operation is, which is the previous highly coupled business components and functional components. The purpose of the operation is to break these coupling into independent components.
Engineering effect preview
Componentized project example project address
Componentized open source Project Git repository address
Here we will explain these questions
- Why componentization, and what advantages does it give us
- How should each component be split, and how should the granularity of the split be controlled
- How to build a componentized architecture project from zero to one
Why componentization
Let’s start with a picture of the dependencies between our modules before componentization
From the above interdependence of each business component, they are interdependent. Serious coupling between business component and business component is generated, which greatly reduces the expansibility of our project and increases the maintenance cost.
For example, suppose the product manager says one day that our company’s business is growing so well that our marketing module needs to be separated into a separate application so that we can add more efficient marketing tools. At this time we are stunned, need to separate an APP out, how can this ah, marketing module code and a lot of other business code coupled together, now to be independent, it can only write a marketing application, the previous code stripping is not clean.
We can see from the simple example above: Before the project do not really in the sense of componentization, each business module and the height of the coupling between business module, high coupling between functional components and functional components for the future of the company’s business expansion, the cost is very high, can’t do the height of the same business logic code reuse, so for our development and productivity.
Well, some of you might say, well, since the coupling between the modules is so high, I’m going to decouple the coupling, and then you might have the relationship between the modules in the picture below.
From the diagram below, we can see that there is indeed complete decoupling between the business modules, they are no longer dependent on each other, and we have introduced a role of an intermediate scheduler, and now the business modules and the role of the intermediate scheduler are heavily dependent. After thinking, we found that our various business modules rely on this intermediate scheduler, which is completely normal, because they need this scheduler to do the unified event distribution work, but this scheduler depends on each business module, is this layer of dependence necessary? Let’s think back to the fact that true componentized development is complete de-dependency, a dependency that is completely unnecessary. For example, if we have A new B APP to develop, we also need to use this intermediate scheduler component, but we cannot directly use it because it depends on many business components of A APP. As a result, our componentized architecture design needs another upgrade to the model shown below.
From the above figure, we can see that each business module only relies on the intermediate scheduler, and the intermediate scheduler does not generate any dependence on each module.
Ok, from the comparison of the above three figures, we can have a good understanding of why our projects need to implement componentized architecture development urgently, and the advantages and disadvantages of each.
How should the components be split
There is no complete standard on how to split components, because the business scenarios of each company are different, and the corresponding derived business modules are also different. Therefore, the split of business components can be reasonably divided according to the business modules of the company. Here we talk about the general division direction of the components of the whole project
- Master project: When our project was fully developed using a componentized architecture, we were surprised to find that our master project was an empty shell. Because all of the main project presentation is broken down into separate business components, including the tools components are also independent of each other. In this way, we find that developing a complete APP is just like building Lego blocks, with all the parts, and we can build them by combination at will. Isn’t it cool?
- Business Components: Business components are the individual product business function modules shown in our sample figure above, which we encapsulate as separate components. For example, the electronic invoice business component in sample Demo, business component A, business component B. We build a complete APP project by assembling individual business components.
- Base toolclass components: Base toolclasses are independent tool components without any dependencies. They have no dependencies on other tool components, business components, and so on. Examples of such components are the Safe component for exception protection of arrays and dictionaries, the Array component for extending Array functionality, and the encryption component for encrypting strings.
- Middleware component: This is a special component that we derived for componentized development. The intermediate scheduler in the example figure above is a functionally independent middleware component.
- Basic UI components: View components are more common, such as our encapsulated navigation component, Modal popbox component, PickerView component, etc.
- Business tool components: These are components that provide the basic functionality for various business components. Such components may depend on other components. For example: network request component, image cache component, Jspatch component, and so on
As for the granularity of component resolution, this is really not good to determine, vary from person to person, different requirements function complexity split out of the component size is not the same
How to build a componentized architecture from zero to one
Before we talk about how to implement a component-architecture project from scratch, we need to be familiar with creating component libraries using PODS. Let’s take a look around the provided componentized example project.
First, let’s look at what business components are included in the sample Demo (as shown below) :
In the sample Demo, I provide three business components as demonstration effects, among which business modules A and B are temporary business module components, and electronic invoice business components are real enterprise requirement functional components.
Let’s take a look at what tool components are provided in the sample Demo (as shown below)
Note: the 6 tool components provided here are all functional components that the author has packaged, you can also directly install the installation to use oh.
Detailed Operation Procedure
The first step:
We first create an empty iOS project :MainProject, as our MainProject, which is the shell project mentioned above, and then initialize pod. If you don’t know how to use pod, please refer to the information by yourself.
The second step:
We create an empty project :ModuleA, which serves as our business A component. Then we initialize the pod and the PodSpec file.
Step 3:
We create an empty project :ModuleB, which serves as our business B component. Then we initialize the pod and the PodSpec file.
Step 4:
We create an empty project :ComponentMiddleware, which is what we called the intermediate scheduler above. Then we initialize the pod and the PodSpec file.
Step 5:
We create an empty project: ModuleACategory, which is A categorical project for business component A. Then we initialize the pod and the PodSpec file.
Step 6:
We create an empty project: ModuleBCategory, which is a categorical project for business component B. Then we initialize the pod and the PodSpec file.
Now that the main project and the two business component projects, as well as the two component classification projects, have been created, let’s explain how they work with each other. I start with the main project loading business components and work my way down, leading to the purpose of each project.
Step 7:
We introduced our business component B project ModuleB in the Podfile of the MainProject MainProject, as well as our ModuleB category project: ModuleBCategory. Then we pod install. These two component libraries have now been introduced into our main project.
Example code is as follows:
# Uncomment the next line to define a global platform for your project
platform :ios, '8.0'
source 'https://github.com/CocoaPods/Specs.git'
source 'https://github.com/guangqiang-liu/GQSpec.git'
target 'GQComponentDemo' do
pod 'ModuleB'
pod 'ModuleBCategory'
end
Copy the code
We then add a button event to the main project that clicks push to the page of business component B.
Example code is as follows:
#import <ModuleBCategory/ComponentScheduler+ModuleB.h>
- (void)moduleB {
UIViewController *VC = [[ComponentScheduler sharedInstance] ModuleB_viewControllerWithCallback:^(NSString *result) {
NSLog(@"resultB: --- %@", result);
}];
[self.navigationController pushViewController:VC animated:YES];
}
Copy the code
Step 8:
In step 7 above, we used the ModuleBCategory project. We’ve only exposed two files on this project. These two files are the intermediate scheduler classification above, that is, the middleware classification. So let’s look at the.h and.m implementation of this classification file.
.h
#import "ComponentScheduler.h"
@interface ComponentScheduler (ModuleB)
- (UIViewController *)ModuleB_viewControllerWithCallback:(void(^)(NSString *result))callback;
@end
Copy the code
.m
#import "ComponentScheduler+ModuleB.h"
@implementation ComponentScheduler (ModuleB)
- (UIViewController *)ModuleB_viewControllerWithCallback:(void(^)(NSString *result))callback {
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
params[@"callback"] = callback;
return [self performTarget:@"ModuleB" action:@"viewController" params:params shouldCacheTarget:NO];
}
@end
Copy the code
We found that the classification implementation was very simple, just exposing a function, Then call [self performTarget:@”ModuleB” Action :@”viewController” params:params shouldCacheTarget:NO]; And returns the return value of the execution.
The purpose of this classification is to specify the Target name and the Action name in advance because these two names will be used in the middleware component.
The above performTarget: action: params: shouldCacheTarget function is a function of middleware. Because ModuleBCategory is a classification file for ComponentScheduler, this function can be called.
Middleware components need to be referenced in the ModuleBCategory project so we need to reference middleware components in the ModuleBCategory Podfile
Example code is as follows:
# Uncomment the next line to define a global platform for your project
platform :ios, '8.0'
source 'https://github.com/CocoaPods/Specs.git'
source 'https://github.com/guangqiang-liu/GQSpec.git'
target 'ModuleB-Category' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
# Pods for ModuleB-Category
pod 'ComponentScheduler'
end
Copy the code
Step 9:
Since middleware engineering was referred to in step 8 above, let’s take a look at what middleware engineering actually does. Remember step 8 above, we call a middleware provide functions: performTarget: action: params: shouldCacheTarget, this is the core middleware functions.
Core function code block is as follows:
Remember from step 8 above, we called the argument passed by this function. Let’s take a look at the call code
[self performTarget:@"ModuleB" action:@"viewController" params:params shouldCacheTarget:NO];
We can see that TargetName is the ModuleB we passed, action is the viewController we passed, and we pass these two parameters to the following function:
[self safePerformAction:action target:target params:params];
Let’s see what the values of these two parameters are:
This function finally calls the apple official function: [Target performSelector: Action withObject:params];
If you look at performSelector withObject, you should be familiar with the messaging mechanism of iOS.
[Target_ModuleB performSelector:Action_viewController withObject:params];
Copy the code
The Target_ModuleB class calls its Action_viewController: method and passes it params.
As you’ll notice, we don’t see the Target_ModuleB class anywhere, and we don’t see Target_ModuleB calling its Action_viewController: method.
Yes, the Target_ModuleB class and its Action_viewController method are explained in step 10.
Step 10:
This is the last step. It’s hard to write. Don’t worry, boys
The careful guys noticed that none of the above steps mentioned anything about business component B. Yes, in addition to providing the business functions of Component B, business Component B needs to provide us with a Target file.
Let’s first look at the business code for business component B:
Example code is as follows:
#import "ModuleBViewController.h"
#import "PageBViewController.h"
@interface ModuleBViewController ()
@end
@implementation ModuleBViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = @"I'm module B business component.";
self.view.backgroundColor = [UIColor whiteColor];
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame = CGRectMake(0, 0, 300, 100);
btn.backgroundColor = [UIColor greenColor];
btn.center = self.view.center;
[btn setTitle:@"Module B Business Function Component" forState: UIControlStateNormal];
[btn addTarget:self action:@selector(push) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
- (void)push {
PageBViewController *VC = [[PageBViewController alloc] init];
[self.navigationController pushViewController:VC animated:YES];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
Copy the code
We find that the business code for business component B is also very simple, which is to do a push jump from PageA controller to PageB controller. What is there to say about this
Let’s go back to the target file mentioned above
Example code is as follows:
.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface Target_ModuleB : NSObject
- (UIViewController *)Action_viewController:(NSDictionary *)params;
@end
Copy the code
.m
#import "Target_ModuleB.h"
#import "ModuleBViewController.h"
@implementation Target_ModuleB
- (UIViewController *)Action_viewController:(NSDictionary *)params {
ModuleBViewController *VC = [[ModuleBViewController alloc] init];
return VC;
}
@end
Copy the code
As you can see from the implementation file above, the Target file is also very simple to provide us with the Target controller instance object to navigate to. The target controller instance here is the ModuleBViewController instance for business component B.
Careful little partners found, hey! The target and action we printed in step 9 are Target_ModuleB and Action_viewController: for target files.
We have just covered A series of processes for business component B. The usage of business component A is the same as the usage of business component B. If there are other business components C and D, they are the same, so we will not explain them all.
Well, now you should understand this series of work procedures, if not, can read Casa’s explanation of CTMediator. The authors recommend running the supplied sample Demo project directly for debugging, which makes it easier to understand the relationships between the components.
Componentized project example project address
Componentized open source Project Git repository address
Finally, let’s look at a complete architecture diagram for componentization:
conclusion
What we explain above is just the basic framework of simple project component architecture, but in the real enterprise development, we only build such a simple project framework structure is far from meeting the needs of the development, we also need to add branches in the project framework to meet the existing needs. In the example Demo provided above, I separate the electronic invoice business component into a complete project, and combine the popular MVVM design pattern and RAC data binding framework to realize the functional development of the electronic invoice module. If you are interested in MVVM + RAC development, you can install the e-invoice project separately, the project address: ios-MVM-RAC
Well, it’s the wee hours of the morning again, so that’s the end of this tutorial. The next tutorial will show you how to use MVVM+RAC for real development. Friends, feel the article to help you, help click a like, open source componentization engineering projectiOS-Component-ProDo me a star, too. Thanks in advance.
reference
This article mainly used Casatwy’s CTMediator idea to practice again. There are also MGJRouter of Mogujie and BeeHive of Ali for your reference.
- Casatwy.com/modulizatio…
- Github.com/casatwy/CTM…
- Github.com/alibaba/Bee…
- Limboy. Me/tech / 2016/0…
- Github.com/meili/MGJRo…
More articles
- Author of React Native project OneM(build framework according to enterprise development standards) : OneM: Welcome friends star
- The author of mpvue Meituan takeout small program: MPvue-meituan: Welcome friends star
- Author nuggets technology column: juejin.cn/user/308708…
- The author Jane homepage: book contains more than 60 RN development related technical article www.jianshu.com/u/023338566… Welcome friends: pay more attention and give more likes
- React Native QQ Exchange group (600+ RN engineers) : 620792950
- IOS QQ technical exchange group: 678441305 welcome friends into the group exchange learning