Model-View-ViewModel 模式
注意
本電子書於 2017 年春季出版,此後尚未更新。 這本書中有很多仍然有價值的,但一些材料已經過時。
開發人員 Xamarin.Forms 體驗通常牽涉到在 XAML 中建立使用者介面,然後新增可在使用者介面上運作的程式代碼後置。 隨著應用程式修改,並擴大大小和範圍,可能會發生複雜的維護問題。 這些問題包括 UI 控制項與商務邏輯之間的緊密結合,這會增加 UI 修改的成本,以及單元測試這類程式碼的困難。
Model-View-ViewModel (MVVM) 模式有助於清除應用程式的商業和呈現邏輯與其使用者介面(UI)。 維護應用程式邏輯與UI之間的全新區隔有助於解決許多開發問題,並讓應用程式更容易測試、維護和發展。 它也可以大幅改善程式代碼重複使用的機會,並允許開發人員和UI設計工具在開發應用程式各自的部分時更輕鬆地共同作業。
MVVM 模式
MVVM 模式有三個核心元件:模型、檢視和檢視模型。 每個元件的用途各不相同。 圖 2-1 顯示三個元件之間的關聯性。
圖 2-1:MVVM 模式
除了瞭解每個元件的責任之外,也請務必瞭解彼此互動的方式。 總之,檢視「知道」檢視模型,而檢視模型「知道」模型,但模型不知道檢視模型,而檢視模型不知道檢視。 因此,檢視模型會隔離檢視與模型,讓模型獨立於檢視外演進。
使用 MVVM 模式的優點如下:
- 如果有封裝現有商業規則的現有模型實作,則變更它可能會很困難或有風險。 在此案例中,檢視模型可作為模型類別的配接器,並可讓您避免對模型程式代碼進行任何重大變更。
- 開發人員可以建立檢視模型和模型的單元測試,但不需要使用檢視。 檢視模型的單元測試可以運用和檢視所用完全相同的功能。
- 只要檢視完全在 XAML 中實作,應用程式 UI 就可以重新設計,而不需要觸及程序代碼。 如此,新版本的檢視應可使用現有的檢視模型。
- 設計工具與開發人員可以在開發程式期間獨立且同時在其元件上工作。 設計人員可以著重在檢視,而開發人員專心處理檢視模型和模型元件。
有效使用MVVM的關鍵在於瞭解如何將應用程式程式代碼分解成正確的類別,以及了解類別的互動方式。 下列各節討論 MVVM 模式中每個類別的責任。
檢視
檢視負責定義使用者在畫面上看到的內容結構、版面配置和外觀。 理想情況下,每個檢視都是使用 XAML 定義,具有不包含商務邏輯的有限程式碼後置。 但有時候,程式碼後置可能包含 UI 邏輯,以實作難以利用 XAML 表現的視覺行為,例如動畫。
在 Xamarin.Forms 應用程式中,檢視通常是 Page
衍生或 ContentView
衍生類別。 不過,檢視也可以用資料範本表示,指定顯示時要以視覺化方式呈現物件所用的 UI 元素。 以資料範本為檢視沒有任何程式碼後置,旨在繫結至特定的檢視模型類型。
提示
避免在程式碼後置中啟用與停用 UI 元素。 請確定檢視模型負責定義會影響檢視顯示某些層面的邏輯狀態變更,例如命令是否可用,或指示作業擱置中。 因此,請透過繫結至檢視模型屬性來啟用與停用 UI 元素,不要在程式碼後置中啟用與停用 UI 元素。
有數個選項可在檢視模型上執行程式碼,以在檢視上回應互動,例如按一下的按鈕或選取的項目。 如果控件支援命令,控件的 Command
屬性可以系結至 ICommand
檢視模型上的屬性。 叫用控制項的命令時,會執行檢視模型中的程式碼。 除了命令之外,行為也可以附加至檢視中的物件,並可接聽要叫用的命令或要引發的事件。 作為回應,行為接著可以在檢視模型上叫用 ICommand
,或檢視模型上的方法。
ViewModel
檢視模型會實作檢視可繫結資料的屬性和命令,並透過變更通知事件通知檢視任何狀態變更。 檢視模型提供的屬性和命令會定義 UI 所提供的功能,但檢視會決定如何顯示該功能。
提示
使用非同步作業讓 UI 保持回應。 行動應用程式應將UI線程解除封鎖,以改善使用者對效能的看法。 因此,在檢視模型中,針對 I/O 作業使用非同步方法,並引發事件以非同步方式通知檢視屬性變更。
檢視模型也負責協調檢視與任何所需模型類別的互動。 檢視模型與模型類別之間通常有一對多關聯性。 檢視模型可能選擇直接向檢視公開模型類別,讓檢視中的控制項可以直接在類別上繫結資料。 在此情況下,模型類別即必須設計為支援資料繫結和變更通知事件。
每個檢視模型都會使用檢視可輕易取用的格式提供模型資料。 為此,檢視模型有時會執行資料轉換。 將此資料轉換放在檢視模型中是個不錯的主意,因為它提供檢視可繫結的屬性。 例如,檢視模型可能會結合兩個屬性的值,讓檢視更容易顯示。
提示
將資料轉換集中在轉換層。 您也可以使用轉換器作為坐落在檢視模型與檢視之間的另一個資料轉換層。 例如,當資料需要檢視模型未提供的特殊格式時,這就很有必要了。
為使檢視模型加入到與檢視的雙向資料繫結,其屬性必須引發 PropertyChanged
事件。 檢視模型實作 INotifyPropertyChanged
介面,並在屬性變更時引發 PropertyChanged
事件,藉以滿足此需求。
若為集合,則提供了檢視易讀取的 ObservableCollection<T>
。 此集合會實作集合變更通知,讓開發人員不必對集合實作 INotifyCollectionChanged
介面。
模型
模型類別是封裝應用程式資料的非視覺類別。 因此,您可以將模型視為代表應用程式定義域的模型,這通常包含資料模型以及商業和驗證邏輯。 模型物件範例包括資料傳輸物件 (DTO)、簡單的 CLR 物件 (POCO),以及產生的實體和 Proxy 物件。
模型類別通常會與封裝資料存取和快取的服務或存放庫搭配使用。
將檢視模型連接到檢視
檢視模型可以使用 的數據系結功能 Xamarin.Forms連接到檢視。 有許多方法可用來建構檢視和檢視模型,並在執行階段建立它們彼此的關聯性。 這些方法分為兩個類別,稱為檢視優先組合,以及檢視模型優先組合。 檢視優先組合和檢視模型優先組合該如何選擇,是喜好設定和複雜度的問題。 不過,所有方法都有共同的目標,亦即讓檢視將檢視模型指派給其 BindingCoNtext 屬性。
使用檢視優先組合,應用程式在概念上是由連線到其所相依之檢視模型的檢視所組成。 此方法的最大優點是容易建構結合不緊密的可單元測試應用程式,因為檢視模型本身和檢視不相依。 而遵循應用程式的視覺結構,也很容易了解應用程式的結構,不需追蹤程式碼執行,就能了解類別的建立和關聯方式。 此外,檢視第一個建構與巡覽時負責建構頁面的導覽系統一致 Xamarin.Forms ,這會讓檢視模型第一個組合複雜且與平台不對齊。
使用檢視模型第一個組合時,應用程式在概念上是由檢視模型所組成,服務負責尋找檢視模型的檢視。 檢視模型優先組合對部分開發人員而言更自然,因為他們可以忽略檢視建立,專注於應用程式的邏輯非 UI 結構。 此外,這種組合還允許其他檢視模型建立檢視模型。 不過,這種方法通常很複雜,而且很難瞭解應用程式的各個部分如何建立和關聯。
提示
讓檢視模型和檢視保持獨立。 將檢視繫結至資料來源的屬性,應該是檢視在其對應檢視模型上的主體相依性。 具體而言,請勿從檢視模型參考檢視類型,例如 Button
和 ListView
。 遵循這裡列述的原則,即可隔離測試檢視模型,從而限制範圍以降低軟體瑕疵的可能性。
下列各節會討論連線檢視模型和檢視的主要方法。
以宣告方式建立檢視模型
最簡單的方法是讓檢視以宣告方式具現化其在 XAML 中的對應檢視模型。 建構檢視時,也會建構對應的檢視模型物件。 下列程式碼範例會示範這個方法:
<ContentPage ... xmlns:local="clr-namespace:eShop">
<ContentPage.BindingContext>
<local:LoginViewModel />
</ContentPage.BindingContext>
...
</ContentPage>
建立 ContentPage
時,會自動建構 LoginViewModel
執行個體,並設定為檢視的 BindingContext
。
依檢視排列的這種檢視模型宣告式建構和指派,優點就是簡單,但缺點則是檢視模型中需要有預設 (無參數) 建構函式。
以程序設計方式建立檢視模型
檢視可以有程序代碼後置檔案中的程式代碼,導致將檢視模型指派給其 BindingContext
屬性。 這通常會在檢視的建構函式中完成,如下列程式碼範例所示:
public LoginView()
{
InitializeComponent();
BindingContext = new LoginViewModel(navigationService);
}
在檢視的程式碼後置中以程式設計方式建構及指派檢視模型,優點就是簡單。 不過,此方法的主要缺點是檢視需要為檢視模型提供所有必要相依性。 使用相依性插入容器有助於維護檢視和檢視模型之間的鬆散結合。 如需詳細資訊,請參閱 相依性插入。
建立定義為數據範本的檢視
檢視可以定義為數據範本,並與檢視模型類型相關聯。 數據範本可以定義為資源,也可以內嵌在顯示檢視模型的控件內。 控件的內容是檢視模型實例,而數據範本則用來以可視化方式表示它。 這項技術是先具現化檢視模型的情況範例,後面接著建立檢視。
使用檢視模型定位器自動建立檢視模型
檢視模型定位器是一種自定義類別,可管理檢視模型的具現化及其與檢視的關聯。 在 eShopOnContainers 行動應用程式中,類別 ViewModelLocator
具有附加屬性 AutoWireViewModel
,用來將檢視模型與檢視產生關聯。 在檢視的 XAML 中,這個附加屬性設定為 true,表示檢視模型應該自動連接到檢視,如下列程式代碼範例所示:
viewModelBase:ViewModelLocator.AutoWireViewModel="true"
屬性 AutoWireViewModel
是初始化為 false 的可系結屬性,而且當其值變更 OnAutoWireViewModelChanged
事件處理程式時,就會呼叫它。 這個方法會解析檢視的檢視模型。 下列程式代碼範例示範如何達成此目的:
private static void OnAutoWireViewModelChanged(BindableObject bindable, object oldValue, object newValue)
{
var view = bindable as Element;
if (view == null)
{
return;
}
var viewType = view.GetType();
var viewName = viewType.FullName.Replace(".Views.", ".ViewModels.");
var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
var viewModelName = string.Format(
CultureInfo.InvariantCulture, "{0}Model, {1}", viewName, viewAssemblyName);
var viewModelType = Type.GetType(viewModelName);
if (viewModelType == null)
{
return;
}
var viewModel = _container.Resolve(viewModelType);
view.BindingContext = viewModel;
}
方法 OnAutoWireViewModelChanged
會嘗試使用慣例型方法解析檢視模型。 此慣例假設:
- 檢視模型與檢視類型位於相同的元件中。
- 檢視位於 中。檢視子命名空間。
- 檢視模型位於 中。ViewModels 子命名空間。
- 檢視模型名稱會對應至檢視名稱,並以 「ViewModel」 結尾。
最後,方法會將 OnAutoWireViewModelChanged
檢視類型的 設定 BindingContext
為解析的檢視模型類型。 如需解析檢視模型類型的詳細資訊,請參閱 解析。
這種方法的優點是,應用程式具有單一類別,負責具現化檢視模型及其與檢視的連線。
提示
使用檢視模型定位器輕鬆替代。 檢視模型定位器也可以做為相依性替代實作的替代點,例如單元測試或設計時間數據。
更新檢視以響應基礎檢視模型或模型中的變更
檢視可存取的所有檢視模型和模型類別都應該實作 INotifyPropertyChanged
介面。 在檢視模型或模型類別中實作這個介面,可讓類別在基礎屬性值變更時,將變更通知提供給檢視中的任何資料繫結控制項。
應用程式應根據下列需求來建構,以正確使用屬性變更通知:
- 如果公用屬性值變更,一律引發
PropertyChanged
事件。 不要因為知道 XAML 繫結如何發生,就假設引發PropertyChanged
事件可予以忽略。 - 凡經檢視模型或模型中其他屬性使用其值的任何計算屬性,一律引發
PropertyChanged
事件。 - 在變更屬性的方法結尾或已知物件處於安全狀態時,一律引發
PropertyChanged
事件。 引發事件會透過同步叫用事件的處理常式來中斷作業。 如果在作業中途發生這種情況,物件可能會在不安全的部分更新狀態下,向回呼函式公開。 此外,PropertyChanged
事件可能會觸發串聯變更。 串聯變更通常需要先完成更新,才能安全執行串聯變更。 - 如果屬性未變更,絕不引發
PropertyChanged
事件。 這表示您必須先比較新舊兩值,再引發PropertyChanged
事件。 - 如果您要初始化屬性,請絕對不要在檢視模型建構期間引發
PropertyChanged
事件。 檢視中的資料繫結控制項目前尚未訂閱接收變更通知。 - 絕對不要只在一個同步叫用的類別公用方法內,使用相同的屬性名稱引數引發一個以上的
PropertyChanged
事件。 例如,假設NumberOfItems
屬性的備份存放區是_numberOfItems
欄位,如果方法在執行迴圈期間將_numberOfItems
遞增 50 次,在所有工作完成後,應該只會在NumberOfItems
屬性上引發一次屬性變更通知。 若是非同步方法,請在非同步接續鏈的每個同步區段中,針對指定的屬性名稱引發PropertyChanged
事件。
eShopOnContainers 行動應用程式會使用 ExtendedBindableObject
類別來提供變更通知,如下列程式代碼範例所示:
public abstract class ExtendedBindableObject : BindableObject
{
public void RaisePropertyChanged<T>(Expression<Func<T>> property)
{
var name = GetMemberInfo(property).Name;
OnPropertyChanged(name);
}
private MemberInfo GetMemberInfo(Expression expression)
{
...
}
}
Xamarin.Form 的 BindableObject
類別會 INotifyPropertyChanged
實作 介面,並提供 OnPropertyChanged
方法。 ExtendedBindableObject
類別提供 RaisePropertyChanged
方法以叫用屬性變更通知,且因而使用 BindableObject
類別所提供的功能。
eShopOnContainers 行動應用程式中的每個檢視模型類別都衍生自 ViewModelBase
類別,而類別又衍生自 ExtendedBindableObject
類別。 因此,每個檢視模型類別都會使用 ExtendedBindableObject
類別中的 RaisePropertyChanged
方法來提供屬性變更通知。 下列程式代碼範例示範 eShopOnContainers 行動應用程式如何使用 Lambda 表達式叫用屬性變更通知:
public bool IsLogin
{
get
{
return _isLogin;
}
set
{
_isLogin = value;
RaisePropertyChanged(() => IsLogin);
}
}
請注意,以這種方式使用 Lambda 運算式牽涉到較小的效能成本,因為每個呼叫都必須評估 Lambda 運算式。 雖然效能成本很小,而且通常不會影響應用程式,但當有許多變更通知時,成本可能會累積。 不過,此方法的優點是能在重新命名屬性時,提供編譯階段類型安全性和重構支援。
使用命令和行為進行UI互動
在行動應用程式中,動作通常會在回應用戶動作時叫用,例如按鍵,藉由在程式代碼後置檔案中建立事件處理程式來實作。 不過,在 MVVM 模式中,實作動作的責任在於檢視模型,並應避免將程式碼放在程式碼後置中。
命令提供了方便的方式,以表示可繫結至 UI 控制項的動作。 它們會封裝實作動作的程序代碼,並協助將其與檢視中的視覺表示分離。 Xamarin.Forms 包含可以宣告方式連接到命令的控制項,而且這些控制件會在使用者與控件互動時叫用命令。
行為也允許控制項以宣告方式連線到命令。 不過,您也可以使用行為叫用與控制項所引發之一系列事件相關聯的動作。 因此,行為會處理許多與啟用命令控制項相同的案例,同時提供更大的彈性和控制力度。 此外,針對未特別設計與命令互動的控制項,您還可以使用行為建立其與命令物件或方法的關聯性。
實作命令
檢視模型通常會公開從檢視系結的命令屬性,這些是實 ICommand
作 介面的物件實例。 許多 Xamarin.Forms 控件提供 Command
屬性,它可以是系結至 ICommand
檢視模型所提供之對象的數據。 ICommand
介面會定義可封裝作業本身的 Execute
方法、指出是否可以叫用命令的 CanExecute
方法,以及變更發生時所出現影響命令是否應執行的 CanExecuteChanged
事件。 Command
和 Command<T>
Xamarin.Forms類別會實ICommand
作 介面,其中 T
是 和 CanExecute
自變數Execute
的類型。
在檢視模型中,應該有 類型 Command
的物件,或 Command<T>
類型檢視模型中每個公用屬性的物件 ICommand
。 或 Command<T>
建Command
構函式需要Action
叫用 方法時所呼叫的ICommand.Execute
回呼物件。 方法是 CanExecute
選擇性的建構函式參數,而且是 Func
傳回 的 bool
。
下列程式代碼示範 Command
如何藉由指定檢視模型方法的委派來建構代表緩存器命令的 Register
實例:
public ICommand RegisterCommand => new Command(Register);
此命令會透過傳回 ICommand
參考的屬性向檢視公開。 對 Command
物件呼叫 Execute
方法時,只要透過 Command
建構函式中所指定的委派,將呼叫轉送至檢視模型中的方法即可。
指定命令的Execute
委派時,可以使用 和 await
關鍵詞來叫用async
異步方法。 這表示回呼是 Task
,而且應等候。 例如,下列程式代碼示範 Command
如何藉由指定檢視模型方法的委派來建構代表登入命令的 SignInAsync
實例:
public ICommand SignInCommand => new Command(async () => await SignInAsync());
您可以使用 Command<T>
類別將命令具現化,將參數傳遞至 Execute
和 CanExecute
動作。 例如,下列程式代碼示範 Command<T>
如何使用 實例來 NavigateAsync
指出方法需要 類型的 string
自變數:
public ICommand NavigateCommand => new Command<string>(NavigateAsync);
在 Command
和 Command<T>
類別中,每個建構函式中的 CanExecute
方法委派都是選擇性的。 如果未指定委派,Command
則會針對 CanExecute
傳回 true
。 不過,檢視模型可以對 Command
物件呼叫 ChangeCanExecute
方法,指出命令的 CanExecute
狀態變更。 這會引發 CanExecuteChanged
事件。 系結至命令之 UI 中的任何控件都會更新其啟用狀態,以反映數據系結命令的可用性。
從檢視叫用命令
下列程式碼範例示範如何使用 TapGestureRecognizer
執行個體將 LoginView
中的 Grid
繫結至 LoginViewModel
類別的 RegisterCommand
:
<Grid Grid.Column="1" HorizontalOptions="Center">
<Label Text="REGISTER" TextColor="Gray"/>
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding RegisterCommand}" NumberOfTapsRequired="1" />
</Grid.GestureRecognizers>
</Grid>
您也可以使用 CommandParameter
屬性選擇是否定義命令參數。 預期引數的類型是在 Execute
和 CanExecute
目標方法中指定。 當使用者與附加控制項互動時,TapGestureRecognizer
會自動叫用目標命令。 如果提供,命令參數將會當做自變數傳遞至命令的 Execute
委派。
實作行為
行為可讓功能新增至 UI 控制項,卻不需要將其子類別化。 相反地,功能會在行為類別中實作並附加至控制項,如同控制項本身的一部分。 行為可讓您實作通常必須撰寫為程式代碼後置的程式碼,因為它會直接與控件的 API 互動,如此一來,它就可以簡潔地附加至控件,並封裝在多個檢視或應用程式中重複使用。 在 MVVM 內容中,行為是連線控制項與命令的實用方法。
透過附加屬性附加至控制項的行為稱為附加行為。 然後,行為可以使用其所附加元素的公開 API,在檢視的視覺化樹狀結構中,將功能新增至該控制項或其他控制項。 eShopOnContainers 行動應用程式包含 LineColorBehavior
類別,這是附加的行為。 如需此行為的詳細資訊,請參閱 顯示驗證錯誤。
行為 Xamarin.Forms 是衍生自 Behavior
或 Behavior<T>
類別的類別,其中 T
是應該套用行為的控件類型。 這些類別提供 OnAttachedTo
和 OnDetachingFrom
方法,其應該被覆寫,才能提供行為附加至控制項以及中斷連接時所執行的邏輯。
在 eShopOnContainers 行動應用程式中,類別 BindableBehavior<T>
衍生自 Behavior<T>
類別。 類別的目的是BindableBehavior<T>
為需要將行為設定為附加控件的行為BindingContext
提供基類Xamarin.Forms。
BindableBehavior<T>
類別會提供可覆寫的 OnAttachedTo
方法以設定行為的 BindingContext
,和可覆寫的 OnDetachingFrom
方法以清除 BindingContext
。 此外,該類別會在 AssociatedObject
屬性中儲存對附加控制項的參考。
eShopOnContainers 行動應用程式包含類別 EventToCommandBehavior
,其會執行命令以回應事件發生的事件。 這個類別衍生自 BindableBehavior<T>
類別,所以當行為被取用時,其可繫結至 Command
屬性指定的 ICommand
,並予以執行。 下列程式碼範例顯示 EventToCommandBehavior
類別:
public class EventToCommandBehavior : BindableBehavior<View>
{
...
protected override void OnAttachedTo(View visualElement)
{
base.OnAttachedTo(visualElement);
var events = AssociatedObject.GetType().GetRuntimeEvents().ToArray();
if (events.Any())
{
_eventInfo = events.FirstOrDefault(e => e.Name == EventName);
if (_eventInfo == null)
throw new ArgumentException(string.Format(
"EventToCommand: Can't find any event named '{0}' on attached type",
EventName));
AddEventHandler(_eventInfo, AssociatedObject, OnFired);
}
}
protected override void OnDetachingFrom(View view)
{
if (_handler != null)
_eventInfo.RemoveEventHandler(AssociatedObject, _handler);
base.OnDetachingFrom(view);
}
private void AddEventHandler(
EventInfo eventInfo, object item, Action<object, EventArgs> action)
{
...
}
private void OnFired(object sender, EventArgs eventArgs)
{
...
}
}
OnAttachedTo
和 OnDetachingFrom
方法可用來註冊及取消註冊 EventName
屬性中所定義事件的事件處理常式。 然後,當事件發生時,會叫用執行命令的 OnFired
方法。
在事件發生時使用 EventToCommandBehavior
執行命令的優點,是可以建立命令與未設計和命令互動之控制項的關聯性。 此外,這會將事件處理程式碼移至檢視模型,在此進行單元測試。
從檢視叫用行為
EventToCommandBehavior
特別適合用於將命令附加到不支援命令的控制項上。 例如,會ProfileView
使用 EventToCommandBehavior
執行 事件在列出用戶訂單的 ListView
上時執行 OrderDetailCommand
ItemTapped
,如下列程式代碼所示:
<ListView>
<ListView.Behaviors>
<behaviors:EventToCommandBehavior
EventName="ItemTapped"
Command="{Binding OrderDetailCommand}"
EventArgsConverter="{StaticResource ItemTappedEventArgsConverter}" />
</ListView.Behaviors>
...
</ListView>
在執行階段,EventToCommandBehavior
會回應與 ListView
的互動。 在中ListView
選取專案時,ItemTapped
會引發 事件,這會在 中ProfileViewModel
執行 OrderDetailCommand
。 事件的事件引數預設會傳遞給命令。 此資料會隨著屬性中指定的 EventArgsConverter
轉換子在來源和目標之間傳遞而轉換,這會從 傳回 Item
的 ListView
ItemTappedEventArgs
。 因此,執行 時 OrderDetailCommand
,選取 Order
的項目會當做參數傳遞至已註冊的 Action。
如需行為的詳細資訊,請參閱 行為。
摘要
Model-View-ViewModel (MVVM) 模式有助於清除應用程式的商業和呈現邏輯與其使用者介面(UI)。 維護應用程式邏輯與UI之間的全新區隔有助於解決許多開發問題,並讓應用程式更容易測試、維護和發展。 它也可以大幅改善程式代碼重複使用的機會,並允許開發人員和UI設計工具在開發應用程式各自的部分時更輕鬆地共同作業。
使用MVVM模式時,應用程式的UI和基礎簡報和商業規則會分成三個不同的類別:檢視,其會封裝UI和UI邏輯:檢視模型,其封裝呈現邏輯和狀態;和模型,其會封裝應用程式的商業規則和數據。