レイヤ構成の検討

料金計算を呼び出すコードを記述する前に、レイヤの構成について確認しておきましょう。というのも、ドメイン駆動設計では基本的なレイヤアーキテクチャを次のように定義しています。

・UI層…ユーザに対して提供する層(Ls.UI)
・アプリケーション層…ドメインオブジェクトを操作する層(Ls.App:ここで作成)
・ドメイン層…ビジネス上の概念を表現する層(Ls.Domain)
・インフラストラクチャ層…技術的な基盤にあたる層(LightSpeed)

これまでのように、UI層からUnitOfWorkを使ってクエリを操作するのは手軽で良いのですが、機能が増えてくるとUI層でコードの重複が発生することになりますし、テスト駆動開発との相性も良くありません。そのため、ドメインオブジェクトを操作するアプリケーション層を挟みたいと思います。

アプリケーション層(Ls.App)を追加

ここでは、「Ls.App」というクラスライブラリプロジェクトを追加しました。ドメイン層(Ls.Domain)と各種DLLに参照設定を行い、ServiceBase.cs、RentalService.cs、DvdService.csというクラスを追加しました。このアプリケーション層のコードには、ユーザインタフェースの要素を除くサービス的なロジックを記述しています。

アプリケーション層の設計

ASP.NETアプリケーションであればGlobal.asax等でリクエストの開始と終了を検知してUnitOfWorkの生成と破棄を一元管理できるのですが、Windowsアプリケーションの場合、そのタイミングを計るのが難しいので、上図のアプリケーション層のコードではステートレスなイメージでメソッド毎にデータ操作が完結(UnitOfWorkを破棄)するようにプログラムしています。Webアプリケーションでの実装方法については、サンプル「ASP.NET MVC」と「Online store」が参考になるでしょう。

また、UI層からアプリケーション層のクラスを取得する手段として、Service LocatorやDIコンテナを用意しておく方法もあるかもしれませんが、本稿ではそこまでは実装しませんでした。LightSpeedContextクラスもSingletonパターンを適用するほうが良いかと思います。

UI層の実装

最後に、UI層のコードを記述します。UI層(Ls.UI)では、アプリケーション層(Ls.App)への参照設定を追加しておきます。

レンタルを読み出して、金額の計算を行うコード

//アプリケーション層のクラス
RentalService _rentalService = new RentalService();

//クリックイベント
private void btnCalc_Click(object sender, EventArgs e)
{
  //エンティティの取得
  IList list = _rentalService.GetAll();

  //ビュー用に整形
  var disp = from rental in list
             from dvd in rental.Dvds
             orderby rental.Id, dvd.Title
             select new
             {
               合計金額 = rental.CalcAll(),
               レンタル日数 = rental.DaySpan,
               タイトル = dvd.Title,
               区分 = dvd.Type,
               区分名称 = dvd.TypeName,
               個別金額 = dvd.Calc(rental.DaySpan)
             };

  //バインディング
  dataGridView1.DataSource = disp.ToList();
}

アプリケーションの実行結果を見てみましょう。

Windowsアプリケーションの実行結果

合計金額、区分名称、個別金額といった追加した振る舞いが動作していることが確認できました。

非接続状態でのエンティティの操作

なお、このレイヤ構成の場合、UI層ではUnitOfWorkが存在しない状態になります。そのため、遅延ロード対象のエンティティを操作しようとするとデータを読み出せずエラーが発生します。この非接続の状態で、関連エンティティを操作する場合は、それらの関連に対して同時ロード(Eagar Load)設定をしておきUnitOfWorkを切断する前に読み込んでおく必要があります。

また、非接続状態で変更したエンティティをデータベースに反映させたい場合は、別のUnitOfWorkでAttachメソッドを呼び出します。このメソッドを呼び出すことで、そのUnitOfWorkの管理対象として設定することができます。

まとめ

以上、3回に分けてLightSpeedを活用したアプリケーションの構築方法について紹介してきました。ドメインモデルを利用すると、クラスのメソッドの連鎖で処理を実現していくため最初は理解しにくいかもしれません。しかし、慣れてくるとコードの全体量が少なくなり、クラスの役割が明確になってくるので生産性の高い開発をすることができます。また丁寧にモデリングを行うことで仕様漏れを早くから検知することもできます。

通常は、データベースと画面の構成を決めてからプログラムを始めることが多いと思いますが、今後は、モデリングを中心に据えて、シームレスで反復的にプログラミングしていく開発スタイルが増えてくるかもしれません。このような開発方法に興味をもたれた方は、LightSpeedを導入してみてはいかがでしょうか。