SpringはどうやってDIしているのか
残念ながらちゃんと理解できたとは言い難いけど、SpringContainerの起動時にどんなことをしているのか何となくイメージが沸いて、とてもおもしろかった。
Bean定義の登録
- AnnotationConfigApplicationContext
- @ConfigurationがついているクラスのBean定義をBeanDefinitionに変換
- AnnotatedBeanDefinitionReader
- @Conditionalでスキップ可能
- Beanの名前を決める
- DefaultListableBeanFactory
- ConcurrentHashMapとしてインスタンス変数にBeanDefinitionが保存される
- Bean生成処理を担う
Component scan
- refresh
- BeanFactoryPostProcessor:フックして処理を挟める
- ConfigudationClassPostProcessor:component scan
- classファイルを探す
- バイトコードを解析
- クラス名やAnnotationをチェック
- Beanとなるクラスのみを選択してBeanDefinitionで表現
- BeanDefinitionRegistryに登録
Bean生成
- singletonスコープのbeanを生成
- BeanDefinitionを取得(親Bean定義をマージ)
- getBean ⇒ キャッシュがあれば返す、なければ生成する
- この中でインジェクション(Autowiredはコンストラクタインジェクション)
- DefaultListableBeanFactoryでdependencyを解決する、その際にgetBeanが再帰的に呼ばれる
- Reflecition(newInstance)でBeanを生成
- init-methodの呼び出し
- DefaultSingletonBeanRegistoryにキャッシュ
- ここまでで、ようやくAnnotationConfigApplicationContextのコンストラクタ(refresh())が終了
まとめ
- BeanFactory / BeanDefinitionReaderがあればDIはできる
- DI以外をやるためには、ApplicationContextが必要
- DefaultListableBeanFactoryの仕事多すぎ!継承が複雑!(各インタフェースの役割を把握すると読みやすい)
Javaエンジニアのためのブロックチェーン入門
話題になっている
- 来年が盛り上がりのピークになる??
- 仮想通貨取引所は淘汰されるはず(今までは免許がなくても参入でき参入障壁が低かった)
- 2017/5から金融の法律が変わる
- メディアと金融業界が先に加熱
- 技術スタートアップが続々誕生している、ここから3年で淘汰が進むだろうが、残るものは確実にあるはず
- 巨大な中央機関不要はアジテーションとしても、仲介手数料モデルの破壊はあるはず
ブロックチェーン
- bitcoin
- P2Pネットワーク上の分散型台帳
- 国境不在・世界単一市場。決済コストも安い。
- ネットワーク内で合意が取れた段階で取引が確定する(リアルタイムではない)
- 不特定多数のP2Pネットワーク(悪い人もたくさんいるはず)、P2Pノードには何の得がある?、一貫性を維持できる?、改善の心配は?
- ブロックチェーン
- bitcoinはブロックチェーンを利用したアプリケーション
- P2Pノードに同一の記録を同期
- 一定のルールで正当性を決定、整合性を担保
- P2Pノード:マイナー
- パブリックなもの(bitcoin)とプライベートなもの(閉じたパートナーの中で)がある
- 一定分の取引の集まり⇒ブロック
- 複数ノードで並行に処理した結果を、他ノードが検証。不正な結果を排除する。マイナーはこれで報酬を得る。⇒インセンティブ設計が素晴らしかった
- 複数の正しい結果からただ一つの結果を採用する(コンセンサスアルゴリズムを決めておく)
- 権威者がいなくともグローバルに一意に整合性が取れる
スマートコントラクト
- 自力で執行される契約処理(例:自動販売機)、概念は90年代に提唱
- データと契約処理をブロックチェーンに委ねる
- データそのものも改ざんできない
etherenum
- スマートコントラクト用のプロットフォーム、言語はSolidity
- 値だけでなく、取引の状態遷移全体の記録台帳としてブロックチェーンを利用
- 自由にコントラクトを作り独自データを扱える
- clientからはjsonを投げる
- eherenum for Spring(ULFIRE)
- etherenumへのアクセスを隠蔽、Spring Dataっぽく分散台帳にアクセス
- @Etherenum、EtherenumCnfigudation
- @TransactionListener
- コントラクト記述はとても難しい。ここが真の勝負。
- データサイズに応じたETHが発生する、データそのものは別管理(IPFS)でそこへのリンクを張る
- データは閲覧される可能性がある、シビアなデータはプライベートなストレージへ(個人情報、BtoBの取引先)
講演を聞いて、取引自体が偽物でないことをどう担保しているのか、とか、スパム的な取引をどう防いでいるのかが気になったけど、下記が分かりやすかった。
Spring CloudでDDD的なマイクロサービスを作ってみる
境界づけられたコンテキスト
- どこのチームもレガシーとの闘い、だんだん開発スピードが落ちてくる
- レイヤー分けしていたり、コーディングルールもある
- なのに数年経つとだんだん手を出せなくなってくる
- データモデルがどんどん大きくなっていく
- あちこちでデータを触っているのでどこがどこに影響を与えるか分からなくなる
- IFさえしっかりしておけば、中は自由にできる
- 商品マスタなどはコピーして持っている
- 例:商品はあらゆるコンテキストに関わる(注文、出荷、請求、在庫、決済、カタログ、分析・・・)
- 細かく切れば、それぞれのコンテキストから商品を触ることが重要
- DBを直接のぞけば、そのときの開発は早く進むが・・・
ユビキタス言語
- チーム内の共通言語が英語なって気が付いたこと
- 一生懸命話している単語が、会話/モデル/コードで共通の言葉になっている
リポジトリ
- 「そのカラムに」「〇と□をjoin」
- 業務とは関係ない、RepositoryがDomain Logicにはみ出していた
- Repositoryに隠蔽
- 結果として、Domain Logicが凝縮され、コードを読めばわりと業務が分かるようになった
デモ:DDDスタイル
- 実践DDDは面白く、著者のコードも勉強になる
- 前は、ドメインモデルも、Entity構造、Enumで区分値などを知っていた⇒リポジトリに閉じ込めた
- 実践DDDでは、「集約がトランザクションの境界」を強調している
- ユビキタス言語をなるべくクラスにする(面倒くさいし、冗長な処理はあるけど)、例:OrderId
- repositoryの中で、DOMAのDaoを使っている
- Domain Serviceを定義するのは、集約と集約を取り扱うとき
これから先に備えての素振り
- 集約同士の整合性を保つのにドメインイベント:Kafka
- クラスを知っていないとserialize / deseriaizeできない
- jsonを使って、必要な項目だけを取ってくるようなUtilityを用意する
- CQRS
- Domain Modelはなるべくputとgetだけにする
- それぞれのアプリ用では、query用にViewModelを作る
いつマイクロサービスする?
- チームが分かれるとき
- DDDスタイルやイベントやり取りは、1つのアプリでもできる
- 1つのアプリの方が、ブレークスルー的なモデルの変更に対応しやすい
- 将来的にマイクロサービスになっていけるアーキテクチャ(変化していけるアーキテクチャ)
- 最初はDaoでもよい
- 生き延びてドメインが分かってきたらDomain Model
- 大きくなってきたらBCを分ける
- チームが大きくなってきて分かれるときには、サービスを分ける
- 今回のデモ
- 認証/userサービス/config-service/discovery-service
- メインのアプリは最初は1つで作った
- State Sourcing ⇒ Event Sourcing
ドメイン駆動設計とScala 〜既存プロジェクトへの適用〜
Why DDD?
- 常にドメインモデルを意識した設計をするので、行き過ぎた単純化・汎用化を防ぐことができる
- Serviceに詰め込みすぎて肥大化するのを防ぐ
- 各ドメインモデルの責務が明確になり、処理が散らばらない
- 解決すべきは、技術的問題よりもビジネスの問題のこともある
HRMOS採用管理での課題
- 巨大なモデルが存在(一つのclassでデータ持ちすぎ)
- methodを持っていない
- ドメインモデル貧血症
- XXService, XXUtilにビジネスロジックが散らばっている
- ソフトウェアの関心事とビジネスの関心事が分離できていない
モデルの再定義
- ユビキタス言語が適切に定義されているか?
- 専門的すぎる用語は咀嚼して自分たちの言葉で再定義
- チーム全員がその言葉を聞いて勘違いすることなく、同じ物事を思い浮かべることができるまで言葉を洗練する
- 境界づけられたコンテキストの中で通じる言葉であればいい
- 言葉の意味が変わらない範囲 = 境界づけられたコンテキスト
- モデルを明確に分けていなかった
- requestを受け付けたモデル?ドメインモデル?response用のモデル?
- package分けによるサポート
- clean architecture
- controller
- presenter
- application/service:処理の振り分け+トランザクション制御
- domain:ドメインモデル
- infrastructure/repository:永続化
- gateway:外部サービスとのやり取り(ex. メールサービス)
- util:ビジネスロジック以外のユーティリティ
- 見つけ出した言葉をドメインモデルに落とし込む
Entity / ValueObject / Aggregate
- Entity:同一性を識別することが必要になるモデル
- ValueObject:イミュータブルに保つ値、状態を持たない(Scalaだとval / case class)
- 誕生日:BirthDate型を用意して、calcAgeのようなメソッドを用意してやる
- 同姓同名の人がいても別名なので人としては識別が必要、名前はただの文字列なのでValueObjectとして扱えばよい
- 主体によって、Entity / ValueObjectは変化する
- 千円札はValueObject、でも日本銀行からすれば識別が必要でお札はEntity
- Aggregate:Entityのライフサイクルを同一にするものを集める
- トランザクション整合性を取る範囲、集約の外側は結果整合性
- 自動車は、タイヤ・車両を含んだ集約のルートエンティティになる
- 集約の内部のオブジェクトにアクセスしてよいのは集約のルートのみ、ルートを経由して制御すること
- 加速する ⇒ 車に対して指示を出す ⇒ 車がタイヤやエンジンを制御する
- モデルの一貫性を保つためのもの、そこに責任を負う範囲
- PlantUMLで書いてみた
集約が大きすぎる
- CQRS
- すべてがドメインモデルだと重くなる
- queryはドメインモデルをスキップ
- ドメインモデルとRepositoryの関係が悩ましい
Payara Micro の設計と実装
Payara micro
- Embedded release of Payara
- Hazelcastによる自動クラスタリングがサポートされている
- Components
- Embedded GlassFish
- Hazelcast
- Payara Service:監視サービス
- Health check, notification, request tracking
- Core
- fish.payara.micro
- PayaraMicro(Bootstrapp) / PayaraMicroRuntime(Instance) / BootstrapException
Startup
- javaコマンドでbootstrapのjarを呼ぶ(その時にoptionを付ける)
- 多くの製品のshell scriptの中身はそうなっている
- メインクラス:PayaraMicro⇒Kernelを起動(jarファイル自身)⇒Payara Microが起動した状態
Shutdown
- Ctrl + C => shutdown hookで埋め込みGlassFisをshutdown(もちろんAPIでも可能)
自動クラスタリング
- Hazelcastべったり
- HazelcastはJCache providerでもある
- node立ち上げ
- Hazelcastの初期設定(payara microのdefault or 独自)
- Hazelcast instanceを起動 ⇒ caching providerを作成 ⇒ JNDIに登録
- Hazelcast clusterへの追加/削除は、自動的に実施される
- HTTP Auto-binding
- Hazelcastが5900から探していくのを真似ている
Application Deployment
- deploy方法
- warを直接指定
- ディレクトリを指定して、その中のwarをすべてdeploy
- maven repository
- 自分自身に埋め込まれているwarをdeploy(Uber jar)、META-INF/deploy
- GlassFish Deployerを使っており、EARなどもdeploy可能
- ROOT.warが、コンテキストルート"/"にdeployされる
- それ以外のコンテキストルートの設定は、warファイルに依存する(deployment descriptorなど)
- maven repositoryから取るパターン、コンテキストルートはartifactIdが使われる
Uber jar
- 実行可能jarの中にアプリケーションとruntimeが両方入っている、単体でwebアプリとして動かせる
- Spring Boot, WildFly swarmなど
- PayaraMicro#packageUberJar
- 自分自身のファイルをすべてコピー
- deploy対象のwarファイルを集めてすべてコピー
- 設定を埋め込む
- jarに書き出す
Payara MicroのConfiguration
- hazelcast.xml
- domain.xml
- GlassFishのdomain設定ファイル(config beanをjackson形式で書き出したもの、実行中に書き換えられる)
- Loggingはデフォルトではコンソール出力
- -logToFileオプションで指定可能(ファイル出力する場合も、コンソールにも出力される)
Payara Micro API
- Java EE7 Web Profile + α
- 設定&オペレーション用のAPIを用意している、コマンドラインオプションをカバーしている
PayaraMicro.getInstance().addDeployment("app.war").bootStrap();
- PayaraMicro.bootStrapの戻り値は、PayaraMicroRuntimeのinstanceを返す(以前はvoidだった)
- 設定はgetter/setter、ただしsetterは自分自身が戻り値なので、method chainで指定可能