セキュアバイデザイン を読んでいるので、備忘メモ
Chapter 1: Why is Design important
- セキュリティを個別の機能ではなく、concern として捉えて設計に組み込む
- Confidentiality: 機密性
- Integrity: 完全性
- Availablity: 可用性
- Traceablity: 追跡可能性
- 開発者にセキュリティ専門家と同等の知識、意識を持ってもらうのは現状では難しい
- ドメインとして何を満たすべきかという設計を意識させる
- サイズの確認
- lexical contentの確認
- syntaxの確認
- security in depth
- ドメインプリミティブに、特定のドメインに関する知識をカプセル化する
Chapter 2: Tragedy of Hamlet
- e.g. 価格がこのドメインでどのように機能するのかを理解できているのか
- 浅いモデリング
- この概念をどうしたら理解できるのか
- 明示的な表現をする
Chapter 3: Concept of Domain Driven Design
- critical complexity: 対応しなければならない複雑さ
- ドメインモデルを機能させるために
- シンプルである
- 厳格である
- 深く理解したことを捉える
- 実用的に最善
- 誰もが理解できる言葉を使う
- システムに要求されることに対して最も適したモデルを選択する
- ドメイン言語
- ユビキタス言語: 用語の意味を一貫させる, UI, マニュアル, 仕様書, ソースコードなどで統一して使う
- エンティティ
- identityを持つ, 原則不変, 他のエンティティや値オブジェクトを持つ
- 所有するオブジェクトの操作を調整する責務を持つ
- 一意性の範囲は状況による
- value object
- identityを持たない, 値で識別する
- immutable, conceptual whole, 制約が厳密, 生存期間が短い
- エンティティを参照できる
- aggregate
- モデルに含まれる要素をまとめて、概念的な境界を構築する
- トランザクションの管理可能な境界
- boundaryとrootがある
- rootはaggregateの特別な1つのエンティティ
- rootは境界内のオブジェクトへのアクセスを制御する
- root以外のエンティティはローカルで一意
- root以外のエンティティや値オブジェクトは外部から参照可能だが、保持できない
- aggregate内のオブジェクトの不変条件はトランザクションごとに維持される
- bounded context
- 異なるコンテキスト間でのやりとり
- 同じ名前でも意味が異なる場合がある
- DRYしすぎない
- context map
- 異なるコンテキスト同士がどのように境界を超えてコミュニケーションを取るのか
- コンテキストの境界を見つける
Chapter 4: Technic to implement safety
- 特定の状況下で、ある実装テクニックの方が他よりなぜ優れているかを理解していれば、優れた方を採用するはずだ
- immutability
- 異なるスレッドで共有されても安全
- Javaならfinalを使う
- fail-fast
- 事前条件、契約
- DRYにするためにドメインプリミティブを使う
- validation
- オリジン、サイズ
- lexical content, syntax, semantics
Chapter 5: Domain Primitive
- その存在だけで、その値が有効であることを保証する厳格な値オブジェクトのようなもの
- 不変条件を持つ
- ドメインにおいてその概念が何なのか正確に定義する
- 有効なデータ
- 妥当性
- APIなどで外部に公開されないようにする
- DTOを介すなど
- Read-Onceオブジェクト
- 意図しない使われ方を検知する
- 機密性の高いデータをシリアライズさせない
- サブクラスや拡張を作れない
- 確証バイアス: ここまで問題がないから、この後も問題ないだろう
汚染解析: taint analysis
- あえて汚染したデータを与え、妥当性確認がされていることを検証する
- 汚染源: taint source
- 汚染除去: untainting
- 汚染伝搬ポリシー: propagation policy, 処理や他データとの組み合わせの結果、汚染されたかどうかの指針
- 汚染シンク: taint sink
Chapter 6: Guarantee Integrity
- 状態の変化をドメイン駆動設計のエンティティが扱うようにモデリングする
- 生まれた時から正しい状態のエンティティにする
- ORMには引数なしコンストラクタをチュートリアルにしているものがあるので注意する
- ドメインモデルと永続層を分離する
- 永続層を扱うクラスを別のパッケージにまとめる
- 永続層を扱うオブジェクトが不完全な状態で生成される手段を公開しない
- ドメインモデルと永続層を分離する
- クラス内のListオブジェクトは外部に公開しない
- 読み取り専用のビューを返す
fluent interface
- 流れるように生成する
- コマンド・クエリ分離の原則に反してしまう
- 複数のフィールドにまたがる制約がある場合などに対応しにくい
- 呼び出し元に制御を返す前に、不変条件の確認をする
class AccountService {
void openAccount() {
// 一部の変数を省略
Account account = new Account(number, accountowner, interest)
.withCreditLimit(limit)
.withFallbackAccount(fallbackAccount);
}
}
public class Account {
// メンバ変数を省略
public Account(AccountNumber number, LegalPerson owner, Percentage interest) {
// 省略
}
public Account withCreditLimit(Money creditLimit) {
// 省略
return this; // 自身の参照を返してメソッドを繋げられるようにする
}
public Account withFallbackAccount(AccountNumber fallbackAccount) {
// 省略
return this;
}
}
builder pattern
- エンティティ生成における複雑さを別のビルダーに隠す
- 作りかけの状態を見せない
- 作りかけのものに対して操作はできる必要がある
- Javaなら内部クラスを利用できる
- 作るものが複雑な時に有効
void openAccount() {
AccountBuilder accountBuilder = new AccountBuilder(number, accountOwner, interest);
accountBuilder.withCreditLimit(limit);
Account account = accountBuilder.build();
}
// 内部クラスを使う場合
public class Account {
// メンバ変数省略
private Account(AccountNumber number, LegalPerson owner, Percentage interest) {
// private, 処理省略
}
private void checkInvariants() throws Exception {
// 処理省略
}
public static class Builder {
private Account product;
public Builder(*args) {
product = new Account(*args);
}
public Builder withCreditLimit(Money creditLimit) {
// 処理
return this;
}
}
public Account build() {
validState(product != null);
product.checkInvariants();
Account result = product;
product = null;
return result;
}
Chapter 7: Reduce complexity of state
- partially immutable entity
- state object
entity snapshot
- エンティティに必要な情報を取得できる
- ドメインロジックも扱える
- エンティティを変更するためにはドメインサービスを用意する
- 多くのDBは、効率性のために、レコード単位ではなく、メモリページ単位でロックを取得する
- このため、単一レコードを取得した場合でも、複数のレコードをロックしうる
- 応答性を改善しうる
- 読み込みと書き込みを分離しやすい
public class OrderSnapshot {
public final OrderID orderid;
public final CustomerID custid;
private final List<OrderItem> orderItemList;
public OrderSnapshot(OrderID orderid,
CustomerID custid,
List<OrderItem> orderItemList) {
this.orderid = notNull(orderid);
this.custid = notNull(custid);
this.orderItemList = Collections.unmodifiableList(notNull(orderItemList));
checkBusinessRuleInvariants();
}
public List<OrderItem> orderItem() {
return orderItemList;
}
public int nrItems() {
// 略
}
private void checkBusinessRuleInvariants() {
validState(nrItems() <= 38, "Too large");
}
}
public class OrderService {
public OrderSnapshot findOrder(OrderID orderid) {} // 略
public List<OrderSnapshot> findOrdersByCustomer(CustomerID custid) {} // 略
}
entity relay
- エンティティの生存期間をいくつかのフェーズに分割し、それぞれを表現するエンティティを作る
- 戻ることのない境界を利用する
- できれば最後となる状態を1つだけ持つ
- 状態が多すぎたり、分岐したりするときは、ドメインエキスパートと相談する
2/2へ続く