はじめに
マイクロサービスアーキテクチャにおける最も重要な決定の一つはデータストレージです。モノリシックシステムは伝統的に一つのリレーシステムデータベースにすべてのニーズをサービスすることに依存していました—数十年にわたってこのモデルはうまく機能しましたが、強い結合を生み出し、スケーラビリティを制限し、すべてのドメインが適切な適合性に関わらず同じ永続化パラダイムに従わなければならないと強制します。
モダンな分散システムは、マルチグロット・パーソリズンテンデンスという概念を採用しています—同じシステム内で異なるデータストレージ技術を使用する実践で、それぞれが提供するドメインのアクセスパターンと特徴に合わせて選ばれます。MVP eコマースプロジェクトは、具体的な方法でこのパターンを示しています:3つの異なるデータベース、それぞれが異なるマイクロサービスをサービスとして提供し、それぞれが意図的に選ばれました。
三つのデータベースアーキテクチャ
ここで研究されているプラットフォームは、3つの専門的なストアにデータを整理しています:
| サービス | データベース | タイプ | 根拠 |
|---|---|---|---|
| 注文サービス | PostgreSQL | リレーショナル (ACID) | トランザクショナルな一貫性、財務データ |
| 商品サービス | MongoDB | ドキュメント (NoSQL) | 柔軟なスキーマ、豊富なカタログデータ |
| カートサービス | Redis | メモリ内K/V | ミリ秒以下のスピード、儚かな状態 |
これはですサービスごとにデータベースパターン[1]各サービスはそれぞれのデータベースを排他的に所有しており — 他のサービスのストアから直接読み取ることはありません。この境界は緩やかな結合を強制し、各チームが独立してスキーマを進化させ、サービス間の破損のリスクをなくします。
オーダーサービス向けのPostgreSQL:ACIDが要件として求められます
A リレーショナルデータベースはデータをテーブルに整理します—構造化されたグリッドで、各行がレコードであり、各列が型付けされた制約属性です。テーブル間の関係は外部キーを通じて表現されますは、一つのテーブルの列で、別のテーブルのプライマリキーを参照し、エンジンが参照の整合性を自動的に強制するものです。この厳格なスキーマは制限ではなく、意図的な保証です:すべての行は同じ構造に従わなければならず、エンジンは書き込み時に制約を検証します。その報酬はACIDです。 — 複数の書き込みを一つの全有無トランザクションにまとめる能力、完全にコミットするか完全にロールバックするかで、データベースを失敗に関わらず一貫した状態に残す。
注文サービスは財務記録を保存する。注文は単なるデータではない——それは法的な記録であり、コミットメントである。これによりACID保証は交渉不可能となる。
サービスはSpring Data JPAとFlywayを使用してスキーマのマイグレーションを行います。スキーマは古典的なリレーショナルデザインを反映しており、親ordersテーブルと子order_itemsテーブルが外部キーでリンクされています。ON DELETE CASCADE。
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id VARCHAR(255) NOT NULL,
total DECIMAL(19, 2) NOT NULL,
status VARCHAR(50) NOT NULL,
order_date TIMESTAMP NOT NULL
);
CREATE TABLE order_items (
id BIGSERIAL PRIMARY KEY,
order_id BIGINT NOT NULL,
product_id VARCHAR(255) NOT NULL,
price DECIMAL(19, 2) NOT NULL,
quantity INTEGER NOT NULL,
CONSTRAINT fk_order FOREIGN KEY (order_id) REFERENCES orders (id) ON DELETE CASCADE
);
OrderService.placeOrder()メソッドはアノテーションされています@Transactional は、チェックアウトフローでどのステップ(商品リストの作成、合計金額の計算、記録の永続化)も失敗した場合に、データベースが一貫した状態にロールバックされることを保証します。JPA クラスター設定は、親 Order エンティティを保存するときに、すべての子 OrderItem エンティティを単一の原子操作で永続化することを保証します。
Flywayはバージョン付き、再現可能なマイグレーション スクリプトを提供します。起動時、サービスは実行中のスキーマが期待されるベースラインと一致することを検証し、「私のマシンでは動作する」環境間のずれを防ぎます[2].
プロダクトサービス用のMongoDB:カタログ規模でのスキーマの柔軟性
A ドキュメントデータベースは、自己記述記録としてデータを格納しており—通常はJSONやBSONオブジェクト—各ドキュメントは異なるフィールドセットを持ちます。強制される列リストはありません;ドキュメントは単にアプリケーションが書き込むものを含んでいます。同じ概念を表すドキュメントはコレクションに存在します。、しかしエンジンはそれらが構造的に同一である必要はありません。これにより、データモデルが異質なドメインに文書データベースが適しているのです。
製品には異なる属性があります:ラップトップにはRAMとストレージがあり、Tシャツにはサイズと色があり、本にはISBNと著者があります。これらを厳密なリレーショナル列に合わせるには、複雑なEAV(エンティティ属性値)スキーマか、スパースな可 Null 列のいずれかが必要ですが、どちらもメンテナンスの負担です。
MongoDBのドキュメントモデルは、各製品を自己記述するJSONドキュメントとして保存します。カタログチームが新しい属性カテゴリを追加する必要がある場合、スキーママイグレーションは不要です。アプリケーションコードは単に新しいフィールドを書き始め、既存のドキュメントは有効のまま残ります。
Product ServiceはSpring Data MongoDBを使用し、リポジトリ抽象化を行います:
@Document(collection = "products")
public class Product {
@Id
private String id;
private String name;
private String description;
private BigDecimal price;
private Integer stockQuantity;
private String skuCode;
private String category;
}
ザ@DocumentアノテーションはJavaクラスをMongoDBのコレクションにマッピングします。Spring DataのMongoRepositoryCRUD操作と動的なクエリ派生をボイラープレートSQLなしで提供します。
カートサービス用のRedis:メモリスピードの一時的な状態
エーキー値ストア最もシンプルなデータベースモデルです:各エントリは一意のペアですキー と関連付けられた 値、それ以上の強制された構造はありません。スキーマもなく、クエリ言語もなく、リレーショナルな機構もない—検索は常にキーで行われ、エンジンは関連する値をできるだけ速く格納し、取得する以外のことは行いません。そのシンプルさがキーバリュー・ストアを速くするのです:クエリのパース、制約の強制、トランザクション・ログの管理といったオーバーヘッドがなく、エンジンはメモリ速度で読み書きを処理できます。
ショッピングカートはセッションのようなものです:頻繁に変化し、ミリ秒以下の読み書き応答時間が必要であり、本質的に一時的なものです——カートが失われた場合、顧客は簡単にアイテムを再追加できます。これらの特徴により、リレーショナルデータベースは適切な選択ではなく(短命の状態に対する取引オーバーヘッドが多すぎる)、ドキュメントデータベースは適切ですが最適ではありません。
Redisはこの使用例のために正確に設計されています。メモリ内データ構造ストアとして、キー値操作にはマイクロ秒レイテンシを提供します[3]。カートサービスはカートデータをRedis Hashとしてモデル化し、トップレベルのキーはcart:{userId}で、値はJSONシリアライズされたCartオブジェクトです
。カスタムRedisConfigは設定を構成しますRedisTemplate は明示的なシリアライザを使用:
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
ObjectMapper mapper = new ObjectMapper();
JacksonJsonRedisSerializer<Object> serializer = new JacksonJsonRedisSerializer<>(mapper, Object.class);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
return template;
}
この設定では、キーは人間が読み取れる文字列として保存され(cart:user123)、値はJSONとして保存され、Redis CLIで検査可能であり、サービスの再起動を越えて移動可能です.
取引のトレードオフ:このアーキテクチャのコスト
多言語の持続性は無料ではありません。自律性とパフォーマンスのメリットは実際の運用コストが伴います。例えば:
サービス間の結合はできません。注文サービスは直接
ordersテーブルとMongoDBで結合できません。productsコレクション。モノリスでは、JOINはデータベースエンジン内でマイクロ秒単位で行われ、完全なトランザクション分離が保証されます。サービス間では、同等の操作はHTTPのラウンドトリップとなり、変動する遅延とネットワークの可用性に依存します。最終的整合性.
OrderService.placeOrder()メソッドでは、ユーザーがチェックアウトすると、カートがクリアされます。try/catch— そこでの失敗は既にコミットされた注文をロールバックしません。本当に跨サービストランザクションにはSagaパターン[4]が必要です.運用上のオーバーヘッド. PostgreSQL、MongoDB、RedisをRabbitMQとKeycloakと共に単一の環境で実行する
docker-compose.ymlは開発において達成可能ですが、各ストアには個別のバックアップ戦略、監視、および生産における運用専門知識が必要です。
結論
ここで説明されているデモECプラットフォームは、意図的に適用される多言語持続性が、各コンポーネントがその自然な最適な状態で動作するシステムを生み出すことを示しています。PostgreSQLは、金融記録が求めるACID保証を提供します。MongoDBは、多様な製品カタログが求める柔軟性を提供します。Redisは、ショッピングカートの相互作用が必要とする速度を提供します。
重要な洞察は、持続性技術の選択はドメインの要件に従うべきであるということ——組織の慣れや最も抵抗が少ない道に従うべきではない。実践的には、データベースを手に取る前に、異なるセットの質問をする必要がある。データは関係的か、それとも可変構造の自己記述ドキュメントか? 一貫性が厳しい要件か、利用可能性和速度の代償として一時的な分岐を許容できるか? データは長期的で監査可能か、それとも一時的な性質か? これらの質問はそれぞれ異なるストレージパラダイムを指し示し、単一のエンジンがすべての質問に最適に答えることはない。
ソースコード: github.com/dancodingbr/ecommerce
参考文献
[1] Microservices.io. パターン: 各サービスごとにデータベース. 参照先: https://microservices.io/patterns/data/database-per-service.html
[2] Flyway. データベースマイグレーションについて. 参照先: https://documentation.red-gate.com/fd/why-database-migrations-184127574.html
[3] Redis。始めましょう。利用可能場所:https://redis.io/docs/latest/get-started/
[4] Microservices.io。パターン:Saga。利用可能場所:https://microservices.io/patterns/data/saga.html














