背景
前回に引き続き、今回はクリーンアーキテクチャを実現するためのフレームワークについて考える。
前回:クリーンアーキテクチャ+DDDでAndroidアプリを作る(序)
成果物 ( java, androidで使えるフレームワーク )
まず「クリーンアーキテクチャをどう実現するか」に対する私見を述べる。
開発当初からフレームワークによってクリーンアーキテクチャの思想を支えておけば、あらゆる場面で様々な恩恵を受けることができるのではないか、というものだ。
そこで生み出したものがこちら。
Foca:https://github.com/y-takano/Foca
(ライブラリ設定や実際の使い方は上記のページから。)
2017/05/07 投稿からすっかり時間がたってしまったが元の作りだと何の役にも立たなかったので一から考え直して全面改修・ドキュメント整備をした。
何故フレームワークが必要なのか
クリーンアーキテクチャに沿ったアプリケーションを保守していくためには、始めからこの思想を組んだ作りこみをしていく必要がある。
かといってアプリケーションを開発するにあたり、クリーンアーキテクチャのあるべき姿をあーでもないこーでもないと考えながらクラスの設計や実装をしていくのはやはり効率が悪い。
以上の対策として、DIコンテナのフレームワークが最適なのでは思われる。
Focaが持つ役割
Focaフレームワークのあるべき姿を絵にかいてみる。
概念図
クリーンアーキテクチャで指摘されている問題から、自分で書いた絵と照らしてフレームワークの持つべき機能を分析する。
問題領域 | 解決手段 | 実現すべき機能 | 重要度 |
---|---|---|---|
1.業務層がアダプタ層に依存する | DIP | PresenterのDI | 最高 |
2.業務層がDB環境に依存する | DIP | GatewayのDI | 最高 |
3.アダプタ層が基盤環境に依存する | DIP | ViewのDI | 高 |
4.横断的関心事が外部環境に依存する | DIP+AOP | AspecterのDI | 中 |
5.外側のロジックが業務層に依存する | 生成ロジックの分離 | ControllerのDI | 低 |
(用語)
・DIP ・・・ 依存関係逆転の原則(Dependency Inversion Principle)
・AOP ・・・ アスペクト指向プログラミング(Aspect Oriented Programming)
・DI ・・・ 依存性の注入(Dependency Injection)
以上の考え方で果たしてクリーンアーキテクチャの思想を支えるフレームワークになっているのか、実際に使ってみて検証する。
使ってみる
まず、XMLとAPIの利用で以下を実現するための設計をする。
↓ この仕組みを維持することでクリーンアーキテクチャのレイヤ化を実現する。
↓ この仕組みを実装する。
DIコンテナ定義(XML)
<?xml version="1.0" encoding="UTF-8"?>
<!-- LayerContext: アプリケーションの定義
schemaLocationで示したurlにアクセスすることで、githubにあるxsdと連携して検証することができる。
-->
<LayerContext
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.java_conf.gr.jp/ke/namespace/foca"
xsi:schemaLocation="http://www.java_conf.gr.jp/ke/namespace/foca https://raw.githubusercontent.com/y-takano/Foca/master/foca-dicon.xsd">
<!-- Logger: ロガーの定義
アプリケーションから名前を指定して使用する. 複数定義可.
-->
<Logger level="DEBUG" name="tracelog" class="..."/>
<!-- Aspect: アスペクト(横断的関心事)の定義
Controller,Presenter,Gatewayをjoinpointするadviceを指定. 複数定義可.
-->
<Aspect name="trace-advice" advice="..."/>
<!-- DataFlow: InterfaceAdapterの定義
Controller,Presenter,Gatewayの依存性を指定. 複数定義可.
-->
<DataFlow type="UI" name="test">
<Controller>
<InputPort />
...
</Controller>
<Presenter>
<View />
...
</Presenter>
</DataFlow>
<!-- DataFlow: Gatewayを定義する場合 -->
<DataFlow type="DB" name="db-stub">
<Gateway>
<Driver />
...
</Gateway>
<Gateway>
<InputPort />
...
</Gateway>
</DataFlow>
</LayerContext>
アプリケーション実装( java )
public class Outside {
// DIコンテナ定義を読み込む
Foca.updateDefault(new URL("xxx"));
// インスタンスへDI
SomeObj obj = new SomeObj();
Foca.getDefault().inject(obj);
// DI済インスタンスを生成
SomeObj obj = Foca.getDefault().createInstance(SomeObj.class);
}
public class SampleEventHandler {
// Controllerの宣言
@Controller(name = "test")
private InterfaceAdapter controller;
// Loggerの宣言
@Log
private Logger logger;
// イベントハンドルメソッド
public void handle() {
// Logger呼出
logger.debug("debugログ");
logger.info("infoログ");
// Controller呼出
Data dto = new Data();
controller.invoke(dto);
}
}
public class SampleUsecase {
// Presenterの宣言
@Presenter(name = "test")
private InterfaceAdapter presenter;
// Gatewayの宣言
@Gateway(name = "test-db")
private InterfaceAdapter gateway;
// InputPortの宣言
@InputPort
public void execute(Data dto) {
// Gateway呼出
gateway.invoke();
// Presenter呼出
presenter.invoke();
}
}
public class SampleDriver {
// Gatewayの宣言
@Gateway(name = "test-db")
private InterfaceAdapter gateway;
// Driverの宣言
@Driver
public void access(Data dto) {
// Gateway呼び出し
gateway.invoke(...);
}
}
public class SamplePresenter {
// Viewの宣言
@View
public void print(Data dto) {
...
}
}
これで、レイヤ毎に疎結合されたモジュールが実装できた。
性能や利便性はともかくとして、クリーンアーキテクチャで謳われている、独立性・再利用性の網羅はできているのではないか。
次回はアプリケーション本体について考えたい。