清洁架构:如何在UI中反映数据层的变化

我正在尝试基于Android中的Bob叔叔Clean Architecture进行devise 。

问题:

我想解决的是如何使在一个存储库中生成的更改反映在应用程序的其他部分,如其他存储库或视图。

这个例子

我为这个例子devise了一个非常简单的例子。 请注意,边界界面已被删除,以保持图表小。

想象一下,一个显示video列表的应用程序(标题,thumnail和类似的计数),点击一个video,你可以看到细节(你可以喜欢/不喜欢的video)。

此外,该应用程序有统计系统,统计用户喜欢或不喜欢的video数量。

这个应用程序的主要类可以是:

对于video部分/模块: 在这里输入图像说明

对于统计部分/模块: 在这里输入图像说明

目标

现在想象一下,检查你的统计数据,然后浏览video列表,打开其中的细节,然后点击类似button。

之后类似的被发送到服务器,应用程序的几个元素应该知道的变化:

  • 当然,详细视图,应该更新与更改(这可以通过callback,所以没有问题)
  • video列表应更新给定video的“喜欢”数量
  • StatsRepository可能会在投票新video后更新/无效caching
  • 如果统计数据列表可见(想象一个分屏),它也应该显示更新的统计数据(或者至less接收事件以重新查询数据)

问题

什么是解决这种交stream的常见模式? 请尽可能完整地填写答案,指定事件的生成位置,通过应用程序传播的方式等。

注:赏金将被给予完整的答案

Related of "清洁架构:如何在UI中反映数据层的变化"

发布/订阅

通常,对于n:m通信(n个发送者可以向m个接收者发送消息,而所有发送者和接收者彼此不知道),则将使用发布/订阅模式 。 有很多库实现这样的通信风格,对于Java来说,例如在Guava库中有一个EventBus实现 。 对于应用内通信,这些库通常被称为EventBus或EventManager以及发送/接收事件

域名事件

假设您现在创build了一个事件VideoRatedEvent ,它表示用户喜欢或不喜欢video。 这些types的事件被称为域事件 。 事件类是一个简单的POJO,可能看起来像这样:

 class VideoRatedEvent { /** The video that was rated */ public Video video; /** The user that triggered this event */ public User user; /** True if the user liked the video, false if the user disliked the video */ public boolean liked; } 

派遣活动

现在,每当用户喜欢或不喜欢video时,您都需要发送VideoRatedEvent 。 使用番石榴,您只需传递一个实例化的事件对象来对象EventBus.post(myVideoRatedEvent) 。 理想情况下,这些事件是在您的域对象中生成的,并在持久化事务中进行调度(请参阅此博客文章以了解详细信息)。 这意味着,当你的领域模型状态是坚持的,事件被派遣。

事件监听器

在您的应用程序中,受事件影响的所有组件现在都可以侦听域事件。 在您的特定示例中, VideoDetailViewStatsRepository可能是StatsRepository事件侦听VideoRatedEvent 。 当然,您需要使用EventBus.register(Object)将这些注册到Guava EventBus。

这是我的个人5cents,可能与你的“清洁build筑”的例子关系不够。

我通常试图强制一种MVC的android的活动和片段,并使用发布/订阅通信。 作为组件,我有处理业务逻辑和数据状态的模型类。 它们的数据更改方法只能由控制器类来调用,而控制器类通常是活动类,同时也处理会话状态。 我使用片段来pipe理应用程序的不同视图部分以及这些片段下的视图(显然)。 所有片段都订阅一个或多个主题。 我使用我自己的简单的DataDistributionService处理不同的主题,从注册的发布者处获取消息并将消息转发给所有订阅者。 (部分受到OMG DDS的影响,但更为原始)。一个简单的应用程序只能有一个主题,例如“Main”。

视图交互(触摸等)的每个部分首先由其片段处理。 片段可能会改变一些东西而不发送通知。 例如,如果应用程序的其余部分不需要知道/反应,则切换呈现的数据元素的子范围。 否则片段将发布一个包含必要参数的ViewRequest(…)给DDS。

DDS广播该消息,并在某个时刻到达控制器。 这可以简单地作为主要活动或特定的控制器实例。 应该只有一个控制器,这样的请求只能处理一次。 控制器基本上有一长串请求处理代码。 当请求到达时,控制器调用模型中的业务逻辑。 该控制器还处理其他视图相关的事情,如排列视图(标签)或用户input(覆盖文件?)和其他东西,模型不应该知道,但影响(抛出新的NoOverWritePermissionException())的对话框

一旦模型改变完成,控制器决定是否发送更新通知。 (通常是这样)。 这样,模型类不需要监听或发送消息,只需要处理业务逻辑和一致的状态。 更新通知由运行“updateFromModel()”的片段广播和接收。

功效:
命令是全局的。 任何ViewRequest或其他types的请求都可以在任何可以访问DDS的地方发送。 片段不必提供监听器类,也不需要更高级的实例为其实例片段实现监听器。 如果一个新的片段不需要新的请求,它可以不加任何改变地添加到控制器类中。

模型类根本不需要了解通信。 保持一致的状态并处理所有的数据pipe理可能非常困难。 不需要消息处理或会话状态处理。 然而,这种模式可能不会被保护,以防止恶意的电话。 但是,这是一个普遍的问题,如果模型必须在某个时候提供参考,那么这个问题是无法避免的。 如果您的应用程序没有问题,只能通过复制/平面数据的模型。 但在某些时候,ArrayAdapter只需要访问他应该在gridview中绘制的位图。 如果你买不起拷贝,你总会有“观看对模型的呼唤”的风险。 不同的战场

更新电话可能太简单了。 如果一个片段的更新是昂贵的(OpenGL片段重新加载纹理…),你想有更详细的更新信息。 控制器可以发送更详细的通知,但是实际上不需要/能够知道模型的哪些部分完全改变了。 从模型发送更新笔记是丑陋的。 模型不仅需要实现消息传递,而且混合通知也会变得非常混乱。 控制器可以通过使用主题来分割更新通知和其他。 例如,您的video资源更改的特定主题。 这样片段可以决定他们订阅哪些主题。 除此之外,你想有一个模型,可以查询更改的值。 时间戳等我有一个应用程序,用户在canvas上绘制形状。 它们被渲染成位图,并被用作OpenGL视图中的纹理。 我当然不希望重新加载纹理,每次在GLViewFragment中调用“updateFromModel()”。

依赖规则:
可能一直不被尊重。 如果控制器处理一个标签开关,它可以简单地在一个TabHost上调用“seletTab()”,因此对外部圆有依赖性。 你可以把它变成一个消息,但它仍然是一个逻辑的依赖。 如果控制器部分必须组织视图的某些元素(在通过image-gallery-fragmen-tab加载图像后自动显示image-editor-fragment-tab),则无法完全避免依赖关系。 也许你可以通过对viewstatebuild模来完成它,并让你的视图部分从viewstate.currentUseCase组织起来,或者像这样。 但是如果你需要全局控制你的应用程序的视图,你会得到这个依赖规则的问题,我会说。 如果您尝试保存一些数据并且您的模型要求覆盖权限,该怎么办? 你需要为此创build一些UI。 再次依赖。 你可以发送一个消息给视图,并希望DialogFragment将其提取出来。 如果它存在于你的链接描述的非常模块化的世界。

实体:
是我的方法中的模型类。 这是非常接近你提供的链接。

用例:
我现在没有明确的模型。 Atm我正在编辑video游戏资产。 在一个片段中绘制形状,在另一个片段中应用着色值,在一个galleryfragment中保存/加载,导出到另一个片段的纹理图集……类似的东西。 我将添加用例作为某种请求子集。 基本上,一个用例作为一组规则,要求哪些命令被允许/要求/预期/被禁止等等。我会像交易一样build立它们,以便用例可以继续进行,可以完成,可以取消,甚至可以进行滚动背部。 例如,用例将定义保存新绘制的图像的顺序。 包括发布一个对话框来请求覆盖权限,如果权限不允许或超时,则回滚。 但用例的定义方式有很多种。 一些应用程序有一个小时的活动用户交互的用例,一些应用程序有50个用例只是为了从一个atm获得收益。 ;)

接口适配器:
这里有点复杂。 对我来说,这似乎是非常高的水平android应用程序。 它指出“接口适配器环包含GUI的整个MVC体系结构”。 我不能真正包裹我的头。 也许你正在构build比我更复杂的应用程序。

框架和驱动程序:
不知道该怎么想这个。 “networking是一个细节,数据库是一个细节…”,graphics在这个环中也包含“UI”。 我的小脑袋太多了

让我们检查另一个“断言”
独立于框架。 这个架构并不依赖于一些function强大的软件库的存在。 这允许你使用这样的框架作为工具,而不是把你的系统塞进有限的约束。
嗯,好吧,如果你运行你自己的架构,那就是你所得到的。

可testing。 业务规则可以在没有UI,数据库,Web服务器或任何其他外部元素的情况下进行testing。
正如在我的方法模型类既不知道控制器或意见,也没有消息传递。 我们可以单独testing这些类的状态一致性。

独立的用户界面。 用户界面可以很容易地更改,而无需更改系统的其余部分。 例如,Web UI可以replace为控制台UI,而无需更改业务规则。
再次有点矫揉造作是不是? 独立是的。 在我的方法中,只要不需要在更高层的地方进行明确的处理,就可以添加或删除碎片。 但是用一个控制台UI取代一个Web用户界面,让系统像以前一样运行,这是对build筑怪胎的一个梦想。 一些UI元素是所提供的服务的组成部分。 当然,我可以很容易地交换一个控制台绘图片段的canvas绘图片段,或者一个“拍摄控制台”片段的经典照片片段,但这并不意味着应用程序仍然可以工作。 从技术上讲,我的方法很好。 如果你实现了一个ascii控制台的video播放器,你可以在那里渲染video,没有其他应用程序的一部分必须在意。 然而,这可能是由于控制器支持的请求集不能很好地与新的控制台UI保持一致,或者用例不是针对需要通过控制台界面访问video的顺序。 这种观点并不总是不重要的呈现奴隶,许多build筑大师喜欢把它看成是。

独立于数据库。 您可以换出Oracle或SQL Server,Mongo,BigTable,CouchDB或其他。 您的业​​务规则不绑定到数据库。
所以啊? 这与你的架构直接相关? 使用正确的适配器和抽象,你可以在一个hello世界的应用程序。

独立于任何外部机构。 事实上,你的商业规则根本就不了解外面的世界。
同样在这里。 如果你想要模块化的独立代码然后写它。 很难说具体的事情。