[1章:設計とアーキテクチャ]
設計とアーキテクチャに違いはない。
"ソフトウェアアーキテクチャの目的は、求められるシステムを構築・保守するために必要な人材を最小限に抑えること"
速く進む唯一の方法は、うまく進むこと。
自信過剰による再設計は、元のプロジェクトと同じように崩壊する。
[2章:2つ価値のお話]
ソフトウェアシステムには、"振る舞い"と"構造"の価値がある。
以下の優先順位でいうと、アーキテクチャ(重要)は1と2、コードの振る舞い(緊急)は1と3。
★よくある間違いは、3を1に昇格させること
1.緊急かつ重要
2.緊急ではないが重要
3.緊急だが重要ではない
4.緊急でも重要でもない
ソフトウェア開発チームには、"昨日の緊急性よりもアーキテクチャの重要性を強く主張する必要がある"。
[3章:パラダイムの概念]
パラダイム(プログラミング方法)は"構造化プログラミング"、"オブジェクト指向プログラミング"、"関数型プログラミング"の3つがある。
これらは、アーキテクチャの3つの大きな関心事、"コンポーネントの分離"、"データ管理"、"機能"に対応している。
[4章:構造化プログラミング]
構造化プログラミングの価値を高めるのは、反証可能なプログラミングの単位を作成する能力。
そのため、現代の言語では、goto文がサポ―トされていない。このことは、アーキテクチャレベルにおいて、機能分割がベストプラクティスだと考えられている理由。
要は、gotoがあると木構造(読んで呼ばれて)の構造が壊れてスパゲッティになるので制限したと。
[5章:オブジェクト指向プログラミング]
オブジェクト指向の強みは、ポリモーフィズムにある。
なぜなら、依存関係の逆転(DIP:Dependency Inversion Principle)が可能になるため。
※AはBのメソッドC()を使いたい。しかしAが直接Bに依存するとA→Bという依存関係になる。
ここでインターフェイスTを導入し、Bがそれを実装すると、ソースコードの依存関係はA→Tだが、実行時の依存関係は、A→Bになる。
例えば・・・
・A(注文処理クラス)はPaymentProcessorインターフェイスに依存
・B1(クレジットカード決済)、B2(PayPay決済)、B3(後払い)はそれを実装
→Aのコードを一切修正せずに支払い手段を切り替えられる
オブジェクト指向とは、"ポリモーフィズムを使用することで、システムにあるすべてのソースコードの依存関係を絶対的に制御する能力"。
これにより、アーキテクトは、"プラグインアーキテクチャ"を作成できる。
[6章:関数型プログラミング]
関数型プログラミングの本質は「代入と可変状態をなくすこと」にある。
OOPがポリモーフィズムによって依存関係逆転を可能にしたように、FPは「状態の不変性」によってシステムの複雑さを抑える。
なぜ代入と変更をなくすのか・・・
代入は"時間に依存するバグを生む"ため(x+=1とか)
要は、時間や順番に結果が左右されないプログラミングの考え方。
つまり、純粋関数(引数だけに依存し、副作用がない(外部状態を読んだり書いたりしない))のこと。
[7章:SRP:単一責任の原理]
"どのモジュールもたった一つのことだけを行うべき"というのはSRPとは別物。
単一責任の原則(SRP)="モジュールはたった一つのアクターに対して責務を負うべきである。(アクターの異なるコードは分割するべき)"というもの。
なんでSPRが必要か・・・
同クラスで複数アクターに対する処理を書いている場合、
・特定アクターへの処理のみを変更したいときに他アクターへの処理に影響が出る。
・コーディングで競合が頻発する。
解決方法・・・
"Facadeパターン"=既存のクラスを複数組み合わせて使う手順を、「窓口」となるクラスを作ってシンプルに利用できるようにするパターンです。
[8章:OCP:オープン・クローズドの原則]
オープン・クローズドの原則(OCP)="ソフトウェアの振る舞いは、既存の成果物を変更せず拡張できるようにするべきである"というもの。
下位クラスを追加するのに、上位クラスを変更してはいけないということ。
なんでOCPが必要か・・・
・依存する側/される側の双方を変更から守るため。
・変更の波及を止める/"改造"ではなく"追加"で対応するため。
DIPとは違うのか・・・
OCPは"拡張の仕方"の原則、DIPは"依存方向"の原則である点で違う。
要は、OCPによる拡張のために、結合を弱めるDIPを用いているということ。
[9章:LSP:リスコフの置換原則]
リスコフの置換原則="スーパークラス(やインターフェース)が保障(期待)する振る舞いをサブクラスも壊してはいけない"というもの。
要は、スーパークラスのルールをサブクラスでも守れということ。
なんでLSPが必要か・・・
・クライアント(その処理を呼ぶ側)を壊す可能性があるため(例:動作が変わる、例外が増える、状態の一貫性が崩れる)
・置き換え可能性が失われると将来の拡張で致命的になることが多い。
解決方法・・・
・継承をやめてコンポジションにする(インターフェースのみ実装とか)
・ミュータブルなsetterを避け、コンストラクタで値を渡す不変オブジェクトにする。
・インターフェースを小さく保つ(ISP)
・スーパークラスで書かれたクライアントテストをサブクラスでも流す。
[10章:ISP:インターフェイス分離の原則]
インターフェース分離の原則(ISP)="クライアントは、
自分か利用しないメソッドに依存すべきではない"という考えに基づき、
大きなインターフェースを小さく分割することによって各クラスが必要とする機能だけに依存させる設計原則。
例えば・・・
User1~3ごとの処理が、Operationsクラスにo1()~o3()としてある場合、
ユーザーごとのインターフェースを作成して、それぞれをOperationsクラスに実装。
[11章:DIP:依存関係逆転の原理]
DIPに関するコーディングレベルのプラクティスは以下。
・変化しやすい具象クラスを参照しない
・変化しやすい具象クラスを継承しない
・具象関数をオーバーライドしない
・変化しやすい具象を名指しで参照しない
これらの依存性管理では、"Abstract Factoryパターン"が有効。
Abstract Factoryパターンとは・・・
インスタンスの生成を専門に行うクラスを用意することで、整合性を必要とされる一連のオブジェクト群を間違いなく作成するためのパターン。
具象クラスの参照をファクトリに閉じ込めることでクライアントを具象から切り離す。
[12章:コンポーネント]
コンポーネント="システムの一部としてデプロイできる最小限のまとまり"のこと。
メモリの進化によって、動的にリンクされたファイルを実行時にプラグインできるようになり、コンポーネントプラグインアーキテクチャを手に入れた。
[13章:コンポーネントの凝集性]
コンポーネントの凝集性="一緒に変更されるものは同じコンポーネントにまとめる"
これによって変更の波及を小さくし、保守性・リリースの単位を明確にする。
関連原則は3つ・・・
・再利用・リリース等価の原則(REP)
→ 再利用可能な単位は、リリース可能な単位であるべき(=コンポーネント=リリース単位)
・閉鎖性共通の原則(CCP)
→ 一緒に変更される要素は同じコンポーネントに置く
・全再利用の原則(CRP)
→ 一緒に再利用されないものを同じコンポーネントに入れてはいけない
★REPとCCPはコンポーネントを小さくするアプローチ、CRPはコンポーネントを大きくするアプローチを取る。
→ バランスを取る & 今必要なアプローチの判断が必要
なぜこれらの原則が必要か・・・
・変更の局所化
・リリース単位の明確化
・チーム分業がやりやすくなる
・循環依存を避けやすくなる
※個人的なまとめ
コンポーネントは、プロジェクト単位にしたり、ファイル単位にしたりと色々調整できる。
もしプロジェクト単位のコンポーネントかつ、ほかのプロジェクト連携がない場合は本原則はあまり気にしなくてもOK。
要は、コンポーネントはデプロイの単位になるから、
ほかと連携する際はそれを考慮して分割する必要ある
& 一緒に変更が必要になるものは同じコンポーネントにまとめる必要があるということ。
[14章:コンポーネントの結合]
コンポーネントの関連を扱うものとして以下の3つの原則がある。
・非循環依存関係の原則(ADP)
・安定依存の原則(SDP)
・安定度・抽象度等価の原則(SAP)
非循環依存関係の原則・・・
これはコンポーネントの関係で、循環依存があってはいけないというもの。
※循環依存とは、A→BかつB→Aの依存性のこと。A→B→C→Aも同じく。
解決方法・・・
・依存関係逆転の原則の適用
・AとB両方が依存する新しいコンポーネントを作成し、A,Bの依存性をこのコンポーネントに移動
安定依存の原則・・・
これは、コンポーネント従属は安定度の高い方向に依存するべきというもの。
コンポ―ネントの不安定さの指標は、「I=ファン・アウト÷(ファン・イン+ファン・アウト)」で表せる。
※I=1・・・安定度最小(変更での影響小、変更自体が頻発するもの)。
I=0・・・安定度最大(変更での影響大、変更自体が少ないもの)
つまり、従属関係はI= 1→0.5→0.3→0.25→0.1→0みたいな順にするべきということ。
安定度・抽象度等価の原則(SAP)・・・
これは、コンポーネントの抽象度は、その安定度と同程度でなければいけないというもの。
コンポーネントの抽象度を表す指標は、「A = Na ÷ Nc」で表す。
※Na・・・抽象クラス数、Nc・・・クラスとインターフェースの総数
Aを縦軸、Iを横軸にとった時、(0,0)周辺を苦痛ゾーン、(1,1)の周辺を無駄ゾーンと呼び避けるべき。
※苦痛ゾーンは、安定度が高い&抽象度が低いため2重苦になる。
無駄ゾーンは、最大限に抽象化されているにもかかわらず、安定度が低い(=使用されない)ためゴミとなっている。
コンポーネントは、(0,0)と(1,0)を結ぶ直線上(主系列と呼ぶ)に近いほど良い。
主系列との距離は、「D = |A + I - 1|」で表される。
[15章:アーキテクチャとは]
アーキテクチャの本質・・・
ソフトウェアアーキテクチャの目的は「システムをできるだけ長く生かし続けること」。
そのためには 変更しやすい構造 にする必要がある。
つまり、アーキテクチャは「柔軟性をいかに確保するか」が主眼であり、パフォーマンスやツール選びは二次的。
歴史的な背景・・・
過去には「ハードの制約」に縛られていたので、ソフトウェアの設計はハード性能に依存していた。
しかし今ではハードは圧倒的に進歩し、ソフトウェアの構造をハードに合わせる必要はほぼなくなった。
それでも多くの人が「フレームワーク」「データベース」などに設計を引っ張られてしまうが、それは誤り。
アーキテクチャの本当の価値・・・
良いアーキテクチャは フレームワーク・UI・データベースに依存しない。
ビジネスルール(本質的な振る舞い)を守り、外部要素は取り替え可能にする。
これによってシステムは長期間にわたり「変化に耐えられる」。
[16章:独立性]
アーキテクチャの目的・・・
システムを長く保守・進化させること。
そのために、コンポーネントが「他に縛られずに」変更できるようにする必要がある。
独立性が必要な理由(4つの観点)・・・
・デプロイの独立性
あるコンポーネントだけを差し替えてデプロイできるようにする。
→ 「全部まとめてリリース」しなくて済む。
・開発の独立性
チームごとに異なるコンポーネントを並行して開発できる。
→ 「依存関係が複雑で足を引っ張り合う」状態を避けられる。
・理解の独立性
コンポーネントを単体で読めば意味がわかる。
→ 巨大なシステムを全部把握しなくても、一部分だけ理解できる。
・テストの独立性
コンポーネント単位でテストできる。
→ 結合して全体を動かさなくても品質を担保できる。
実現するための手段・・・
コンポーネント分割を工夫する。
例:UI、ビジネスルール、データベースアクセスを分離する。
依存関係逆転の原則 (DIP) を活用して、重要なルール(ビジネスロジック)が外部に縛られないようにする。
「変更が伝播しない」ように設計することが鍵。
[17章:バウンダリー:境界性を引く]
バウンダリー(境界)= モジュール/コンポーネントの「責務境界」。
内部実装を隠して外部と契約(インターフェース)だけでやり取りする。
目的・・・
依存を制御し、変更の波及を止め、差し替えやテストを容易にする。
SRP(単一責任の原則)は境界決めの有力な基準の一つ:境界は「異なるアクター(利用者)」や「異なる変更理由ごと」に引く。
境界を守る具体技術・・・
・ポート & アダプタ(Hexagonal)
内側はポート(抽象)、外側はアダプタ(具象実装)で接続。
・依存関係逆転(DIP)
高レベルモジュールは低レベル実装に依存しない。抽象に依存する。
・DTO(Data Transfer Object)
境界でデータの翻訳を行い、内部モデルを外に漏らさない。
・ACL(Anti-Corruption Layer)
外部の設計が汚い場合に翻訳・仲介する層。
・SRP(単一責任)
境界を決める有力な基準。誰が(どのアクターが)使うか/何が変わるかで区切る。
つまりGUI、ビジネスロジック、DB等で境界を作り、それぞれをプラグイン化。
そうすることでお互いを意識しないで開発できるし、後から変更が容易ということ。
[18章:境界線の解剖学]
アーキテクチャは、コンポーネントとそれらを隔てる境界で決まる。
境界は様々な形(同一アドレス空間/プロセス間/サービス)をとり、協会を超える度に「関数呼び出し、データのマーシャル/アンマーシャル、コンテキスト切り替え(プロセス・スレッド)」といった振る舞いが発生する。
これを正しく扱うためにソース構造や契約を決め、境界越えの実行形態に合わせた実装を行う必要がある。
境界を超えるとどうなるか・・・
①同一アドレス空間(in-memory/ライブラリ呼び出し)
・直接オブジェクト参照やポイントを渡せる。最も軽い。
・境界は主にインターフェイスで実現
②ローカルプロセス(プロセス間)
※別のエントリポイント(main) を持つファイル/クラスのこと
・アドレス空間が分離される。OSシステムコール、マーシャリング/アンマーシャリング、コンテキストスイッチが発生。
・データはシリアライズして渡す。レイテンシー(遅延)、コピーコストあり。
③サービス(リモート/ホスト間)
・ネットワーク越し。最も強い境界。遅延、部分障害、認証・セキュリティ、バージョニングなどの運用面の考慮が必須。
・下位レベルサービスは上位サービスにプラグインされるべきで、上位のコードに階の物理情報(URI等)を埋め込んではいけない。
ソース管理・配置に関するルール・・・
①境界の強さに応じてソースを配置する
・同一アドレス空間なら同レポジトリ/モジュールに置いてもいい。
・プロセスやサービス境界なら、別プロジェクト/別ビルドアーティファクトにして、契約(インターフェース、IDL、API)だけを共有する。
※IDL・・・異なるプログラミング言語やシステム間で通信を行うためのインターフェースを定義する言語
②上位モジュールに下位の物理情報を埋め込まない(例:URIや、実装クラス名)
・実行時の接続情報は設定/ファクトリ/DI が担う。上位コードは抽象(契約)にだけ依存する。
③下位は上位にプラグインされる(下位のサービスは上位で差し替え可能であること)。
[19章:方針とレベル]
方針 = ビジネスのルール/設計方針は、システムの入力・出力(I/O)からなるべく遠い層に置き、実装(メカニズム)はその方針に従うプラグインとして設計する。
依存は“下位(実装)→上位(方針)”の向きにして、方針を守れるようにする。
※I/Oから遠ざける = ビジネスの方針(ルール)を、UI・DB・ネットワークなどの入出力の影響を受けない(依存しない)場所に置く
○良い(遠い)例:core/order/DiscountPolicy が純粋な計算だけをする。引数は値で、外部ライブラリや DB は参照しない。
×悪い(近い)例:Controller が OrderEntity を直接読み書きして、DBのSQLやフレームワーク型のオブジェクトをビジネスロジックそのものに混ぜている。
[20章:ビジネスルール]
ビジネスルールは「お金を生み出す/節約するための最重要ルール」。
これらは I/O(UI/DB/ネットワーク)から独立して定義され、テスト・再利用・長期の安定性を持つべきである。
用語整理・・・
①最重要ビジネスルール
・企業の価値に直結するルール
・自動化されていないプロセスにも存在するルールも含む(=システム化されているかは問わない)。
②最重要ビジネスデータ
・ルールを実行するために必要なデータ
・これらはルールと密接に結びつく。
③エンティティ
・「最重要ビジネスデータ(プロパティ)」と「最重要ビジネスルール(振る舞い/関数)」を同一モジュールにまとめたオブジェクト。
・DB/UI/フレームワークに依存しない純粋なドメインオブジェクトであるべき。
例)貸出残高を更新する、与信条件を評価する等
④ユースケース
・アプリケーション固有のビジネスルールの実装(「どう使うか」を定義)。
・入力(RequestModel)を受け取り、エンティティのルールを呼び出し、出力(ResponseModel)を返す。
・UI の見た目や DB の SQL を知らない。インターフェース(ポート)で定義され、実実装は Interactor が行う。
例)ユーザーからの注文を受け、在庫確保→決済→通知を行う等
[21章:叫ぶアーキテクチャ]
アーキテクチャは、システムで使用しているフレームワークではなく、システムそのものについての情報を伝える必要がある。
ソースコードを見たときに、会計システム、在庫管理システム等だとわかるようなものであるべき。
また、フレームワークやDB等のその他の環境の問題やツールの意思決定は延期・留保できる状態にしておくべき。
[22章:クリーンアーキテクチャ]
ヘクサゴナルアーキテクチャ、DCIアーキテクチャ、BCEなどいろいろなアーキテクチャがあるが、すべて以下のことを実現するためのもの。
・フレームワーク依存
・テスト可能
・UI非依存
・データベース非依存
・外部エージェント非依存
有名な同心円の図・・・
・Enterprise Business Rules(エンティティ)
ビジネスロジックを表現するオブジェクトが所属するレイヤ―。
ドメイン駆動設計でいうところのエンティティはここに入る。
・Application Business Rules(ユースケース)
ソフトウェアは何ができるかを表現する
Enterprise Business Rulesに所属するオブジェクトを協調させ、ユースケースを達成する。
ドメイン駆動設計でいうところのアプリケーションサービスはここ。
・Interface Adapters(コントローラー、プレゼンター、ゲートウェイ)
入力、永続化、表示を担当するオブジェクトが所属する。
※入力 = Application Business Rulesに伝えるためのデータ加工。
永続化 = データの保存
表示 = 結果の表示
一般的なMVCフレームワーク、単体テストクラスなどはここ。
・Frameworks & Drivers(UI、DB、デバイス、ウェブ、外部インターフェイス)
Webフレームワークや、データベース操作オブジェクトなどのギークなコードはここ。
フロントエンドのUIなどもここ。
同心円の外側から内側への依存関係になるような作りにしないいけない。
[23章:プレゼンターとHumble Object]
プレゼンターはHumble Objectの一種であり、アーキテクチャの境界の特定と保護に役立つもの。
Humble Object・・・
ユニットテストを実行する人が、テストしにくい(Humble Object)/しやすい振る舞いを分類するためのデザインパターン。
GUIのテストは難しいが、PresenterとViewの2つのクラスに分けられる。
GUIのようにそもそも View がテストしづらいものであるので、「ただ表示する」ということだけに特化させてテストしないで済むようにしようという考え
[24章:部分的な境界]
境界(コンポーネント/プロセス/サービス)を最初から全部作るのはコストが高い
→ 今は単体で動く実装にしておきつつ、将来「安全に」「段階的に」切り出せるように設計上の「シーム(継ぎ目)」を作っておく。
方法・・・
①最終ステップを省略する
物理的に別のコンポーネント(別プロセス/別jar/別サービス)にするための最終デプロイ分離ステップを当面しない方法。
実際は、同一プロセス内で動かすが、設計上は分離可能にしておく。
どうやる?・・・
・フォルダ/モジュール分離をしておく → でもビルドはひとつにまとめる。
・インターフェースを定義し、具象実装をそのモジュールに配置。
・将来、別jarに切り出すときは ビルド設定だけ変更すれば良い。
②片方だけの境界
境界の片側だけをインターフェース化/抽象化しておく方法。
つまり「呼び手側(あるいは提供側)」のどちらか一方だけが境界の存在を意識する設計。
どうやる?・・・
・内側がインターフェースを持つ:内側(呼び出される法)にインターフェースを置き、外側(呼び手側)が具象実装を提供する。
③Facade
外部サブシステム群や複雑なAPI群に対して問合せ先(Facade)を作る。
外側からはこのFacadeにだけ依存させ、内部は自由に変える。
どうやる?・・・
・複数の機能(DB + キャッシュ + 外部API など)をまとめて OrderFacade や PaymentFacade を作る。
・アプリの他の部分はこの Facade のメソッドだけを呼ぶ。
・将来、その Facade 自体を別プロセスにしても、呼び口は変わらない(ただ呼び方は RPC に変えるだけ)
[25章:レイヤーと境界]
境界は、一度設計したら終わりではなく、継続的に見直すべきであるということ。
常に今のコストと、将来のコストを見積もっておくべきとのこと。
[26章:メインコンポーネント]
メインコンポーネントは、究極的な詳細(最下位レベルの方針)であり、システムの最初のエントリポイント。
クリーンアーキテクチャの円でいうと、一番外側のフレームワーク層に属する。
やることとしては、
・設定ファイルや、設定変数の読み込み
・インフラ(DB接続、UI、外部サービス)の準備
・インターフェースト実装の紐づけ(DI(DepadencyInjection):依存性注入)
・アプリケーションの開始
[27章:サービス:あらゆる存在]
サービス指向アーキテクチャや、マイクロサービスアーキテクチャは最近普及したものだが、これらはアーキテクチャではない。
よく言われるこれらのメリットは・・・
①サービスがお互いに分離されているように見える
②サービスが開発とデプロイを独立させているように見える
ただこれらは、部分的にしか正しくない。
理由・・・
・①について
プロセッサ内やネットワーク上にある共有リソースについては分離されていないため
例えば・・・
・サービス間で渡されるデータレコードに新しいフィールドが追加
→そのフィールドを使うすべてのサービスに変更が必要。
・データの解釈についても強く合意しておく必要がある。
つまり、サービスはデータレコードと強く結びついている。
・②について
サービスは必ずしも独立して開発・デプロイ。運用できるとは限らない。
→データや振る舞いが結びついている限り、開発・デプロイ・運用は調整が必要。
[28章:テスト境界]
テストもシステムの一部だが、最も独立したコンポーネントであり、
テストがシステムの変化に左右されないようにシステム側にテストしやすい境界(テストAPI)を設けよとのこと。
ポイント・・・
・テストはシステムの一部
テストは実行可能なコンポーネントであり、テストコードもソフトウェアのアーティファクトとして設計上扱うべき。
・運用には不要
本番システムの本体はテストを必要としない(テストはビルド・CI・開発用の存在)
・最も独立したコンポーネント
テストは自由に組み合わせ、起動できるべきで、本体のUIや外形(GUI、外部サービス)に強く依存してはいけない。
・テストも同様に境界を必要とする
テストはどこをどう検証するかを明確に検証するため、アプリケーションの構造から切り離せるAPIを用意するのがいい
[29章:クリーン組み込みアーキテクチャ]
依存性の逆転、境界、責務の分離などのクリーンアーキテクチャの本質はそのまま有効だが、組み込み特有の制約(メモリ、CPU、割り込み等)に合わせて設計の実用的な調整が必要。
[30章:データベースは詳細]
DBはドメインの一部(Entity)ではなく、外側の実装詳細。
ドメインのエンティティは、ストレージの仕方(テーブル、列、インデックス、SQL)に依存してはいけない。
[31章:ウェブは詳細]
ウェブも要はGUIであると。そしてGUIは詳細だからウェブは詳細。
[32章:フレームワークは詳細]
フレームワークも詳細なので、EntityやUseCaseにフレームワーク特有のアノテーションや依存を書いてはいけない。
代わりに、MainコンポーネントでDIを行い、内部は純粋なオブジェクトにするなど必要。
フレームワークを切り離せなくなるような実装にはしないこと。
[33章:事例:動画販売サイト]
SOLID原則に従ったコードの構成にしておくことで、システムをデプロイするときにも思い通りに進められる。
デプロイ可能な単位でコンポーネントをまとめることもできるし、変更も容易。
[34章:書き残したこと]
設計や、コーディングに関するアプローチ・・・
・レイヤーによるパッケージング(レイヤードアーキテクチャ)
コントローラ層、ビジネスロジック層、永続化層で分ける手法
最初は、レイヤードアーキテクチャに従うのが得策。
とりあえず動くものを複雑になりすぎないように手早く作るに優れた手法
ただ、ソフトウェアの規模が大きくなると3層では手に負えなくなる
・機能によるパッケージング
関連する機能、ドメインの概念、集約ルート(ビジネスロジック)に基づいて分割する手法。
レイヤードよりも、ビジネスドメインについて叫ぶようになる。
・ポートとアダプタ―(ヘキサゴナルアーキテクチャ、BCE)
ビジネス(ドメイン)に関するコードを技術的な実装(フレームワークやデータベース)から切り離して独立させることが狙い。
内側(ドメイン)、外側(インフラストラクチャ)で分けることが多くある。
※例えば、Web(外側)、ドメイン(内側)、データベース(外側)のような
・コンポーネントによるパッケージング
ビジネスロジックと永続化コードを一つにまとめてコンポーネントにする手法。
大きな利点は、もし注文に関するコードを書きたいなら、OrdersComponentだけを見ればいいという点
ただ、これらのアプローチはPublic修飾子を適切に使わないとコードベースのどこからでも(controllerからrepositoryなど)アクセスできるようになるので崩れる。
対処法・・・
コンポーネントのエントリポイント(大体インターフェース)となる部分だけをPublicにするというアプローチ
OSGi等のモジュールフレームワーク
コードをそれぞれ別のソースコードツリー(モジュール、プロジェクト)に分割する