This repository has been archived by the owner on Dec 6, 2021. It is now read-only.
Releases: umm/cafu_core
Releases · umm/cafu_core
v3.1.0
Re-review the definition of Clean Architecture
Dependency Inversion Principle for Presentation Layers
What?
- 所謂「依存性逆転の原則」(Clean Architecture 本 P.103 参照)
- Presentation レイヤーの依存の向きを逆にする
- 参照の持ち方が完全に逆転する
Why?
- 本来的に Clean Architecture が定義する依存の単一方向性からズレてしまっていたから
- 本来は View --> Presenter --> UseCase や DataStore --> Repository --> UseCase となるべき
- Wiki 参照
- 加えて、interface の定義場所が DIP: Dependency Inversion Principle (依存性逆転の原則)のルールに従い切れていなかったので、それも修正
- 本来は依存される側の namespace に interface を配置して、依存する側が namespace を跨ぐべき
Before
namespace UseCase
{
public interface IHogeUseCase {}
public class HogeUseCase : IHogeUseCase
{
}
}
namespace Presenter
{
public interface IFugaPresenter {}
public class FugaPresenter : IFugaPresenter
{
private IHogeUseCase HogeUseCase { get; }
}
}
namespace View
{
public interface IPiyoView {} // 実際に View の interface が切られることは無かった…かな?
public class PiyoView : IPiyoView
{
private IFugaPresenter FugaPresenter { get; }
}
}
After
namespace UseCase
{
// 最悪なくても良いが、テストする時に必要になる
public interface IHogeUseCase {}
// UseCase 的に「こういう振る舞いを持っていて欲しい Presenter」を定義
// 実際には、Suffix として Presenter を付けずに I<Actor><Behaviour> とするのが理想…かな?
// IMoleImageRenderer とか IWebCamInitializer とか?
public interface IFugaPresenter {}
public class HogeUseCase : IHogeUseCase
{
private IFugaPresenter FugaPresenter { get; }
// Zenject で DI しちゃうのがヨサソウ
public HogeUseCase(IFugaPresenter fugaPresenter)
{
FugaPresenter = fugaPresenter;
}
}
}
namespace Presenter
{
// Presenter 的に「こういう振る舞いを持っていて欲しい View」を定義
// 実際には、Suffix として View を付けずに I<Verb><Actor><Behaviour> とするのが理想…かな?
// ITransitionSceneTrigger とか IPlayTutorialTrigger とか?
public interface IPiyoView {}
public class FugaPresenter : IFugaPresenter
{
private IPiyoView PiyoView { get; }
// これも DI で
public FugaPresenter(IPiyoView piyoView)
{
PiyoView = piyoView;
}
}
}
namespace View
{
public class PiyoView : IPiyoView
{
}
}
- Zenject の Constructor Injection を使うとキレイになる
- とはいえ、開発時はフィールド増える度にコンストラクタを修正する必要が出てくるので面倒
- なので、開発時は
[Inject]
属性でプロパティに DI しておいて、リファクタリングフェーズでコンストラクタに移動するとかが筋が良いかも
Flatten interfaces
What?
- 各レイヤを表現する基底 interface の配置をフラットにする
CAFU.Core.Domain.UseCase.IUseCase
→CAFU.Core.IUseCase
的な。
Why?
- 現状の interface 群は「役割を表現するための型」であり、クラスのインタフェースを表現するためのモノではないため、namespace をネストさせると混乱を招くという判断もある
- また、 v2 と v3 の共存を睨んで、同一の型名が存在しないようにしたかった想いもある
IEntity
, IStructure
What?
- Entity レイヤを UseCase よりも更に上の概念として切り出す
- 各レイヤを跨ぐデータの受け渡しは Entity を直接用いずに、専用の構造体(まぁクラスでも良いけど)を用いて行う
- 各 UseCase 間で Entity を経由して処理の受け渡しを行う
- 具体的には、
FooUseCase
でIHogeEntity.FugaSubject.OnNext()
してBarUseCase
でIHogeEntity.FugaSubject.Subscribe()
を行う、みたいなイメージ- まぁ、ココも本当は Subject を経由せずに
Fuga()
とOnFugaAsObservable()
とかした方が良いのかも知れないけど、面倒なのでそこは Subject に任せちゃってもヨサソウ
- まぁ、ココも本当は Subject を経由せずに
- 具体的には、
- また、これに伴い Entity の宣言箇所を Domain 配下に移動する
Foo.Data.Entity
→Foo.Domain.Entity
IEntity
- Entity は「状態の管理」や「Entity に閉じた処理」を行うクラスとして定義される
- Actor に閉じているべきで、複数の役割を持たせないコトが肝要
IStructure
- Structure は「値の管理」を行うデータ構造として定義される
IDictionary<TKey, TParam>
やプリミティブな値のみで処理できる場合は不要- ちょっと複雑なデータ構造を受け渡しする場合や、受け渡しするデータに対して明確な意味づけをしたい場合に用いる
- 基本的にメソッドを持たずにフィールド・プロパティのみを持つ
- IObservable や Action/Func を持つのはアリ
Why?
- SRP: Single Responsibility Principle に従い、責務を分離したかったため
- UseCase で状態を管理せずに、Entity で管理することで、処理と状態の分離も図る
Add IResolver
What?
IResolver<TParam1, TParam2, ..., TConcrete>
なインタフェースを追加- 例えば画像の読み込みに際して、「サーバから取得する ImageFromHttpDataStore」と「キャッシュから読む ImageFromLocalCacheDataStore」とを切り替える必要がある場合に、「キャッシュの有無」によってどちらの DataStore を返すのかを処理するためのレイヤ
- イメージとしては Factory に近いが、都度インスタンスを生成するわけではなく、Resolver 側にそれぞれの DataStore のインスタンスを Inject しておくコトで効率化を図ったりする
Why?
- チョイチョイ使う割に名前がなかったので、レイヤーとして切り出して名前を与えたかった
IModel
IModel
What?
CAFU.Core.Domain.Model.IModel
を削除
Why?
- Entity と Structure の分離により、不要となるので削除
View.Controller
View.Controller
What?
CAFU.Core.Pesentation.View.Controller
を削除
Why?
- Zenject により Presenter の初期化が不要になったため
Rename Translator interfaces
Add DataStoreResolver
Features
- #39 Add DataStoreResolver
メモリリークを修正
@umm/unirx_observablelifecyclemonobehaviour を更新
Changes
@umm/unirx_observablelifecyclemonobehaviour
を v1.0.2 に更新