ドメインモデルをLINQで構築する(その4)

前回までは参照シナリオを取り扱っていましたが今回から数回に分けてお買い上げ(Order)の更新シナリオを取り扱います。

お買い上げ(Order)ドメインモデル

まずは、今までと同じようにLINQ to SQLマッピング情報であるdbmlを作成します。

ただし、今までと異なり、お買い上げ(Order)は他の商品(Product)や店舗(Shop)エンティティを参照します。この点をどのように取扱か考える必要があります。1つの方法としては他のエンティティもお買い上げのモデルの中に含めてしまう方法です。これは特定のシナリオに特化したドメインモデルを作成するという考え方で、狭い範囲でモデルを作成できるためモデルの単純化と処理の最適化が行いやすいメリットがあります。ただ、共通化が行えないデメリットがあることは留意する必要があります。

もう一つの方法は、既存の商品(Product)や店舗(Shop)エンティティを利用する方法です。この方法は共通化を促進するメリットがありますが、複数に定義されたドメインモデルを統合的に取り扱う必要があります。今回は、複数のパッケージに分けて開発したドメインモデルを利用するシナリオにチャレンジすることもあり、こちらの方式で進めるようにします。

お買い上げの作成

まずは、空のお買い上げ(Order)が作成できるシナリオを目標にモデルを作成するようにします。単純にお買い上げを作成して登録するシナリオです。


シナリオのリファクタリング

作成したシナリオを見ると、店舗とお買い上げのDataContextが現れてなど少し冗長な部分がいくつか見えます。これをリファクタリングして見通しのコードが記述できるようにしていきます。

まずは、店舗とお買い上げのDataContext共通で利用できるBurgerShopDataContextを導入します。

自動生成で作成される店舗とお買い上げのDataContextを見るとわかるのですが、DataContextを継承して単純に各テーブルごとプロパティとユーザ拡張のためのpartial methodの定義があるだけで薄いラッパーになっています。それをより一般化するというかベースクラスに近い個別テーブルの機能をなくして共有可能にします。(MIXIN的なクラスを導入したほうが良いかもしれませんがここではDataContextをまとめる最も単純な方法を選択しました)

次に、お買い上げ(Order)の初期データを設定している処理をコンストラクタで処理できるように、新しいお買い上げのコンストラクタを作成します。

これでかなりすっきりしてきました。なお、自動生成されたデフォルトコンストラクタでいくつかの処理を行っていますので呼び出すことを忘れないようにします。

拡張メソッドを利用した店舗(Shop)の機能の拡張

お買い上げで作成時にコンストラクタの引数に店舗を指定していますが、これをもう少し読みやすいコードにならないか考えます。お買い上げのオーナーである店舗でお買い上げを作成するという、より積極的に意味を持たるコードにします。

var shop = shops.Single(s => s.Name == "OSAKA");
var myOrder = shop.CreateOrder();

この拡張はいままでだと、店舗(Shop)クラスを改定する必要があり、しかも店舗クラスがお買い上げ(Order)クラスに依存させる必要がありました。しかし、C#3.0で導入された拡張メソッドを利用するとこのShopの拡張が別のクラスで実装することが可能になりました。

依存関係が逆転するようなリソース(もの)エンティティに対してイベント(こと)エンティティの機能を追加する場合に拡張メソッドは威力を発揮します。拡張メソッドでは内部構造を直接触れない制約があり出来ることが限られますが、逆に適切なレベルで制御できると捉えればある意味扱いやすい技術だと言えます。

最後にリファクタリングされたコードに対するテストケースを示します。最初から比べるとすっきりしました。