テスターですが何か?

ホビープログラマ略してHPです

ASP.NET MVCのクラスの作り方を考えてみた(1)

leave a comment »

以前のエントリーで紹介した「プログラミングASP.NET MVC」を読んだこと、.NETラボ勉強会でMVVMの話を聞いたこともあって、ASP.NET MVCでどのようにクラスを作っていくのかを考えてみました。プロジェクトテンプレートにはControllerクラスしかありませんし、スキャフォールドで作成したコードはControllerが直接データアクセスクラスを呼び出しいているし。ネットには超単純なシナリオにしか(現実的にありえないような)対応出来ないコードだし。

ネットで公開されているようなコードはわかりやすく・単純化するために仕方がないと思います。確かに新機能の説明や「ASP.NET MCとは」という内容に200~300行書かれても読む気がなくなってしまいますから。しかし、patterns&practicesで公開されている「Project Silk」や「Project Liike」のコード、現実のアプリケーション開発のコードはそれらネット上のサンプルからの乖離(飛躍)が非常に大きく、サンプルと現実の溝を少しだけ埋めてみようかなと思った次第です。

一発で「こうだ」というコードが公開できるとは考えていないので、ブログを書きつつ、考えつつ、軌道修正していくので複数回のブログになると思います。あと、前もって断っておきますが書籍「プログラミング ASP.NET MVC」の内容の自分なりの理解内容を発言することになると思います。とくにオリジナリティはないと思います。

今回はControllerクラスの設計について考えてみました。具体的には、この3つのテーマです。

  • Controllerクラスには何を記述すべきか
  • クラス同士の依存性をいかにして弱くするか
  • MVCの「C」と「M」の境目はどこになるのか

Controllerクラスには何を記述すべきか

Controllerの責務はクライアントからHTTPリクエストを受け取って、HTTPレスポンスを返す(Viewを返す)ことです。それ以上でもそれ以下でもありません。建物の出入口に当たる部分になり、クライアントの都合に引っ張られる部分になります。そして、セッション管理などのWeb/HTTPに依存する処理は担当するものの、いわゆるビジネスロジックと呼ばれる部分はコントローラーには記述しません。その役目はModelの部分が担います。Modelはなるべくクライアントアーキテクチャーに依存しないように作成したほうが、汎用性/再利用性が増すため、Webというアーキテクチャーに依存する部分はControllerに記述すべきだと考えます。ViewはModelのことを知らないですし、ModelはViewのことを知らないわけですから、クライアントアーキテクチャーに依存する部分はControllerが担当するのは自然なことだと思います。

そしてControllerに対して1つのWorkerServiceと呼ばれるクラスを作成します。ControllerとWorkerServiceは1対1で紐付き、Controllerで受け取ったHTTPリクエストに対する処理を行います。Controllerから必ず1つのWorkerServiceを呼び出します。複数のControllerから共通のWorkerServiceを呼び出しても構いません。そして、WorkerServiceはコーディネーターとしての役割を担います、WorkerService自体にはいわゆるビジネスロジックは記載せずに、複数のビジネスロジッククラスを呼び出して処理を行います。そして処理結果をViewModelとしてControllerに返します。Controllerは自分が知っている1人のWorkerServiceに処理を委ねます、WorkerServiceは自分が知っている複数のビジネスロジッククラスを呼び出して、処理結果をViewModelとしてControllerに返します。ConrotollerはHTTPリクエストの受付、WorkerServiceはビジネスロジックの実行を受け付けます。

図にするとこうなります。

image

クラス同士の依存性をいかにして弱くするか

クラス同士の依存性を弱くすると何が嬉しいのか、何なんでしょうねぇ...一番のメリットはテストがしやすくなることです。コードを変更せずに依存するクラスを変更することができれば、Mockを使ったテストがやりやすくなります。それ以外のメリットは...すみませんよくわかりません。個人的な実装と実行の分離の現実界は、DI(Dependency Injection)の利用かなとおもいます。実装クラスにに対してインターフェースを用意し、コンストラクタで依存性を注入するやり方です(コンストラクタインジェクション)。

このやり方の最も現実的なところは、依存性の型情報をC#のコードとして記述できることです。DIコンテナとしてSearsarプロジェクトのS2Containerを利用したことがありますが、設定ファイルに文字列として型情報のマッピングを記載しています。個人的にはtypoまみれになるので設定ファイルの利用が嫌いです、マッピングを文字列ではなく型情報をとしてコードで記述できれば、インテリセンスが効きますしtypoをかなり防ぐことができます。設定ファイルにマッピング情報を記述してtypoでハマると悲惨ですよ...ハイ。なのでS2Cotainer.NETのQuillの考え方はすごく好きです。ASP.NET MVC+C#の考え方なら、設定ファイルでゴリゴリ書くよりもコードで書くほうが合うと思うんですよね。

あと、プロダクションコードではDI目的のインターフェースと実装クラスは1対1になるので同じファイルに書いてしまったほうがコードの見通しが良くなると思います。

Unityも確か設定ファイルにマッピン情報を記載したはずです。Project Silkのコードをしっかり読んでないとか、Unityについてほとんど調べてないです、すみません...

MVCの「C」と「M」の境目はどこになるのか

ControllerとServiceWorkerの話が出現すると、「C」と「M」の境目はどこなのか、ServiceWorkerは「C」なのか「M」なのかという話題になるかもしれません。個人的には

「(´・ω・`)知らんがな」

です。「C」と「M」って論理的な考え方なので、物理的なクラスがどちらに属するかを真剣に議論してもねぇ。論理的な考えに基づいてきちんとクラスの責務が決められていればいいと思うんですよ。敢えてどちらかと言えば「C」かと思います、ServiceWorker自体はビジネスロジッククラスの呼び出しを行なってViewModelを作っているだけなので、「C」なのかな、と。

そして出来上がったコードはこのようになりました

Controller

public class HogeController : Controller

{

    private IHogeWorkerService WorkerService;

 

    // 通常のコンストラクタ

    public HogeController()

    {

        WorkerService = new HogeWorkerService();

    }

 

    // DI(コンストラクタインジェクション)

    public HogeController(IHogeWorkerService workerService)

    {

        WorkerService = workerService;

    }

 

    // アクションメソッド

    public ActionResult Index()

    {

        // コントローラーはHTTPリクエストを受け取って

        // HTTPレスポンス(View)を返すのみ

        // Webに依存する部分(セッション管理など)のみを担当する

        // アクションフィルタでできることはアクションフィルタで済ませる

       

        // アプリケーションとしての処理はすべてWorkerServiceに委ねる

        // ViewModelの作成もWorkerServiceに任せる

        // 1つのアクションメソッドで1つのWorkerServiceの呼び出しを行う

 

        // コントローラーはこのようなすっきりとしたものになるはず

        var ViewModel = WorkerService.MakeHoge();

        return View(ViewModel);

    }

}

WorkerService

// DIのためのインターフェース、実装クラスと11なので同じファイルにしてしまってもいいと思う

public interface IHogeWorkerService

{

HogeViewModels MakeHoge();

}

 

// WorkerServiceクラス、コントローラーから処理を任される

// このクラスから各種ビジネスロジッククラスを呼び出して

// アプリケーションの仕様を実現する

public class HogeWorkerService : IHogeWorkerService

{

    private IHogeDao Dao;

 

    // 通常のコンストラクタ

    public HogeWorkerService()

    {

        Dao = new HogeDao();

    }

 

    // DI(コンストラクタインジェクション)コンストラクタ

    public HogeWorkerService(IHogeDao dao)

    {

        Dao = dao;

    }

 

    // コントローラーから呼び出されるメソッド

    public HogeViewModels MakeHoge()

    {

        // この例ではデータベースアクセスを行う処理だけを呼び出しているが

        // 現実はもっとたくさんのメソッド呼び出しを行うはず

        var Hoge = Dao.GetHogeList().First();

 

        // ViewModelの作成はこのクラスで担当する

        return new HogeViewModels

        {

        HogeId = Hoge.Id,

        HogeName = Hoge.HogeName

        };

    }

 }

Model(データアクセスクラスのみ)

// DIのためのインターフェース、実装クラスと11なので同じファイルにしてしまってもいいと思う

public interface IHogeDao

{

    IEnumerable<HogeModels> GetHogeList();

}

 

// データアクセスクラス 「XXXRepository」という名前にするのは違和感がある

// Models」ディレクトリでいいんだろうか...

public class HogeDao : IHogeDao

{

    public IEnumerable<HogeModels> GetHogeList()

    {

        using (var db = new MvcHogeAppContext())

        {

            return db.HogeModels.ToList();

        }

    }

}

作成したソース(ソリューション一式)はSkyDriveにアップしてあります、「MvcHogeApp」をダウンロードしてください。サイズを抑えるため、アセンブリ類は削除してあるのでVisual Studio 12 Express for Webがインストールされた環境で開いてください。

Written by david9142

2012年7月13日 @ 10:35 AM

カテゴリー: ASP.NET MVC

Tagged with ,

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。