イケてる技術で品質を担保しつつ
スピード感のある開発を実現する冴えたやり方
2018/10/23 AADojo #0
株式会社オルターブース 松村 優大
松村 優大 (MLBお兄さん)
株式会社オルターブース
業務執行役員/CTA
C#/PHP/Azure/AWS
31歳/島根出身/娘1人
開発において大事にしていること
• テストできる状態(Testable)を保つ・網羅する
• 今日書いたコードは今日テストを書く
• 開発初期段階からCIを回す(Continuous Integration)
なぜテストが必要か(再確認)
• エラーを見つける
• 想定内のエラー:想定通りのエラー処理であることを確認
• 想定外のエラー:リリース前にエラーに気付くため
• 変更に伴う“デグレ”を生まない・残さない
Testableなコードとは
• 処理(関数)の目的が明確かつ粒度が小さい
• 途中処理の差し替えが容易である(モック)
• 外部リソースの挙動の差し替えが容易である(モック)
データベース SaaS (API) ストレージ
.NET Coreのバージョンとサポート計画
バージョン レベル リリース日 サポート終了日
1.0 LTS 2016/06/27 2019/06/27
1.1 LTS 2016/11/16 2019/06/27
2.0 Current 2017/08/14 2018/10/01
2.1 LTS 2018/05/30
At least three years
from LTS declaration
(August 21, 2018)
2.2-preview3 Preview 2018/09/12 -
※2018/10/23現在
Data Store
Repository
Controller
View
Model
Service/Domain
Repository
Model
Repository
Model
Data Store
Model
Controller
View
Model Model
ユースケースに従って
ServiceやDomainを
構成する
CRUDの操作のみ
データ保存の振る舞い
public interface IRepository<T> where T : class
{
IEnumerable<T> GetAll(); //一覧
T Find(int id); //取得
T Create(T entity); //作成
void Update(T entity); //更新
void Delete(T entity); //削除
}
public interface IUserRepository : IRepository<User> { }
public class UserRepository : IUserRepository {
private readonly DbContext db;
public UserRepository(DbContext db) => this.db = db;
public IEnumerable<User> GetAll() => ...;
public User Find(int id) => ...;
public User Create(User entity) => ...; db.SaveChanges();
public void Update(User entity) => ...; db.SaveChanges();
public void Delete(User entity) => ...; db.SaveChanges();
DbContext = データベース = 外部リソース
外部リソースを直接操作する
ため、モックを作れない
(=テストしづらい)
public class UsersController : Controller {
private readonly IUserRepository userRepository;
public UsersController(IUserRepository userRepository)
=> this.userRepository = userRepository;
public IActionResult Create(User user)
{
userRepository.Create(user);
return Ok();
}
テスト時はモックのリポジトリを差し込む
処理のなかで外部リソースが現れるため
単体テストを行うことができない
(擬似的な保存の振舞いができない)
ユースケースに従って
ServiceやDomainを
構成する
CRUDの操作のみ
データ保存の振る舞い
データストアを隠蔽
単体テストが可能
public interface IUnitOfWork {
int SaveChanges(); //DbContext.SaveChanges(既存の保存処理)
bool SaveEntities(); //実際に呼び出される保存処理の定義
}
public class MyContext : DbContext, IUnitOfWork {
public DbSet<User> Users { get; set; }
public bool SaveEntities() => 0 < this.SaveChanges();
//public bool SaveEntities() => true; //モックの書き方
} DbContextに新しい保存処理を実装
(テストではモック化し保存の振舞いだけ)
public class UserRepository : IUserRepository {
private readonly DbContext db;
public IUnitOfWork UnitOfWork => db;
public UserRepository(DbContext db) => this.db = db;
---
public IActionResult Create(User user) {
userRepository.Create(user);
userRepository.UnitOfWork.SaveEntities();
return Ok();
}
リポジトリから保存処理を切り離す
処理のなかで外部リソースが隠蔽されたため
単体テストを行うことが可能
アプリケーションアーキテクチャを
どう学ぶか
アーキテクチャを学ぶうえで良い資料
実行環境をどうするか
PaaS?
コンテナー?
サーバーレス?
アプリケーション構成に応じて適した環境を選ぶべき
→言語(ランタイム)、規模、スケール、実行頻度、etc
→Kubernetesを使うかどうかは規模によりけり
Appendix
• https://www.microsoft.com/net/learn/dotnet/architecture-guides
• https://docs.microsoft.com/ja-jp/dotnet/standard/modern-web-apps-azure-architecture/
• https://github.com/dotnet-architecture/eShopOnWeb
• https://github.com/dotnet-architecture/eShopOnContainers
• https://12factor.net/ja/
ご清聴ありがとうございました。

イケてる技術で品質を担保しつつスピード感のある開発を実現する冴えたやり方