ReactiveX,关于移动应用架构和层

ReactiveX, about mobile app architecture and layers

我正在学习 Rx(尤其是 RxSwift),我对架构、层和边界有疑问。

我习惯了分层架构(数据、域、表示),通常在 MVP 或 VIPER 上。对于这个项目,我使用的是 MVVM,这是 Reactive 应用程序的推荐架构。这些是我目前的合作者:

**** Presentation ******************************************

                 ________________________
                |                        |
                |   GameViewController   |
                |                        |
                |      ____________      |
                |     |            |     |
                |     | BoardView  |     |
                |     |____________|     |
                |                        |
                |________________________|
                             |
                             |
                            \|/
                 ________________________
                |                        |
                |     GameViewModel      |
                |________________________|
                             |
**** Domain **************** | *****************************
                            \|/
                 ________________________
                |                        |
                |     GameController     |
                |________________________|

当用户点击(移动)时,BoardView 发出一个事件,GameViewController 正在观察该事件,它调用 GameViewModel 中的一个方法,该方法与 GameController 检查移动是否正确,然后发出另一个事件,链中的每个事件都在观察,最后 BoardView 根据移动的正确性绘制它的东西。

我的问题是,这个流程正确吗?我必须坚持这种做事方式还是有一种更适合的 Reactive 方式?例如,也许 BoardView 可以在不涉及视图控制器的情况下直接与视图模型对话,并且没有边界中断也没有 "violation of rules"。

我对 Rx 更好的架构有点迷茫,MVVM 很简单,但要让它可靠,你必须创建更多的协作者,然后可观察链可能有点过度设计。

任何帮助将不胜感激!谢谢:)

通常,当您采用数据驱动架构(例如 MVVM)时,您会有多个 Service 对象来满足您的业务逻辑。在 Rx 世界中,你订阅你的服务,服务将 DTOs 推送到你的控制器中,你将它映射到 ViewModel。如果你打算 MVVM,我真的建议你查看 SOA,因为 ViewModel 只是为了成为 DTO 的装饰器,可以被不同的观点所接受。您的业​​务逻辑应包含在服务中。

Rx 只是推送接口的集合以及它们随时间的转换,使您的对象更加封装和自给自足。 Rx 方法允许您的对象在业务逻辑需要时提供数据,而不是在控制器决定时提取数据。我认为在使用 Rx 时,不可能为每个业务逻辑场景在 MVVMVIPER 内定义 100% 的拟合规则。只需使用常识,看看什么时候您的对象将信息推送给接收者而不是在特定时间拉取它是有意义的。

旁注:我真的建议您远离任何数据驱动的对象定义方法(无论是 MVVM 还是 VIPER)。 Here is why.

在编写良好的响应式应用程序中,您的逻辑往往会封装在许多无状态(静态)函数中,而不是对象中。从而使您的代码更具声明性。

例如,您的 GameViewModel 应该接受它将观察的多个输入可观察对象,并为要订阅的视图生成多个输出可观察对象。

struct MyViewModel {
    let output: Observable<OutState> 
    init(input: Observable<InState>) {
        output = input.map {
            // transform input state into output state
        }
    }
}

注意上面的转换是一个纯函数。另请注意,对象本身是多余的。它可以很容易地成为一个函数:

func myOutput(input: Observable<InState>) -> Observable<OutState> {
    return input.map {
        // transform input state into output state
    }
}

当然,这个转换块本身可以是一个函数。

func transform(in: InState) -> OutState {
}

它非常容易测试并且自然地封装了您的应用程序的特定用例。