導入
ここまでの9章で、タスク管理アプリは「テストだけを頼りに動くものを積み上げる」というやり方で完成に近づきました。仕上げに入る前に、一度立ち止まって全体を見渡しましょう。バラバラに見えていたEntity・Repository・Service・ViewModel・Viewが、実は1つの一貫した依存の向きのルールでつながっていたことに気づけるはずです。
説明
flowchart LR
subgraph Core["MyApp.Core(Model・外部技術に依存しない)"]
TT["TaskTitle / Priority<br/>(ValueObject)"]
TI["TaskItem<br/>(Entity)"]
FAC["TaskFactory"]
ITR["ITaskRepository(抽象)"]
ITS["ITaskService(抽象)"]
DTO["TaskDto / TaskMapper"]
end
subgraph Infra["MyApp.Infrastructure"]
IMR["InMemoryTaskRepository"]
end
subgraph App["MyApp(実行可能・View/ViewModel)"]
VM["TaskListViewModel"]
VW["View(XAML)"]
CR["Composition Root<br/>(起動時にDIを組み立てる)"]
end
FAC --> TI --> TT
IMR -.実装.-> ITR
TS["TaskService"] -.実装.-> ITS
TS --> ITR
VM --> ITS
VM --> DTO
VW <-->|"DataContext / {Binding}"| VM
CR -.配線.-> IMR
CR -.配線.-> TS
CR -.配線.-> VM
- Entity/ValueObject(第2章):
TaskItem・TaskTitle・Priority。業務データそのものと、それが必ず守るべきルールを持つ。何にも依存しない - Factory(第3章):
TaskFactory。生成のルールを1箇所にまとめる - Repository(第4章):
ITaskRepository(抽象)とInMemoryTaskRepository(実装)。データの出し入れだけを担当し、業務ルールは持たない - Service(第5章):
ITaskService/TaskService。「重複タイトル禁止」のような、複数のデータを見比べる業務ルールを担当する - Mapper/DTO(レッスン19):
TaskDto/TaskMapper。業務データ(TaskTitleなど)を画面用のプレーンな値に変換する - ViewModel(第6章):
TaskListViewModel。画面の状態(Tasks・NewTaskTitle)と操作(AddCommand)を、UIフレームワークに依存しない普通のC#クラスとして表現する - View(第7章): XAML。
DataContextと{Binding}でViewModelとつながるだけで、ロジックは持たない - DI/Composition Root(第8章): どの実装をどこに注入するかを、アプリ起動時の1箇所(Composition Root)でまとめて決める
- テスト(第9章): 各層は自分より内側(Core寄り)のインターフェースにしか依存しないため、外側をFakeに差し替えるだけで高速なテストが書けた
矢印の向きに注目してください。どの層も、自分より業務の核(Core)に近い層にしか依存していません。ViewはViewModelを知っていますが、ViewModelはViewの存在を知りません。ServiceはRepositoryのインターフェースを知っていますが、Repositoryの具体的な実装(InMemoryかDBか)は知りません。この「依存の向きを常に核へ集める」という1つのルールが、レッスン3のプロジェクト分割からテストのしやすさまで、コース全体を貫いていました。
まとめ
- Entity→Repository→Service→ViewModel→Viewは、すべて「自分より内側にしか依存しない」という1つのルールでつながっている
- このルール(依存性逆転)のおかげで、外側(Repository・Service・View)をFakeや別実装に差し替えても、内側のコードは変更不要になる
- 次のレッスンから、この構造を保ったまま新機能を2つ追加する
次回: 「期限切れ強調表示」機能をTDDで追加します。