Value Object・Entity・Aggregate - DDD のモデル構成要素
EC サイトの注文に「代金引換で支払う注文は、明細の合計金額が引換の上限額を超えない」という規則を置いたとします。明細を書き換えられる経路が複数あると、規則を検査しない経路から上限を超えた注文ができます。同姓同名の顧客を同じ人として扱う取り違えも、値の比べ方を決めていないと起きます。 ...
EC サイトの注文に「代金引換で支払う注文は、明細の合計金額が引換の上限額を超えない」という規則を置いたとします。明細を書き換えられる経路が複数あると、規則を検査しない経路から上限を超えた注文ができます。同姓同名の顧客を同じ人として扱う取り違えも、値の比べ方を決めていないと起きます。 ...
注文の画面にも、値引きの条件にも、請求書にも「合計」が出てきます。どれも同じ注文に付く同じ語なので、注文のモデルに total を 1 つ持てば足りるように見えます。 ...
Repository とは、Aggregate(集約。規則を一緒に守るオブジェクトのまとまり)を 1 つの単位として保存・復元する仕組みで、呼び出す側からはメモリ上のコレクションのように見えます。注文と明細のように、まとめて検査すべき規則を持つデータを DB(データベース)に保存する時に、明細だけを書き換える経路を塞ぐために使います。 ...
Domain Event とは、ドメインの中で既に起きた出来事を表すオブジェクトです。過去の記録なので通常は不変(作った後に値を変えない)で、出来事が起きた時刻と、関与したエンティティの識別子を持ちます。 ...
Kubernetes の Job(完了したら終わるバッチ処理)に、ログを送るコンテナを 1 つ足しただけで、Job が終わらなくなることがあります。処理のコンテナは 5 秒で終わっているのに、kubectl get jobs の完了の数は 0 のままです。以下は、手元の Kubernetes v1.34.0 で、同じ処理の Job を 3 通りの置き方で動かした結果です。 ...
アプリの設定を開くと Redis の宛先が 127.0.0.1:6380 で、同じ Kubernetes の Pod には HAProxy のコンテナが動いている。 Redis は、書き込みを受け付ける 1 台の master と、その複製を持ち読み取りだけを受ける replica に分けて組めます。この localhost のプロキシは、master が落ちて別のサーバーに切り替わった時、アプリの書き込みをどこまで守ってくれるのでしょうか。 ...
監視用の補助コンテナを古いバージョンのままにして、PostgreSQL を 16 から 17 に上げると、ダッシュボードで checkpoint 回数のグラフだけが途切れます。DB は動いていて、接続を表す pg_up は 1 のままで、DB が落ちた時に鳴らすアラートも鳴りません。補助コンテナを新しいバージョンに上げると、エラーは消えますが、グラフは途切れたままです。 ...
外部の決済サービスが、支払いの status に REVIEW(不正検知の審査中)という値を 1 つ足しました。呼び出す側のコードは例外を 1 件も出さず、そのまま動き続けます。以下は、決済サービスの値を 4 か所でそのまま読むコードと、値を 1 か所で自分たちの語に置き換えるコードに、REVIEW の支払いを 1 件ずつ渡した結果です。決済サービスとコードは、本記事のために書いた架空のものです。 ...
Event Sourcing とは、アプリケーションの現在の状態ではなく、状態を変えた出来事の並びを永続化し、状態はその再生(replay)で復元する方式です。 Event Sourcing は、現在の状態に加えて、そこに至るまでの操作を後から確認したい場合に使います。 ...
通販サイトで、商品ページの価格は新しいのに、商品一覧の価格だけが古いまま、という症状が起こり得ます。全ての商品ではなく 200 商品のうち数個だけで、再読み込みしても直らず、エラーのログも出ません。以下は、この症状を手元で再現した実験の結果です。一覧に価格を反映する処理を並行して進めるスレッド(worker)が 1 つの時は出ず、4 つにして 1 つのキューを共有させると毎回出ました。 ...
まず 10% のユーザーにだけ、新しい決済画面を出すことにしました。Python で次の判定を書き、同じコードを 3 台のサーバーに置きます。 def is_on(user_id: str) -> bool: return hash(user_id) % 100 < 10 3,000 人のユーザーが、ロードバランサーを通して 6 回ずつ画面を開いたとします。6 回の中で、新しい画面と今の画面が入れ替わったユーザーは何人いるでしょうか。 ...
Plugin Architecture は、コア(core、常に動く中心部分)に手を入れずに機能を追加できるようにする設計です。コアが持つのは、拡張を呼び出す処理の流れと、拡張とのやり取りを定める規約だけです。エディタに拡張機能を入れると、本体を差し替えずに機能が増えます。Go の database/sql がデータベースドライバを Register 関数で受け取るのも、同じ形です。 ...
Parse, don’t validate は、「外部から来たデータを検査し、条件を満たす値を専用の型に変換して後続処理に渡す」という設計方針です。Alexis King 氏が 2019 年に公開した「Parse, don’t validate」で示しました。 ...
Strangler Fig は、古いシステムの外側に新しいシステムを作り、請求や配送のような業務の機能を 1 つずつ移しながら、最終的に古いシステムを置き換える進め方です。 ...
Distributed Monolith は、複数のプロセスやサービスに分かれているにもかかわらず、それらを個別に変更・テスト・デプロイできない構造です。サービスの数やコンテナの数ではなく、変更時にどこまで同時に動かさなければならないかで決まります。 ...
モジュラーモノリス(Modular Monolith)は、アプリケーションを 1 つのデプロイ単位のまま、内部を注文・請求・在庫のようなモジュールに分け、モジュール間の呼び出しを各モジュールが公開した API だけに限る構成です。実行ファイルもリポジトリも 1 つで構いません。分けるのは実行場所ではなく、コードとデータの持ち主です。 ...
Cell-based Architecture(セルベースアーキテクチャ)は、アプリ・DB(データベース)・キャッシュまで含めたシステム一式を複数用意し、顧客やテナントをそのどれか 1 つに割り当てる構成です。1 つ 1 つの独立した複製を cell と呼びます。cell 同士は状態を共有しないので、ある cell で起きた障害は、その cell に割り当てた顧客の範囲に閉じます。 ...
サーキットブレーカー(Circuit Breaker)は、失敗が続いている下流のサービスへの呼び出しを一定時間止め、呼び出し側をすぐに失敗させる仕組みです。配電盤のブレーカーと同じく、異常を検知すると回路を開いて呼び出しを通さなくなり、しばらく経つと少数の呼び出しで回復を確認します。 ...
Bulkhead(バルクヘッド)は、スレッドや接続のような限られた資源を用途ごとに区切り、1 つの用途が資源を使い切っても、他の用途の分が残るようにするパターンです。名前は船の隔壁から来ています。船体を区画に仕切っておけば、穴の開いた区画だけが浸水し、船全体は沈みません。Microsoft の設計パターンの解説も、この船の例で説明しています。 ...