• Stars
    star
    388
  • Rank 107,150 (Top 3 %)
  • Language
    Objective-C
  • License
    MIT License
  • Created over 6 years ago
  • Updated almost 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A delightful library for app business modular architecture.

License MIT  CocoaPods  Platform  Support  Build Status

有赞logo

项目logo

A delightful library for app business modular architecture.

中文版README

What is Bifrost(/ˈbɪvrɒst/)

The Bifrost is a delightful library for App Business Modular Architecture. The name comes from the Myth of North Europe and the famous Marvel movie Thor. Bifrost is a rainbow bridge, by which People can reach anywhere in a moment.

Some Terms

Firstly let's sync some terms we will use later.

Business Module vs Function Module

Usually, we call a library for non-business functions Function Module, like AFN, SDWebImage, Masonry, ... They can be used across all kinds of apps and they have lot's of APIs. App needs to import their API declaration and invoke their API directly. And the Business Module contains lot's of business code, like the Goods Module, Trade Module, ... They can only be used in limited number of apps(sometimes only 1), and they only export a few of APIs or router urls because business module can't import any API declaration from other business modules.

Code Dependency vs Business Dependency

The Code dependency means the dependency on code. If A module has code dependency on B module, A imports some API declarations from B. In other words, A module can't be built successfully without B module's code. The Business Dependency means the business requirement on other modules. For example, if the Trade module needs to show a goods details page, it has business dependency on Goods module. The Code dependency can be removed by Business Modular Architecture, but the business dependency always exists.

Business Modular Architecture (BMA)

As an app becomes more and more complex, there will be quite a lot of dependencies between different business modules. One file is changed, so many files are affected. The development efficiency is affected. The purpose of BMAD is to remove all code dependency between different modules, so that our coding can be more efficient. NOTE: Not all projects needs BMA. A precondition should be that the project's business domains won't change too frequently. In other words, the app should be able to be divided to some stable modules first, then we can use BMA on it.

Use the demo project as a sample. It contains 4 business modules: Home, Shop, Sale, Goods. Without BMA, it's module dependency relationship can be:

And if with BMA, it's module dependency relationship will be

You can see, all business module won't have code dependency on each other. They only dependent on Common and Mediator module. Like the Sale module don't know the Goods module. It asks goods info from the Mediator. If Goods module registers itself on Mediator, Sale module can get goods info, if not, Sale module can't get goods info, but the Sale module still can be built successfully. In other words, the API error of Goods module won't affect the development of Sale module.

Usage

The Bifrost removes dependencies between different modules by 2 ways: Router URLs and Remote APIs. The Router URLs are often used to go to other UI pages. And the Remote APIs are used for non-UI actions or complex data delivery.

Installation

It's recommended to use CocoaPods to install the Bifrost lib. Like this:

pod 'Bifrost'

Router URL

Bifrost can bind a url string with a block. Simply, we can do the bind in the +load method.

//In GoodsDetailsViewController.m
+ (void)load {
//static NSString *const kRouteGoodsDetail = @"//goods/detail";
//static NSString *const kRouteGoodsDetailParamId = @"id";
//Above router url and param id are defined somewhere to avoid hardcoding
    [Bifrost bindURL:kRouteGoodsDetail toHandler:^id _Nullable(NSDictionary * _Nullable parameters) {
        GoodsDetailsViewController *vc = [[self alloc] init];
        vc.goodsId = parameters[kRouteGoodsDetailParamId];
        return vc;
    }];
}

And to invoke the URL by

//static NSString *const kRouteGoodsDetail = @"//goods/detail";
//static NSString *const kRouteGoodsDetailParamId = @"id";
//Above router url and param id are defined somewhere to avoid hardcoding
NSString *routeURL = BFStr(@"%@?%@=%@", kRouteGoodsDetail, kRouteGoodsDetailParamId, goods.goodsId);
UIViewController *vc = [Bifrost handleURL:routeURL];
if (vc) {
        [self.navigationController pushViewController:vc animated:YES];
}

Bifrost will parse the parameters in the url and put them in the parameters argument of the handler of the bindURL:toHandler: method. If a complex parameter which can't be put in the router url, like an image object, we can use following method:

/**
 The method to handle URL with complex parameters and completion block
 
 @param urlStr URL string
 @param complexParams complex parameters that can't be put in the url query strings
 @param completion The completion block
 @return the returned object of the url's BifrostRouteHandler
 */
+ (nullable id)handleURL:(nonnull NSString *)urlStr
           complexParams:(nullable NSDictionary*)complexParams
              completion:(nullable BifrostRouteCompletion)completion;

The completion parameter in above method is used to do callback of the router url. It will be put in the parameters with the key kBifrostRouteCompletion

Remote API

Also router url can meets most of the requirement, including those with complex parameters, but it's not convenient. So we still need to use remote api to do method invocation directly. Like the Goods module provide following service:

//In GoodsModuleService.h
@protocol GoodsModuleService <NSObject>
- (NSInteger)totalInventory;
- (NSArray<id<GoodsProtocol>>*)popularGoodsList; //热卖商品
- (NSArray<id<GoodsProtocol>>*)allGoodsList; //所有商品
- (id<GoodsProtocol>)goodsById:(nonnull NSString*)goodsId;
@end
@protocol GoodsProtocol <NSObject>
- (NSString*)goodsId;
- (NSString*)name;
- (CGFloat)price;
- (NSInteger)inventory;
@end

(Above declaration is in the file GoodsModuleService.h in the Mediator project in the demo.) Goods Module need to implement a GoodsModule class to conform above GoodsModuleService and provide implementation. The GoodsModule class should also conform to protocol BifrostModuleProtocol, so that it can be recognized by the Bifrost. The GoodsModule should also regitster itself, simply, in the +load method:

@implementation GoodsModule
+ (void)load {
    BFRegister(GoodsModuleService);
}
...
@end

Then wen can invoke those API in GoodsModuleService like this:

//In file ShoppingCartViewController.m in the demo
- (CGFloat)totalPrice {
   CGFloat totalPrice = 0;
   for (ShoppingCartItem *item in self.shoppingCartItemList) {
       id<GoodsProtocol> goods = [BFModule(GoodsModuleService) goodsById:item.goodsId];
       totalPrice += goods.price * item.num;
   }
   return totalPrice;
}

What's More

The Architecture of The Whole Project

The BMA lib (Bifrost) is only a small part of the Business Modular Arch. More work is to refactor the project‘s architecture to conform the BMA requirement: Different business modules can't have code dependency on each others. The following arch used in the demo project is suggested: BMA in Demo There are 3 parts in the App: Business Modules, Common Module and Mediator. Each Business module has 2 targets : static lib for code, and a bundle for resource. Business module put all its public API and router URLs in a ModuleService file. This ModuleService is put in the Mediator project, so that other business module can see these declarations. The business module provides implementation for its ModuleService. You can find more details in the demo project.

Performance

You may worry about the launch performance because we put quite a lot register code in the +load method. In fact the code for Bifrost in +load method is quite simple. I did a test to register 10000 router urls and 100 modules. It only cost another 60 ms.

//Get App pre-main time by Xcode's DYLD_PRINT_STATISTICS settings
//Without test code
Total pre-main time: 344.82 milliseconds (100.0%)
         dylib loading time: 171.59 milliseconds (49.7%)
        rebase/binding time:  36.06 milliseconds (10.4%)
            ObjC setup time: 102.27 milliseconds (29.6%)
           initializer time:  34.74 milliseconds (10.0%)
//With test code to register 10000 router urls and 100 modules
Total pre-main time: 366.12 milliseconds (100.0%)
         dylib loading time: 179.28 milliseconds (48.9%)
        rebase/binding time:  29.32 milliseconds (8.0%)
            ObjC setup time:  63.77 milliseconds (17.4%)
           initializer time:  93.50 milliseconds (25.5%)
//Note: the +load method mainly affects the initializer time.

In facts, One app may only contains 20-50 modules and 200-300 Routers. If you still want to save the time, you can try to put the binding code to some places after app launching.

Why do We Need Router URL?

It seems the Remote API is more powerful than router url. Why not only to use remote api? Like Ali's Beehive lib only provides the support for remote API. The main reason is that sometimes we need a way also can be used in other platform, like h5 page and android. And it's very convenient to use URL to go to another page. So Bifrost also supports router URLs.

More Repositories

1

vant

A lightweight, customizable Vue UI library for mobile web apps.
TypeScript
22,810
star
2

vant-weapp

轻量、可靠的小程序 UI 组件库
JavaScript
17,505
star
3

zent

A collection of essential UI components written with React.
TypeScript
2,240
star
4

zan-proxy

An extensible proxy for PC/Mobile/APP developer
TypeScript
1,810
star
5

vant-demo

Collection of vant demos.
Vue
1,560
star
6

zanphp

PHP开发面向C10K+的高并发SOA服务 和RPC服务首选框架
PHP
1,441
star
7

nsq

A realtime distributed messaging platform (forked from https://github.com/nsqio/nsq)
Go
628
star
8

zanui-weapp

本仓库已不再维护,请移步 https://github.com/youzan/vant-weapp
JavaScript
540
star
9

bugCatcher

方便产品、开发、测试三方协同管理、测试、监控项目进度和质量,以持续交付。
Java
484
star
10

zan

高效稳定、安全易用、线上实时验证的全异步高性能网络库,通过PHP扩展方式使用。
C
462
star
11

php-co-koa

PHP异步编程: 手把手教你实现co与Koa
459
star
12

YZSpamFilter

有赞垃圾内容过滤工具
Python
281
star
13

TitanRecyclerView

A handy RecyclerView can deal with all headers, footers, and loading shit.
Java
249
star
14

tiny-loader.js

A small loader that load CSS/JS in best way for page performance
JavaScript
206
star
15

raven-weapp

Sentry SDK for WeApp
JavaScript
159
star
16

show-me-the-code

TypeScript
151
star
17

gatling-dubbo

A gatling plugin for running load tests on Apache Dubbo(https://github.com/apache/incubator-dubbo) and other java ecosystem.
Scala
149
star
18

felint

A smart way to eslint and stylelint for front end
JavaScript
127
star
19

weapp-plugin-demo

有赞微商城所有小程序插件的演示demo
JavaScript
112
star
20

YouzanMobileSDK-Android

有赞云AppSDK是为移动端应用打造的电商交易系统,通过一个SDK便可以在APP内集成有赞提供的整个交易服务。
Kotlin
111
star
21

SigmaTableViewModel

A simple view model for building organized and scalable TableViews.
Objective-C
106
star
22

zanphp-doc

Zan PHP Framework doc
Python
99
star
23

zan_high_performance_mysql

mysql performance optimize book
98
star
24

httpfetch

对http请求进行封装的,通过对接口函数进行代理,实现优雅的http调用
Java
97
star
25

beeyz

beeyz
Java
85
star
26

sprite-loader

A image sprite loader for webpack.
JavaScript
83
star
27

fast-uglifyjs-plugin

hight performance uglify plugin for webpack
JavaScript
70
star
28

systemtap-toolkit

YouZan systemtap toolkit to online analyze on production
Perl
69
star
29

ngx_http_ipip_module

nginx http module for ipip.net
C
64
star
30

YouzanMobileSDK-iOS

有赞云AppSDK是为移动端应用打造的电商交易系统,通过一个SDK便可以在APP内集成有赞提供的整个交易服务。
Objective-C
61
star
31

zanhttpdemo

HTTP demo for Zan PHP Framework
PHP
57
star
32

go-nsq

Go
56
star
33

open-sdk-php

有赞云网关 SDK for PHP
PHP
54
star
34

php-nsq-client

php nsq client
PHP
38
star
35

nsqJavaSDK

nsq client for java
Java
37
star
36

vue-cli-template-vant

注意:本仓库适用于 vue-cli 2.0, vue-cli 3.0 请参考:https://youzan.github.io/vant/#/zh-CN/quickstart
JavaScript
35
star
37

youzan-sdk

Yet Another Node.js SDK for http://open.youzan.com
JavaScript
33
star
38

zent-kit

[DEPRACATED] React 组件库开发脚手架
JavaScript
28
star
39

vant-doc

本仓库已废弃,请使用 https://github.com/youzan/vant/tree/dev/packages/vant-cli
Vue
27
star
40

zan-installer

Youzan Zan Php Installer
PHP
26
star
41

zan-tool

Zan Node Web 框架配套开发工具
JavaScript
23
star
42

yz-cloud-boot

有赞云有容器应用PHP框架
PHP
21
star
43

open-sdk-node

有赞云网关 SDK for Node
JavaScript
20
star
44

vant-icons

本仓库已迁移至 https://github.com/youzan/vant/tree/dev/packages/vant-icons
CSS
18
star
45

zan-thrift

zan thrift代码生成工具
C++
17
star
46

create-utils

Build your own js library by running one command
JavaScript
15
star
47

ngx_http_html_sanitize_module

It's a nginx http module to sanitize HTML5 with whitelisted elements, whitelisted attributes and whitelisted CSS property
HTML
15
star
48

zanphp.io-server

Proudly build with Zan PHP Framework
CSS
13
star
49

open-sdk-Csharp

有赞云网关Api调用C# SDK
C#
12
star
50

spark-nsq-consumer

spark-nsq-consumer
Scala
10
star
51

zent-seed

[DEPRECATED] React 组件库项目模版,请配合 https://github.com/youzan/zent-kit 使用
JavaScript
8
star
52

yz_aerospike

youzan aerospike client branch
C
6
star
53

felint-config

Shell
6
star
54

zan-doc

PHP
5
star
55

zanphp.io

Zan PHP Framework official site
CSS
5
star
56

zan-utils

TypeScript
4
star
57

zantcpdemo

PHP
4
star
58

zanwebsocketdemo

PHP
4
star
59

extension-point-api

有赞云扩展点PHP桩代码
PHP
4
star
60

zanhttp-boilerplate

Youzan ZanPhp HTTP project Boilerplate
PHP
4
star
61

zent-beta

Zent beta 版文档网站
HTML
3
star
62

eslint-plugin-youzan

Eslint plugin for youzan
JavaScript
2
star
63

yz-cloud-boot-demo-app

有赞云有容器App定制PHP测试Demo工程
JavaScript
2
star
64

vue-cli-template-yzae

A vue-cli template for youzan app-engine developer, forked from vue-cli-template-vant.
JavaScript
1
star
65

youzan.github.io

HTML
1
star
66

zantcp-boilerplate

Youzan ZanPHP TCP project Boilerplate
PHP
1
star
67

cloud-connector-doc

云连接器文档
Shell
1
star
68

zan-snippets

Code snippets for various editors at Youzan
Shell
1
star
69

zan-user-guide

Python
1
star
70

zanwebsocket-boilerplate

PHP
1
star