二、架構(gòu)-MVI

Android Clean Architecture MVI Boilerplate

Note: This is a fork of our original Clean Architecture Boilerplate, except in this repo we have switched out the MVP approach found in the presentation layer to now use ViewModels from the Android Architecture Components Library.
The caching layer now also uses Room.

注意:這是我們最初的Clean Architecture Boilerplate的一個分支,在這個 repo 中,我們已經(jīng)將表示層中的 MVP 方法切換為現(xiàn)在使用 Android 架構(gòu)組件庫中的 ViewModel。緩存層現(xiàn)在也使用 Room。

Welcome ?? We hope this boilerplate is not only helpful to other developers, but also that it helps to educate in the area of architecture. We created this boilerplate for a few reasons:

歡迎??我們希望這個樣板不僅對其他開發(fā)人員有所幫助,而且有助于在架構(gòu)領(lǐng)域進(jìn)行教育。我們創(chuàng)建這個樣板有幾個原因:

    1. To experiment with modularisation
      嘗試模塊化
    1. To experiment with the Android Architecture Components
      嘗試 Android 架構(gòu)組件
    1. To share some approaches to clean architecture, especially as we've been talking a lot about it
      分享一些純凈的架構(gòu)方法,尤其是在我們談?wù)撍臅r候
    1. To use as a starting point in future projects where clean architecture feels appropriate
      在未來的項目中使用純凈的架構(gòu)是一個合適的起點

It is written 100% in Kotlin with both UI and Unit tests - we will also be keeping this up-to-date as libraries change!
它是 100% 用 Kotlin 編寫的,帶有 UI 和單元測試——隨著庫的變化,我們也會保持最新!

Disclaimer 免責(zé)聲明

Note: The use of clean architecture may seem over-complicated for this sample project. However, this allows us to keep the amount of boilerplate code to a minimum and also demonstrate the approach in a simpler form.
注意:對于這個示例項目,使用純凈的架構(gòu)可能看起來過于復(fù)雜。但是,這使我們能夠?qū)影宕a的數(shù)量保持在最低限度,并以更簡單的形式演示該方法。

Clean Architecture will not be appropriate for every project, so it is down to you to decide whether or not it fits your needs ??
純凈架構(gòu)并不適合每個項目,因此由您決定它是否適合您的需求??

Languages, libraries and tools used 使用的語言、庫和工具

Requirements 要求

Architecture 架構(gòu)

The architecture of the project follows the principles of Clean Architecture. Here's how the sample project implements it:
運行時的示例應(yīng)用程序?qū)⑾蚰@示所有 Bufferoos(Buffer 團隊成員?。┑暮唵瘟斜?。

architecture.png

The sample app when run will show you a simple list of all the Bufferoos (Buffer team members!).
這個項目運行起來將會展示所有的項目貢獻(xiàn)者的簡介列表

device_screenshot.png

Let's look at each of the architecture layers and the role each one plays :)
讓我們看看每一個架構(gòu)層以及每一層所扮演的角色:)


ui.png

User Interface(用戶頁面)

This layer makes use of the Android Framework and is used to create all of our UI components to display inside of the Browse Activity. The layer receives its data from the Presentation layer and when retrieved, the received models are mapped using the Bufferoo Mapper so that the model can be mapped to this layer's interpretation of the Bufferoo instance, which is the BufferooViewModel. The Activity makes use of the BrowseBufferoosViewModel to retrieve data.

該層使用 Android 框架,用于創(chuàng)建我們所有的 UI 組件以顯示在Browse Activity中。該層從表示層接收其數(shù)據(jù),并在檢索到時,使用Bufferoo Mapper映射接收到的模型,以便可以將模型映射到該層對 Bufferoo 實例的解釋,即BufferooViewModel。Activity 使用BrowseBufferoosViewModel來檢索數(shù)據(jù)。

Presentation(表示層)

This layer's responsibility is to handle the presentation of the User Interface, but at the same time knows nothing about the user interface itself. This layer has no dependence on the Android Framework, it is a pure Kotlin module. Each ViewModel class that is created implements the ViewModel class found within the Architecture components library. This ViewModel can then be used by the UI layer to communicate with UseCases and retrieve data. The BrowseBufferoosViewModel returns an instance of a BrowseUiModel which contains data that can be used by the UI.

這一層的職責(zé)是處理用戶界面的呈現(xiàn),但同時對用戶界面本身一無所知。該層不依賴 Android Framework,是一個純 Kotlin 模塊。創(chuàng)建的每個 ViewModel 類都實現(xiàn)了在架構(gòu)組件庫中找到的 ViewModel 類。然后,UI 層可以使用此 ViewModel 與 UseCases 通信并檢索數(shù)據(jù)。BrowseBufferoosViewModel返回一個 BrowseUiModel 的實例,其中包含UI 可以使用的數(shù)據(jù)。

The ViewModels use an instance of a FlowableUseCase from the Domain layer to retrieve data. Note here that there is no direct name reference to the UseCase that we are using - we do inject an instance of the GetBufferoos UseCase, however.

ViewModel 使用來自Domain層的FlowableUseCase實例來檢索數(shù)據(jù)。請注意,我們正在使用的 UseCase 沒有直接的名稱引用 - 但是,我們確實注入了GetBufferoos UseCase 的實例。

The ViewModel receives data from the Domain layer in the form of a Bufferoo. These instances are mapped to instance of this layers model, which is a BufferooView using the BufferooMapper.

ViewModel 以Bufferoo的形式從 Domain 層接收數(shù)據(jù)。這些實例被映射到這個層模型的實例,它是一個使用 BufferooMapper 的BufferooView。

Domain(域)

The domain layer responsibility is to simply contain the UseCase instance used to retrieve data from the Data layer and pass it onto the Presentation layer. In our case, we define a GetBufferoos - this use case handles the subscribing and observing of our request for data from the BufferooRepository interface. This UseCase extends the FlowableUseCase base class - therefore we can reference it from outer layers and avoid a direct reference to a specific implementation.

領(lǐng)域?qū)拥穆氊?zé)是簡單地包含用于從數(shù)據(jù)層檢索數(shù)據(jù)并將其傳遞到表示層的 UseCase 實例。在我們的例子中,我們定義了一個GetBufferoos——這個用例處理我們對來自 BufferooRepository 接口的數(shù)據(jù)請求的訂閱和觀察。這個 UseCase 擴展了FlowableUseCase基類——因此我們可以從外層引用它,避免直接引用特定的實現(xiàn)。

The layer defines the Bufferoo class but no mapper. This is because the Domain layer is our central layer, it knows nothing of the layers outside of it so has no need to map data to any other type of model.

該層定義了Bufferoo類但沒有映射器。這是因為 Domain 層是我們的中心層,它對它之外的層一無所知,因此不需要將數(shù)據(jù)映射到任何其他類型的模型。

The Domain layer defines the BufferooRepository interface which provides a set of methods for an external layer to implement as the UseCase classes use the interface when requesting data.

Domain 層定義了BufferooRepository接口,它為外部層提供了一組方法來實現(xiàn),因為 UseCase 類在請求數(shù)據(jù)時使用該接口。

data.png

Data(數(shù)據(jù)層)

The Data layer is our access point to external data layers and is used to fetch data from multiple sources (the cache and network in our case). It contains an implementation of the BufferooRepository, which is the BufferooDataRepository. To begin with, this class uses the BufferooDataStoreFactory to decide which data store class will be used when fetching data - this will be either the BufferooRemoteDataStore or the BufferooCacheDataStore - both of these classes implement the BufferooDataStore repository so that our DataStore classes are enforced.

數(shù)據(jù)層是我們對外部數(shù)據(jù)層的訪問點,用于從多個來源(在我們的案例中為緩存和網(wǎng)絡(luò))獲取數(shù)據(jù)。它包含 BufferooRepository 的實現(xiàn),即BufferooDataRepository。首先,此類使用BufferooDataStoreFactory來決定在獲取數(shù)據(jù)時將使用哪個數(shù)據(jù)存儲類 - 這將是BufferooRemoteDataStoreBufferooCacheDataStore - 這兩個類都實現(xiàn)了BufferooDataStore存儲庫,以便強制執(zhí)行我們的 DataStore 類。

Each of these DataStore classes also references a corresponding BufferooCache and BufferooRemote interface, which is used when requesting data from an external data source module.

這些 DataStore 類中的每一個還引用了相應(yīng)的BufferooCacheBufferooRemote接口,用于從外部數(shù)據(jù)源模塊請求數(shù)據(jù)。

This layers data model is the BufferooEntity. Here the BufferooMapper is used to map data to and from a Bufferoo instance from the domain layer and BufferooEntity instance from this layer as required.

該層數(shù)據(jù)模型是BufferooEntity。這里BufferooMapper用于根據(jù)需要將數(shù)據(jù)映射到域?qū)拥?Bufferoo 實例和該層的 BufferooEntity 實例。

Remote(服務(wù)器層)

The Remote layer handles all communications with remote sources, in our case it makes a simple API call using a Retrofit interface. The BufferooRemoteImpl class implements the BufferooRemote interface from the Data layer and uses the BufferooService to retrieve data from the API.

Remote 層處理與遠(yuǎn)程源的所有通信,在我們的例子中,它使用 Retrofit 接口進(jìn)行簡單的 API 調(diào)用。BufferooRemoteImpl類從 Data 層實現(xiàn)BufferooRemote接口,并使用BufferooService從 API 中檢索數(shù)據(jù)。

The API returns us instances of a BufferooModel and these are mapped to BufferooEntity instance from the Data layer using the BufferooEntityMapper class.

API 向我們返回BufferooModel 的實例,這些實例使用 BufferooEntityMapper從數(shù)據(jù)層映射到 BufferooEntity 實例。

Cache(緩存)

The Cache layer handles all communication with the local database which is used to cache data.

緩存層處理與用于緩存數(shù)據(jù)的本地數(shù)據(jù)庫的所有通信

The data model for this layer is the CachedBufferoo and this is mapped to and from a BufferooEntity instance from the Data layer using the BufferooEntityMapper class.

該層的數(shù)據(jù)模型是CachedBufferoo ,它使用BufferooEntityMapper類映射到數(shù)據(jù)層的 BufferooEntity 實例和從該實例映射。

Conclusion

We will be happy to answer any questions that you may have on this approach, and if you want to lend a hand with the boilerplate then please feel free to submit an issue and/or pull request.

我們很樂意回答您對這種方法可能有的任何問題,如果您想幫助樣板,請隨時提交問題和/或拉取請求??

Again to note, use Clean Architecture where appropriate. This is example can appear as over-architectured for what it is - but it is an example only. The same can be said for individual models for each layer, this decision is down to you. In this example, the data used for every model is exactly the same, so some may argue that "hey, maybe we don't need to map between the presentation and user-interface layer". Or maybe you don't want to modularise your data layer into data/remote/cache and want to just have it in a single 'data' module. That decision is down to you and the project that you are working on ????

再次注意,在適當(dāng)?shù)牡胤绞褂们鍧嵓軜?gòu)。這個例子可能看起來像是過度架構(gòu)的——但這只是一個例子。對于每一層的單個模型也可以這樣說,這個決定取決于你。在這個例子中,每個模型使用的數(shù)據(jù)都是完全相同的,所以有些人可能會爭辯說“嘿,也許我們不需要在表示層和用戶界面層之間進(jìn)行映射”?;蛘?,也許您不想將數(shù)據(jù)層模塊化為數(shù)據(jù)/遠(yuǎn)程/緩存,而只想將其放在單個“數(shù)據(jù)”模塊中。這個決定取決于你和你正在進(jìn)行的項目????

Thanks(感謝)

A special thanks to the authors involved with these two repositories, they were a great resource during our learning!
特別感謝參與這兩個存儲庫的作者,他們是我們學(xué)習(xí)過程中的重要資源!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容