基于协议的业务模块路由管理

简介: 关于业务模块与路由权限的管理方案

概述

这是一个关于业务模块与路由权限的管理方案,用于增强在模块化架构场景下,业务模块的健壮性。

  • 通过对App生命周期的转发,来解除App入口与业务模块管理逻辑的耦合。

  • 通过协议来管理API路由,通过注册制实现API的服务发现。

业务模块

业务模块注册

重新组织后,业务模块的管理会变得松散,容易实现插拔复用。

协议

public protocol SpaceportModuleProtocol {
   
   
    var loaded: Bool {
   
    get set}
    /// 决定模块的加载顺序,数字越大,优先级越高
    /// - Returns: 默认优先级为1000
    static func modulePriority() -> Int
    /// 加载
    func loadModule()
    /// 卸载
    func unloadModule()

    /// UIApplicationDidFinishLaunching
    func applicationDidFinishLaunching(notification: Notification)
    /// UIApplicationWillResignActive
    func applicationWillResignActive(notification: Notification)
    /// UIApplicationDidBecomeActive
    func applicationDidBecomeActive(notification: Notification)
    /// UIApplicationDidEnterBackground
    func applicationDidEnterBackground(notification: Notification)
    /// UIApplicationWillEnterForeground
    func applicationWillEnterForeground(notification: Notification)
    /// UIApplicationWillTerminate
    func applicationWillTerminate(notification: Notification)
}

特性

  • 实现模块加载/卸载保护,模块只会加载/卸载一次。
  • 同一个模块的注册是替换制,新模块会替代旧模块。
  • 提供模块优先级配置,优先级高的模块会更早加载并响应Application的生命周期回调。

最佳实践

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
   
   
    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
   
   
        setupModules()
                // ......
        return true
    }

    func setupModules() {
   
   
        var modules: [SpaceportModuleProtocol] = [
            LoggerModule(),             // 4000
            NetworkModule(),            // 3000
            FirebaseModule(),           // 2995
            RouterModule(),             // 2960
            DynamicLinkModule(),        // 2950
            UserEventRecordModule(),    // 2900
            AppConfigModule(),          // 2895
            MediaModule(),              // 2800
            AdModule(),                 // 2750
            PurchaseModule(),           // 2700
            AppearanceModule(),         // 2600
            AppstoreModule(),           // 2500
            MLModule()                  // 2500
        ]
#if DEBUG
        modules.append(DebugModule())   // 2999
#endif
        Spaceport.shared.registerModules(modules)
        Spaceport.shared.enableAllModules()
    }
}

协议路由

协议路由

通过路由的协议化管理,实现模块/组件之间通信的权限管理。

  • 服务方通过Router Manger注册API协议,可以根据场景提供不同的协议版本。

  • 业务方通过Router Manager发现并使用API协议。

最佳实践

实现API协议

protocol ResultVCRouterAPI {
   
   
    @MainActor func vc(from: ResultVCFromType, project: Project) throws -> ResultVC
    @MainActor func vcFromPreview(serviceType: EnhanceServiceType, originalImage: UIImage, enhancedImage: UIImage) async throws -> ResultVC
}

class ResultVCRouter: ResultVCRouterAPI {
   
   
    @MainActor func vc(from: ResultVCFromType, project: Project) throws -> ResultVC {
   
   
        let vc = ResultVC()
        vc.modalPresentationStyle = .overCurrentContext
        try vc.vm.config(project: project)
        vc.vm.fromType = from
        return vc
    }

    @MainActor func vcFromPreview(serviceType: EnhanceServiceType, originalImage: UIImage, enhancedImage: UIImage) async throws -> ResultVC {
   
   
        let vc = ResultVC()
        vc.modalPresentationStyle = .overCurrentContext
        try await vc.vm.config(serviceType: serviceType, originalImage: originalImage, enhancedImage: enhancedImage)
        return vc
    }
}

注册API协议

public class RouterManager: SpaceportRouterService {
   
   
    public static let shared = RouterManager()
    private override init() {
   
   }
    static func API<T>(_ key: TypeKey<T>) -> T? {
   
   
        return shared.getRouter(key)
    }
}

class RouterModule: SpaceportModuleProtocol {
   
   
    var loaded = false
    static func modulePriority() -> Int {
   
    return 2960 }
    func loadModule() {
   
   
          // 注册API
        RouterManager.shared.register(TypeKey(ResultVCRouterAPI.self), router: ResultVC())
    }
    func unloadModule() {
   
    }
}

使用协议

// 通过 RouterManager 获取可用API
guard let api = RouterManager.API(TypeKey(ResultVCRouterAPI.self)) else {
   
    return }
let vc = try await api.vcFromPreview(serviceType: .colorize, originalImage: originalImage, enhancedImage: enhancedImage)
self.present(vc, animated: false)

总结

我们的业务向模块化、组件化架构演化的过程中,逐步出现跨组件调用依赖嵌套,插拔困难等问题。

通过抽象和简化,设计了这个方案,作为后续业务组件化的规范之一。通过剥离业务模块的生命周期,以及统一通信的方式,可以减缓业务增长带来的代码劣化问题。

目录
相关文章
|
5月前
|
Java Sentinel Spring
网关修改响应码,拯救业务不规范设计
本文探讨了在一个未遵循HTTP标准规范的项目中遇到的问题及解决方案。
|
8月前
|
JavaScript 中间件 PHP
中间件应用程序路由和分发
【5月更文挑战第13天】中间件应用程序路由和分发
55 2
|
网络协议 安全 Unix
虚拟路由和转发 (VRF) 表上下文中的多点标签分发协议带内信令
本文档是 Internet 工程任务组 (IETF) 的产品。它代表了 IETF 社区的共识。它已接受公众审查,并已获互联网工程指导小组 (IESG) 批准出版。有关 Internet 标准的更多信息,请参见 RFC 5741 的第 2 节。
455 0
|
JSON NoSQL JavaScript
快速搭建一个网关服务,动态路由、鉴权的流程,看完秒会(含流程图)
快速搭建一个网关服务,动态路由、鉴权的流程,看完秒会(含流程图)
|
网络协议 Linux 网络安全
如何实现SCTP多归属链路对接
最近完成了贝尔及华为软交换的SCTP 多归属链路对接。由于网络上对于多归属链路介绍的资料特别少。能看到的一些资料介绍,但是说明的不详细,大都是Demo性质不能完全商用。以客户端为例子,概括如下,首先绑定本端两个IP,然后绑定交换的主用Path。然后将该主用的Path的IP设置为PrimaryPath。如果对端交换不支持BEAT心跳消息,就不要发送该BEAT消息。 现在分享下我的具体的经验心得。
如何实现SCTP多归属链路对接
外部BGP的基础配置(下)
文章目录 系列文章目录 实验目的 实验拓扑 实验步骤
151 0
外部BGP的基础配置(下)
|
JSON NoSQL Java
快速搭建一个网关服务,动态路由、鉴权看完就会(含流程图)
快速搭建一个网关服务,动态路由、鉴权看完就会(含流程图)
快速搭建一个网关服务,动态路由、鉴权看完就会(含流程图)
|
Web App开发 缓存 网络协议
跨网络通信及路由知识入门
一、规划IP地址 二、跨网络通信 三、路由表的关键组成 四、路由环境的生成
跨网络通信及路由知识入门
|
域名解析 Cloud Native Serverless
服务路由管理
Knative 默认会为每一个 Service 生成一个域名,并且 Istio Gateway 要根据域名判断当前的请求应该转发给哪个 Knative Service。Knative 默认使用的主域名是 example.com,这个域名是不能作为线上服务的。本文我首先介绍一下如何修改 默认主域名,然后再深入一层介绍如何添加自定义域名以及如何根据 path 关联到不同的 Knative Service。
862 0
服务路由管理