Last active
April 15, 2020 14:16
-
-
Save hadashiA/be36b883fea98faecb3906570e550556 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Entities | |
{ | |
interface ISealDataSource | |
{ | |
UniTask<Seal> Find(int id); | |
UniTask IncrementAsync(int id, int count); | |
UniTask DecrementAsync(int id); | |
} | |
class Seal | |
{ | |
public int Id; | |
public int Count; | |
} | |
class SealRepository | |
{ | |
readonly ISealDataSource dataSource; // ※円の外側であるDB/Devicesなどの実装の詳細を隠蔽 | |
readonly ISubject<int> onChanged = new Subject<int>(); | |
public SealRepository(ISealDataSource dataSource) | |
{ | |
this.dataSource = dataSource; | |
} | |
public async UniTask IncrementAsync(int id, int count) | |
{ | |
await dataSource.IncrementAsync(id, count); | |
onChanged.OnNext(...); | |
} | |
public async UniTask DecrementAsync(int id) | |
{ | |
await dataSource.DecrementAsync(id); | |
onChanged.OnNext(...); | |
} | |
public IObservable<int> OnChangeCountAsObservable() => onChanged; | |
// キャッシュなどしたい場合はここで。 | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Entities; // 円の内側であるEntitiesに依存するのはok! | |
namespace UseCases | |
{ | |
class SealIncrementUseCase | |
{ | |
readonly SealRepository repository; // ※円の内側であるEntityに依存するのはok! | |
public async UniTask RunAsync(int id, int count) | |
{ | |
// ... なにか複雑なロジック | |
await repository.IncrementAsync(id, count); | |
// ... なにか複雑なロジック | |
} | |
} | |
class SealConsumeUseCase | |
{ | |
readonly SealRepository repository; | |
public async UniTask RunAsync(int id) | |
{ | |
// ... なにか複雑なロジック | |
await repository.DecrementAsync(id); | |
// ... なにか複雑なロジック | |
} | |
} | |
class SealChangeDetectionUseCase | |
{ | |
readonly SealRepository repository; | |
public IObservable<int> OnChangeCountAsObservable() | |
{ | |
// .. なにか複雑な状態の読み取りやデータのマージ | |
return repository.OnChangeCountAsObservable(); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UseCases; //円の内側であるUseCasesに依存するのはok! | |
namespace Presentation | |
{ | |
interface ISealListView // ※円の外側である UIの実装の詳細を隠蔽する | |
{ | |
IObservable<int> OnIncrementAsObservable(); | |
IObservable<int> OnConsumeAsObservable(); | |
} | |
class PresenterA : PresenterBase, Initializable | |
{ | |
readonly SealIncrementUseCase sealIncrementUseCase; // ※円の内側であるUseCaseに依存するのはok! | |
readonly SealConsumeUseCase sealConsumeUseCase; | |
readonly ISealListView sealListView; // 円の外側であるUIの詳細を隠蔽 | |
public Presenter(ISealListView sealListView) | |
{ | |
this.sealListView = sealListView; | |
} | |
void IInitializable.Initialize() | |
{ | |
sealListView.OnConsumeAsObservable() | |
.SelectMany(id => sealConsumeUseCase.RunAsync(id).ToObservable()) | |
.Subscribe() | |
.AddTo(this); | |
sealListView.OnIncrementAsObservable() | |
.SelectMany(id => sealIncrementUseCase.RunAsync(id).ToObservable()) | |
.Subscribe() | |
.AddTo(this); | |
} | |
} | |
class PresenterB : PresenterBase, IInitializable | |
{ | |
void IInitializable.Initialize() | |
{ | |
Signal受け取るAsObservable() | |
.SelectMany(id => sealConsumeUseCase.RunAsync(id).ToObservable()) | |
.Subscribe() | |
.AddTo(this); | |
} | |
} | |
class ControllerA : ControllerBase, IInitializable | |
{ | |
readonly SealChangeDetectionUseCase useCase; | |
void IInitializable.Initialize() | |
{ | |
useCase.OnChnageAsObservable() | |
.Subscribe(x => | |
{ | |
// Viewを書きかえたりなにかする | |
}) | |
.AddTo(this); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 円の内側である Entities へ依存するのはok! | |
// ※もしも、UseCases層にしか依存を許したくない場合は、UseCases層に型を増やしてさらにもう一段バイパスします。 | |
using Entities; | |
namespace Data | |
{ | |
class UserPrefsSealDataSource : ISealDataSource // ※より円の内側にある層へ実装を提供する | |
{ | |
public async UniTask<Seal> Find(int id) | |
{ | |
// UserPrefs を読む | |
} | |
public async UniTask IncrementAsync(int id, int count) | |
{ | |
// UserPrefsへ書き込む | |
} | |
public async UniTask DecrementAsync(int id) | |
{ | |
// UserPrefsへ書き込む | |
} | |
} | |
} | |
// たとえば別の実装を用意する場合 | |
namespace Data | |
{ | |
class RpcSealDataSource : ISealDataSource // ※より円の内側にある層へ実装を提供する | |
{ | |
public async UniTask<Seal> Find(int id) | |
{ | |
// サーバからデータを持ってくる | |
} | |
public async UniTask IncrementAsync(int id, int count) | |
{ | |
// サーバへデータを送信する | |
} | |
public async UniTask DecrementAsync(int id) | |
{ | |
// サーバへデータを送信する | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Presentation; // 円の内側であるPresenationに依存するのはok! | |
using UnityEngine; // UnityEngine のアセンブリがないと動作しないのはこのレイヤのみ | |
namespace UI | |
{ | |
// ※より円の内側であるPresentation層へ、実装を提供する | |
class SealScrollList : MonoBehaviour, ISealListView | |
{ | |
[SerializeField] | |
ScrollRect scrollRect; // UnityEngineという名の、Viedwの実装の詳細を知っていて良いのはこのレイヤのみ | |
public IObservervable<TValue, TResult> OnIncrementAsObservable() | |
{ | |
// ... | |
} | |
public IObservervable<TValue, TResult> OnConsumeAsObservable() | |
{ | |
// ... | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Tests | |
{ | |
class StubSealDataSource : ISealDataSource // サーバやファイルに一切依存しないテスト用のスタブ | |
{ | |
public UniTask<Seal> Find(int id) | |
{ | |
return UniTask.FromResult(...); | |
} | |
public UniTask IncrementAsync(int id, int count) | |
{ | |
return UniTask.CompletedTask; | |
} | |
public UniTask DecrementAsync(int id) | |
{ | |
return UniTask.CompletedTask; | |
} | |
} | |
} | |
namespace Tests | |
{ | |
class SealConsumeUseCaseTest | |
{ | |
public SealConsumeUseCaseTest() | |
{ | |
// テスト用の実装を注入することで、ピュアC#ワールドでユニットテストを書くことができる | |
var repo = new SealRepository(new StubSealDataSource()); | |
sealConsumeUseCase = new SealConsumeUseCase(repo); | |
} | |
// テストテストテスト | |
[Test] | |
async Task Consume() | |
{ | |
// 制御フローやI/O に依存せず、ピュアC#ワールドで純粋にロジックのテストを書くことができる | |
// リフレクションでテストダブルをつくることも必要ない。依存の問題を設計によって解決できている | |
sealConsumeUseCase.RunAsnc()......... | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Clean Architecture Guide (with tested examples): Data Flow != Dependency Rule