Contenu connexe
Similaire à 世界一わかりやすいClean Architecture release-preview (20)
Plus de Atsushi Nakamura (14)
世界一わかりやすいClean Architecture release-preview
- 12. 注意事項
Slide 12Copyright 2019 @nuits_jp
今日お話しすることは、あくまで私の解釈です
• 極端に要約しているので、これがすべてではありません
• 書籍の記載とやや矛盾する点もあります
しかし、私は今日お話しする二つこそ、Clean Architectureの
本質だと思っています。
- 15. About Me
Copyright 2019 @nuits_jp Slide 15
中村 充志 / Atsushi Nakamura
• リコージャパン株式会社 所属
• Enterprise(おもに金融)系SIerのITアーキテクト
• 「持続可能なソフトウェア」の探求がライフワーク
• 2019年の目標
• 「世界一わかりやすいClean Architecture」で登壇したい
• 「xUnit & Moqハンズオン」開催
• Blog http://www.nuits.jp
• Blog(英語) https://blog.nuits.jp
• Twitter @nuits_jp
- 26. -The architecture rules are the same! -
(邦訳:アーキテクチャのルールはどれも同じである!)
著者は明言しています
Copyright 2019 @nuits_jp Slide 26
- 34. • SQL ServerのサンプルDB AdventureWorksを利用します
• プロダクト別の総売上をCSV出力するコンソール アプリ
Overview
Copyright 2018 @nuits_jp Slide 34
- 50. Gatewaysのクラス図とER図
Copyright 2018 @nuits_jp Slide 50
dm SalesOrderDetail and Product
Product
«column»
*PK ProductID: int
* Name: nvarchar(50)
* ProductNumber: nvarchar(25)
* MakeFlag: bit = 1
* FinishedGoodsFlag: bit = 1
Color: nvarchar(15)
* SafetyStockLevel: smallint
* ReorderPoint: smallint
* StandardCost: money
* ListPrice: money
Size: nvarchar(5)
FK SizeUnitMeasureCode: nchar(3)
FK WeightUnitMeasureCode: nchar(3)
Weight: decimal(8,2)
* DaysToManufacture: int
ProductLine: nchar(2)
Class: nchar(2)
Style: nchar(2)
FK ProductSubcategoryID: int
FK ProductModelID: int
* SellStartDate: datetime
SellEndDate: datetime
DiscontinuedDate: datetime
* rowguid: uniqueidentifier = newid()
* ModifiedDate: datetime = getdate()
SalesOrderDetail
«column»
*pfK SalesOrderID: int
*PK SalesOrderDetailID: int
CarrierTrackingNumber: nvarchar(25)
* OrderQty: smallint
*FK ProductID: int
*FK SpecialOfferID: int
* UnitPrice: money
* UnitPriceDiscount: money = 0.0
* LineTotal: numeric(38,6)
* rowguid: uniqueidentifier = newid()
* ModifiedDate: datetime = getdate()
クラス図 ER図
Gatewaysが完全にデータベースの文脈で記述されている
- 58. CQRS :Command-Query Responsibility Segregation
CQS :Command-Query Separation
データベースの操作を次のふたつに完全に分離する方法論
1. データベースを更新するCommand
2. データベースを参照するQuery
今回のケースはQueryに該当します。
つまりGatewayの文脈でQuery用のDBオブジェクトを用意する。
CQRS(or CQS)とは
Copyright 2019 @nuits_jp Slide 58
- 70. ソフトウェア エンティティ 代表的なコントラクト
システム OpenAPI(なんならCSVのFTP転送)
サブシステム OpenAPIやSwagger
コンポーネント プログラム インターフェース
クラス プログラム インターフェース
ソフトウェア エンティティとコントラクト
Copyright 2018 @nuits_jp Slide 70
- 74. まとめ
Slide 74Copyright 2019 @nuits_jp
• Clean Architectureとは、どんなソフトウェアにも適用可能な普遍的な
アーキテクチャである
• Clean Architectureは、次のふたつを誤解されがち
• 参考図の通りに関心を分離するのがClean Architectureである
• PresentationとModelの境界はInputとOutputを分離すべし
• 次のふたつを理解せずClean Architectureは語れない
• 依存性は、より上位レベルの方針にのみ向けよ
• 制御の流れと依存方向は分離し制御せよ
Notes de l'éditeur
- こんにちは。
それでは早速はじめさせていただきます。
本日は
「世界一わかりやすいClean Architecture
~ 誤解されがちな二つのことと、おさえるべき二つのこと ~」
というタイトルでお話しさせていただきます。
ちょっとマサカリ投げ放題みたいな内容なんですけど、皆さんくれぐれもお手柔らかにお願いします。
- さて、本題に入る前にふたつ質問させてください
- あまりに有名な図ですが、この図を見たことあるかた
良かったら挙手していただけますか?
- じゃぁもう一つ、この書籍読破したよって方挙手していただけますか?
読んでいない方、掛け値なしの名著だと思うのでまだ読まれていない方は、今すぐ購入して読みましょう。
個人的には
- 「C++はオブジェクト思考とかいうけど
別にCだって多態もカプセル化も実現できたし
なんならC++でカプセル化は弱まってるじゃん(笑)」
みたいな刺激的な話がいっぱいあって21世紀で最高に楽しめた技術書です。
- なお先ほどの表現は、盛大に誇張されたものであり、実際にはもっとちゃんとしてます。
ゴメンナサイ。
- というわけで、本セッションの目指すところでが
- 今日は、この場にいる皆さんが
「クリーンアーキテクチャチョットデキル」
という状態になっていただくことを目指します。
ちょっと自信ない方は今のうちにご退席ください。嘘です。
- というわけで、本セッションの概要ですが
- クリーンアーキテクチャについて、おもに次のふたつの事をお話しさせていただきます。
ひとつは、クリーンアーキテクチャの誤解されがちな二つのことに対する解説。
もうひとつは、私がクリーンアーキテクチャについて、本当に重要だと考えているふたつの事について、お話しします。
- さて本題に入る前に、少し注意事項があります。
- 今回お話しするのは、あくまで私の解釈です。
特に極端に要約しているので、今日お話しすることがすべてではないことはご理解ください。
また、書籍の記載とやや異なる説明もします。
それでも私は著者が本当に言いたかったこと、クリーンアーキテクチャでもっとも大切なことは今日お話しする二点にあると考えています。
- なお異論・反論は大歓迎です。
オンラインでご意見をいただいても良いですし、この後直接お話しさせていただくのも歓迎です。
寝不足なので、その辺で死亡してなかったらですがw
ぜひ議論してさせていただけると嬉しいです。
- 本題に入る前に最後にひとつ。
簡単に自己紹介させてください。
- 中村充志と申します。
リコージャパンのソリューション開発部ってところで、主に金融機関向けの
受託開発でアーキテクトをやってます。
「持続可能なソフトウェア」というテーマで色々考えるのが最近の趣味です。
今年の目標の一つは今日でクリアしましたので、あとはテストのハンズオン開催ができると良いなと思ってます。
では、本題に入りましょう。
- まずは「誤解されがちな二つのこと」です。
- この図をご覧になったことのある方は多いんじゃないかと思います。
- 実はこの図が良くできてい過ぎることが、大きな誤解を招く要因になっていると私は考えています。
どういうことでしょうか?
- ひとつは、この図は決してソフトウェアの関心を
ControllerやUseCase、Entityに分離しろという意味でもなく
レイヤーをこの4つに分割しろという意味ではないということです
- そしてふたつ目は右下のこれです。
これもPresentationを古典的なMVCのように設計しろという意味ではないですし
データや処理の流れを一方通行にしろと言っている訳でもありません。
- では、どういう意味でしょうか?先の図で本当に大切なことは
- 依存性は、外から中だけに向かっていなくてはいけない。
というたった一点にあります。
でもじゃぁ、例えばEntitiesの状態が変わったことで、UIを受動的に変化させたいみたいな
中から外を呼ばなければいけないときはどうすれば良いの?
という疑問が、すぐに湧いてきますよね?
- 右下の図は、内側の変化に応じて外側を呼び出したい場合に
依存性は外から中に向けたまま、処理の流れとして中から外に戻すにはどうすればよいのか?
を例示しているものです。
- そんなこと言っても、本当にあってるのか?って今皆さん思いましたよね?
- はい。もちろん根拠あっての話です。
そもそも著者は書籍の先頭でこう明言しています。
- 「アーキテクチャのルールはどれも同じである!」
日本語版では帯にも書かれていますよね。
- つまり、本当にすべてのソフトウェアが同一のアーキテクチャに帰結するとすれば
・レイヤーが4つ限定であったり
・UIがMVC的でMVVMなどを否定していたり
・そもそもWeb限定だったり
・中から外の呼び出しがApplicationレイヤーとInterface Adapterレイヤーの間だけで発生するわけがないですし
・中央がEntitiesじゃなくちゃだめで、DDDするななんて
そんな分けないですよね?
- 書籍を読めば、ちゃんと先の図があくまで例示であると書かれています。
もともとクリーンアーキテクチャの発表前は、ヘキサゴナルアーキテクチャとかオニオンアーキテクチャが広く共有されていました。
そしてそれらのアーキテクチャには類似の共通点があることも知られていました。
そしてクリーンアーキテクチャの解説には、それらの著名なアーキテクチャは、先ほどの図のように記述することで単一のアイディアに統合することを例示するために書かれた図なんです。
そう
- クリーンアーキテクチャというのは、どんなソフトウェアにも適用可能な普遍的なアーキテクチャなんです。
あえて言うと、クリーンアーキテクチャを採用しないという選択肢は、基本的には「あくまで基本的には」無いといって差し支えありません。
- では先の図のように実装することがクリーンアーキテクチャではないとしたら
クリーンアーキテクチャについて何を抑えたらよいのでしょうか?
私はつぎのふたつこそ、本質的に重要なことだと考えています。
- ひとつめは先ほども話しました。
ソフトウェアは、より上位レベルの方針にのみ依存せよということ
- ふたつ目は
ひとつめを実現するために、依存性を制御の流れから分離してコントロールするということです。
- てことで、ここからは具体例を見ながら解説したいと思います。
- 今回はSQL ServerのサンプルDBであるAdventure Worksを利用します。
こちらにER図がありますが、これらのテーブルからプロダクト別の総売り上げをCSVに出力するコンソールアプリを作ります。
- アーキテクチャはもちろんクリーンアーキテクチャです。
- では早速コードを見てみましょう!
- さて、先ほどのコードをモデル化したのがこちらになります。
全体的にそう悪くないようにも見えるんですが、少なくとも二つは問題があります。
ひとつは、ぱっと見から分かりますね。
- この部分でUsecaseからPresenterに依存関係ができてしまっています。
- 依存関係はPresentaerからUsecaseである必要があります。
- でも逆になっちゃっていますね。
- という訳で修正してみましょう。
- さて、これでクリーンアーキテクチャになったでしょうか?
先ほどのモデルで見直してみましょう。
- IPerenterが移動したことで依存方向がPresenterからUseCaseに移動しました。
- ちゃんと右下の図の通りになりましたね。
処理の流れは一方通行で、でもすべての依存は外から中に向いています。
- しかしこれはクリーンアーキテクチャとは言えません。
なぜか?
- ここです。
Gatewayがデータベースに依存しています。
- このとおり、依存は外から中に向かわないといけませんが
逆になってしまっていますね。
- でもGatewayはDBを呼び出さないといけないし
DB操作にはSQLが必要ですよね?
GatewayがDBに依存しないなんて実現可能なのでしょうか?
勿論可能ですがちょっと意味が異なります。
- この部分を見てください。
Repositoryの実装ではProductテーブルとSalesOrderDetailに対応する
クラスを作ってDBアクセスし、Repositoryの中でProductSalesインスタンスを
生成していましたよね?
これらのクラスをよく見てみましょう。
- 現在の実装は、リポジトリが完全にデータベースの文脈で記述されているのが見て取れます。
さて「抑えておきたい二つのこと」に戻りましょう。
- 制御の流れと依存方向は分離することで、コントロールできます。
- この部分は決してDBからGatewayを呼べとか
GatewayからDBを読んではいけないとか
GatewayでSQLを使ってはいけないという意味ではありません。
- 大切なのは文脈です
- GatewayがDBへ依存しているというのはつまり
Gatewayのクラス設計がDBのスキーマに依存していることを指しているのです
- ではどうすればよいのか?
- DBがGatewayの文脈で定義されていればよいという事になります。
とは言え、これらのテーブルは別にこの機能のためだけにある訳じゃありませんよね。
ではどうすれば良いのか?
- CQRSもしくはCQSを使いましょうという事になります。
- CQRSもしくはCQSですが、今回のレベルでは基本的に違いがありません。
端的にいうとデータベース操作を更新と参照に分離しなさいという方法論のことで
今回はQueryに該当します。
つまりGatewayの文脈に合わせたQuery用のオブジェクトをDB側に用意すればよいということになります。
- ただCQRSを完ぺきに適用しようというのは簡単じゃないです。
特に既存のシステムに組み込むには多大な努力が必要になりますし、そこまでしてもケースによってはやりすぎになってしまいます。
そこで、今回はDBのビューを使った手軽な方法を詳解したいと思います。
- てことでクリーンアーキテクチャと言えるサンプルコードを見てみましょう。
- 結果的にこうなりました。
DBにはEntityのPdoructSalesの文脈に適用したViewを作成して、Repositoryからはそれを参照します。
こうすることで、DBつまり外から中に依存するように文脈が書き換えられました。
- 完ぺきにこのアーキテクチャを実現していますね!
- つまり大切なのはやはり制御の流れから依存関係を分離することにあります。
- 一般的に依存方向は制御の流れに引っ張られがちです。
- しかし、これらは分離してコントロールすることが可能です。
- そのためには二つの依存関係の間のコントラクト、つまり契約・仕様・お約束を文脈によって制御する必要があります。
抽象的なコントラクトを導出することで、それぞれの具象概念はどちらも、抽象概念であるコントラクトに依存することになります。
- そしてこのコントラクトをどちらの文脈で定義するかによって
具象概念間の依存関係を自由にコントロールすることができるようになります。
- 「アーキテクチャのルールはどれも同じである」
という真意が何となく伝わってきたでしょうか?
- そもそもソフトウェアは良くフラクタルのようだと言われていますよね。
①クラスとクラスがまとまってコンポーネントとなり
②コンポーネントとコンポーネントがまとまってサブシステムになります。そして
③サブシステムとサブシステムがまとまってシステムになります。
④ここではクラス・コンポーネント・サブシステム・システムを、ソフトウェア エンティティと呼ぶことにします。
⑤ソフトウェアエンティティのすべてにおいて、ここまでした話の内容は適用できます。
もちろんサブシステム間は例えばWeb APIになったりするでしょうけど
どの要素間であっても、コントラクトの文脈を管理する事で
制御の流れと依存関係は制御することが可能です。
- こちらに各ソフトウェアエンティティごとの代表劇なコントラクトを上げてみました。
なんならシステム間でのやり取りがCSVとか固定長テキストであっても、そのファイルがどっちのシステムの文脈によってるかによって、システム間の依存関係はコントロールできるわけです。
- 文脈によって、制御の流れと依存方向は分離し制御することが可能です。
- クリーンアーキテクチャがあらゆるソフトウェアにおいて
「アーキテクチャのルールはどれも同じである」
ことを受け入れていただけましたか?
- とうわけで、今日のお話は終わりになります。
最後に簡単にまとめましょう。
- という訳で、本日の内容は以上になります。
ご清聴ありがとうございました。