HOME > Learning Place >  アプリケーションアーキテクチャ

アプリケーションアーキテクチャの概要

Ext JS は MVC と MVVM 両方のアプリケーションアーキテクチャに対応しています。これらのアーキテクチャは共に、特定のコンセプトに沿って、アプリケーションコードを論理的な塊を元に分類しています。どちらのアプローチも、アプリケーションをどう分けるかによって強みが違います。

このガイドの目的は、これらのアーキテクチャを組み立てるコンポーネントに関しての基礎知識を提供することです。

MVCとは

MVC アーキテクチャでは、クラスの大半は Model か View か Controller です。ユーザーは Model が持っているデータを表示している View とやりとりします。そういったやりとりは Controller によってモニタリングされていて、それに対して View や Model を必要に応じてアップデートすることで反応します。

View と Model は基本的には互いを認識しあっていません。これは、 Controller がアップデートを指揮する責任を持っているからです。一般的にですが、 Controller は MVC アプリケーション内の大半のアプリケーションロジックを含んでいます。View にはビジネスロジックはほとんどありません (全くない場合もあります) 。 Model はデータに対するインターフェースという役割であり、そういったデータを加工するためのビジネスロジックを含んでいます。

MVC の目的はアプリケーション内の各クラスの責任を定義づけることです。各クラスにはっきりと定義づけられた責任があるため、暗黙のうちに大きな環境から分離されます。これによりアプリは簡単にテストできますし、メンテナンスも楽になり、コードも再利用しやすくなります。

MVVMとは

MVC と MVVM の大きな違いは、MVVM が ViewModel と言って、View を抽出化した機能を持っていることです。ViewModel は Model のデータと View が表示しているデータの違いを「データバインディング」という機能を使ってコーディネートします。

その結果、 Model とフレームワークが最大限に働く事で、View に直接関係するアプリケーションロジックを減らすことができるのです。

Ext JS 4 をお使いの方

Ext JS 5 は MVMM アーキテクチャへの対応も導入し、MVC の (C) への対応も改良されました。これらの改良点を知って有効活用して欲しいと思っていますが、それと同時に私たちは、既存の Ext JS 4 MVC アプリケーションに手を加えなくても機能することにも努力を費やしました。

MVC と MVVM

どちらがあなたのアプリケーションに適しているかを考える場合、まずはそれぞれの頭文字が何を意味するかについて話しましょう。

  • (M) Model – これはアプリケーションのデータです。いくつかのクラス ( Model と呼ばれるもの) がデータのフィールドを定義します (例:user-name と passwor をもつ User Model ) 。Model はデータパッケージを全体で、アソシエーションによって他の Model とリンクすることができます。

    Model はグリッドやその他のコンポーネントにデータを提供するために、普段は Store と一緒に使われます。また、Model はバリデーションや変換などのデータロジックを置く理想的な場所です。

  • (V) View – View は視覚的に表示されるコンポーネントです。例えばグリッド、ツリーやパネルは View として認識されています。

  • (C) Controller – Controller はアプリが動くために必要とする、View のロジックを維持する場所として使われます。これには View のレンダリング、ルーティング、などのアプリケーションロジックが含まれます。

  • (VM) ViewModel( View Model ) – ViewModel は View と直接関係するデータを管理するクラスです。コンポーネントにバインドする許可を与え、データが変わり次第更新します。

これらのアプリケーションアーキテクチャはフレームワークコードに対して構造と一貫性をもたらします。私たちが提案するコーディング規則に沿うだけで、たくさんの利益をもたらしてくれます:

  • 各アプリケーションは同じように動くので、一度覚えるだけで大丈夫です。

  • アプリケーション間でコードを共有することが簡単です。

  • Sencha Cmd を使う事で アプリケーションの最適化されたバージョンを生成する事ができます。

サンプルアプリを作る

ピースの一つひとつを解説する前に、 Sencha Cmd を使用してアプリをビルドしてみましょう。以下のコマンドをコマンドラインから入力してみましょう:

sencha generate app -ext MyApp ./app
cd app
sencha app watch

注: もしここで何が起きているか理解できない場合は、 Getting Started guide をチェックしてください。

アプリケーションの概要

MVC、MVVM、MVC+VM パターンを作るピースについて触れる前に、Cmd で作られたアプリケーションの構造を見てみましょう。

ファイル構造

Ext JS アプリケーションは、どのアプリでも同じ、統合されたひとつのディレクトリ構造で出来ています。私たちが推奨するレイアウトでは、全てのクラスは app フォルダーに入れられています。このフォルダーは ModelStore、 View エレメントを配置してあるサブフォルダーを含みます。 View 、 ViewControllerViewModel などの View エレメントはまとめた方が組織的にもより良い働きをします (次の “main” View フォルダ参照) 。

名前空間

各クラスの最初の行にはクラスの名前があります。この名前は名前空間と呼ばれます。名前空間の書き方は次の様になります。

<アプリ名>.<フォルダ名>.<クラス名(ファイル名)>

サンプルアプリ内では。“MyApp” がアプリ名で、 “view” がフォルダ名、“main” がサブフォルダ名で、“Main” がクラス名でありファイル名です。この情報を元にフレームワークは以下の場所で Main.js というファイルを探します:

app/view/main/Main.js

もしファイルが見つからなければ、Ext JS は状況を変えるまでエラーを表示します。

アプリケーション

index.html を見る事でアプリケーションの査定を始めましょう。

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
 
    <title>MyApp</title>
 
    <!-- The line below must be kept intact for Sencha Cmd to build your application -->
    <script id="microloader" type="text/javascript" src="bootstrap.js"></script>
 
</head>
<body></body>
</html>

Ext JS は Microloader を使って、app.json ファイルに記されたアプリケーションリソースをロードします。これにより、index.html に追加する必要はありません。app.json があれば、全てのアプリケーションのメタデータが一箇所に置かれます。それにより、 Sencha Cmd はアプリをより簡潔に効率よくコンパイルできます。

app.json には沢山のコメントがあって、記述できる情報についての情報を調べるための最高のリソースになります。

app.js

先ほどアプリケーションを生成した時に、私たちは Application.js 内にクラスを作成し、app.js の中でそのインスタンスを作りました。app.js の中身は次の様になります。

/*
 * This file is generated and updated by Sencha Cmd. You can edit this file as
 * needed for your application, but these edits will have to be merged by
 * Sencha Cmd when upgrading.
 */
Ext.application({
    name: 'MyApp',
 
    extend: 'MyApp.Application',
 
    autoCreateViewport: 'MyApp.view.main.Main'
 
    //-------------------------------------------------------------------------
    // Most customizations should be made to MyApp.Application. If you need to
    // customize this file, doing so below this section reduces the likelihood
    // of merge conflicts when upgrading to new versions of Sencha Cmd.
    //-------------------------------------------------------------------------
});

autoCreateViewport は Ext JS 5 の新しい機能です。autoCreateViewport にコンテナークラスを指定することで、どのクラスでも View ポートとして使えます。上記の例では MyApp.view.main.Main (コンテナー クラス) を Viewport としています。

autoCreateViewport コンフィグはアプリケーションに指定された View を作り、 Viewport プラグイン. をアタッチさせるよう指示します。これで View をドキュメントボディーに接続させます。

Application.js

どの Ext JS アプリケーションも Application クラス のインスタンスから始まります。このクラスは、app.js から立ち上げることができ、テスト用にインスタンス化もできます。

以下が Sencha Cmd を使ってアプリを生成した時に自動的に作られる Application.js の中身です。

Ext.define('MyApp.Application', {
    extend: 'Ext.app.Application',
 
    name: 'MyApp',
 
    stores: [
        // TODO: add global/shared stores here
    ],
 
    launch: function () {
        // TODO - Launch the application
    }
});

この Application クラス には、アプリの名前空間や共有 Store などの、アプリケーションのグローバル設定を含みます。

Views

View とは Ext.Component のサブクラスであるコンポーネントにすぎません。 View はアプリケーションの外見に関する情報を全て含んでいます。

スターターアプリの ”main” フォルダー内にある Main.js を開くと以下のようなコードを見る事ができます。

Ext.define('MyApp.view.main.Main', {
    extend: 'Ext.container.Container',
 
    xtype: 'app-main',
 
    controller: 'main',
    viewModel: {
        type: 'main'
    },
 
    layout: {
        type: 'border'
    },
 
    items: [{
        xtype: 'panel',
        bind: {
            title: '{name}'
        },
        region: 'west',
        html: '<ul>...</ul>',
        width: 250,
        split: true,
        tbar: [{
            text: 'Button',
            handler: 'onClickButton'
        }]
    },{
        region: 'center',
        xtype: 'tabpanel',
        items:[{
            title: 'Tab 1',
            html: '<h2>Content ...</h2>'
        }]
    }]
});

View にはアプリケーションロジックが全く含まれていない事がわかりますね。View のロジックに関する部分は全て ViewController, に配置します。それについては次のセクションでふれたいと思います。

この View では、west と center のリージョンを持つ border レイアウト のコンテナーを定義しています。 各リージョンには、 button を配置した toolbar のある paneltab panel が配置されています。 これらのコンセプトについてわからなければ、 Getting Started Guide を参照ください。

この View の二つの興味深い部分は、 controller と viewModel コンフィグです。

controller コンフィグ

controller コンフィグでは、その View のための ViewController を指定できます。この様に View に ViewController が指定されると、イベントハンドラーやレファレンスの入れ物になります。これは ViewController に コンポーネントや View が発火したイベントと 1対1 の関係を結びます。 Controller については後のセクションで更にふれたいと思います。

ViewModel Config

viewModel コンフィグでは、View に ViewModel を指定できます。 ViewModel はこのコンポーネントとその子コンポーネントのデータプロバイダーです。ViewModel に保存するデータは、通常、表示したり編集したりしたいコンポーネントに bind コンフィグを追加することで使われます。

上記の View を見ると、west リージョンのパネルのタイトルは ViewModel とバインドされていることがわかります。つまりタイトルはデータの “name” 値が読み込まれるということであり、これは ViewModel で管理されています。もし ViewModel のデータが変われば、タイトルの値も自動で着にアップデートされます。ViewModel については後半でも解説していきます。

ViewController

次に、 ViewController について見ていきましょう。スターターアプリが作った ViewController MainController.js はこのようにになっています:

Ext.define('MyApp.view.main.MainController', {
    extend: 'Ext.app.ViewController',
 
    requires: [
        'Ext.MessageBox'
    ],
 
    alias: 'controller.main',
 
    onClickButton: function () {
        Ext.Msg.confirm('Confirm', 'Are you sure?', 'onConfirm', this);
    },
 
    onConfirm: function (choice) {
        if (choice === 'yes') {
            //
        }
    }
});

Main.js という View に戻ってみると、tbar ボタンの handler コンフィグが指定されていることがわかります。そのハンドラーは ViewController 内の onClickButton という関数と対応付けられています。このように、この ViewController は特別な設定など必要なく、そのイベントに対応しているということになります。 

これによりアプリケーションにロジックを追加するのが非常に簡単になります。ViewController が View と1対1の関係を結んでいるので、onClickButton 機能を定義さえすればいいのです。

View のボタンをクリックすると、メッセージボックスが生成されます。メッセージボックスはコールバックに onConfirm がセットされていて、同じ ViewController にスコープされるようになっています。

ViewController は次の様にデザインされています。

  • 明示的に “listeners” や “reference”コンフィグを使用することで View に接続します。

  • View のライフサイクルを活用することで、関連づけされた ViewController を自動的に管理します。インスタンス化から破棄まで、Ext.app.ViewController は、参照されたコンポーネントと結ばれています。同じ View クラスの2つ目のインスタンスには、それ自身の ViewController インスタンスが作られます。これらの View が破棄された場合、関連する ViewController インスタンスも同時に破棄されます。

  • ネストした View をより直感的にするために、カプセル化を提供します。

ViewModel

次は ViewModel です。 MainModel.js というファイルを開くと、以下のコードが見られるはずです。

Ext.define('MyApp.view.main.MainModel', {
    extend: 'Ext.app.ViewModel',
 
    alias: 'viewmodel.main',
 
    data: {
        name: 'MyApp'
    }
 
    //TODO - add data, formulas and/or methods to support your view
});

ViewModel はデータオブジェクトを管理するクラスです。そしてこのクラスは、データに関心のある View にバインドして、変更について通知をする許可を与えます。 ViewController のように、 ViewModel はリファレンスされた View のものです。ViewModel は View と関連づけされているため、コンポーネント階層の中の、祖先コンポーネントが持っている親 ViewModel とリンクすることができます。これにより、親 ViewModel のデータを子 View が簡単に「継承」することができます。

Main.js 内の ViewModel コンフィグを使い、View を ViewModel に結合させました。この結合により、View の viewModel を宣言することで、自動的にデータをセットするため、セッターとコンフィグのバインドができるようにします。 データは MainModel.js の例ではインラインになっています。そして、このデータはどんなものでも、どんな場所からきたものでも大丈夫です。データはプロキシー (AJAX、REST、など) から提供されることもあるでしょう。

Model と Store

ModelStore はアプリケーションの情報のゲートウェイとなるものです。大半のデータはこの2つのクラスによって、送られ、取得され、整理され、モデリングされます。

Model

Ext.data.Model はアプリ内にある永続的なデータ (どのようなタイプでも) を表します。各 Model にはフィールドやアプリケーションにデータをモデリングさせる関数があります。 Model は大抵の場合、Store と接合されて使用されます。そして Store はグリッド、ツリー、チャートなどのデータバインドコンポーネントで使うことができます。

私たちのサンプルアプリケーションには、現状では Model がないので以下のコードを加えます。

Ext.define('MyApp.model.User', {
    extend: 'Ext.data.Model',
    fields: [
        {name: 'name',  type: 'string'},
        {name: 'age',   type: 'int'}
    ]
});

名前空間のセクションで述べたように、app/model の下に位置する User.js を 作りましょう。

フィールド

Ext.data.Model はレコードの値を保持し “fields”の定義をします。 Model クラスは “fields” コンフィグを使ってこれらのフィールドを宣言することができます。この場合、“name” は文字列 (string) として宣言され、age は 整数 (integer) です。 他の フィールドタイプ もあり、API ドキュメントに載っています。

フィールドを宣言する利点はたくさんありますが、する必要があるわけではありません。fields コンフィグを含まなければ、データは自動的に読み込まれ、データオブジェクトに入れられます。もしデータに対して次のことが必要であれば fields 定義しなければなりません。

  • バリデーション
  • 既定値
  • convert 関数

次は Store を設定して、これら二つの動作を見てみましょう。

Stores

Store はクライアント側のレコード (Model クラスのインスタンス) のキャッシュです。Store は内部に含まれているレコードの、ソート、フィルタ、検索機能を提供します。

このサンプルアプリケーションには Store がありませんが、心配はありません。Store を定義して、 Model をアサインすればよいだけです。

Ext.define('MyApp.store.User', {
    extend: 'Ext.data.Store',
    model: 'MyApp.model.User',
    data : [
        {firstName: 'Seth',    age: '34'},
        {firstName: 'Scott', age: '72'},
        {firstName: 'Gary', age: '19'},
        {firstName: 'Capybara', age: '208'}
    ]
});

上記の内容を、app/store に配置した User.js に追加します。

Store を Application.js の store コンフィグに加えると、Store のグローバルインスタンスを作ることができます。Application.js 内の store コンフィグは以下のようになっています。

stores: [
    'User'
],

この例では、Store が直接データを含んでいます。実際の多くの状況では、Model や Store のデータをプロキシーを使って集めなければいけないことがあります。プロキシーはデータプロバイダとアプリケーション間でのデータ転送をしてくれます。

Model、Store、データプロバイダについては Data Guide で詳しく解説しています。

次のステップ

私たちは Ticket App. という堅牢で、便利なアプリを作りました。このアプリケーションはログイン/ログアウト セッションを管理し、データバインドを取り入れ、MVC+VM アーキテクチャを活用する場合でのベスト・プラクティスを示します。この例はたくさんコメントされていて、全てのことができる限りクリアになっています。

もし時間があれば、 Ticket App に触れ、理想的なMVC+VM アプリケーションアーキテクチャについて知って欲しいと思います。

Ext JS 5 アーキテクチャー – 公式ガイド翻訳

Learning Placeトップに戻る

PAGETOP