はじめに
一般的なポートフォリオや個人開発の多くは、単一のデータベースに対するシンプルなCRUD操作や、基本的なREST APIの構築にとどまる傾向があります。もちろんそれらも重要な基礎ですが、私はそこからさらに一歩踏み込み、より複雑な課題に挑戦したいと考えました。
私の目的は、「現代のモダンなアプリケーションが、実運用レベルでどのように設計され、構築されているのか」を深く理解することでした。単なるコードの記述を超え、スケーラビリティや耐障害性を考慮したアーキテクチャの探求が必要不可欠でした。
具体的には、複数の領域に分割されたマイクロサービスアーキテクチャ、Kafkaを用いた非同期のイベント駆動型通信、Kubernetes(k8s)によるオーケストレーション、そしてCI/CDパイプラインや高度なオブザーバビリティ(監視・追跡)の実装です。これら一連のモダンな技術スタックを統合し、複雑なシステムをゼロから設計することに挑みました。
このような技術的な探求心と、妥協のないバックエンド開発への情熱から生まれたのが、このプロジェクトです。
フロントエンド画面(Vue.js)
非同期で計算されるランキングセクションを表示するUIです。分析ワークロードをユーザーのブラウジング環境から完全に分離しているため、重い集計処理中であってもユーザー側のレイテンシ(遅延)に一切影響を与えない設計になっています。
プロジェクトの目標
第一の目標は、ドメイン駆動設計(DDD)の原則に基づき、システム全体を自律した複数のマイクロサービスへと適切に分割することでした。単一の巨大なモノリスを作るのではなく、カタログ管理、貸出処理、ユーザー認証などの各ドメインを独立させることで、それぞれのサービスが独自のライフサイクルでスケーリングや保守ができる、モジュール性の高い堅牢な設計を目指しました。
第二の目標は、Kafkaを活用したイベント駆動型アーキテクチャとCQRS(コマンドクエリ責務分離)を実装し、パフォーマンスの最適化を図ることです。具体的には、リアルタイムなユーザーのブラウジングと、バックグラウンドでの分析ワークロード(ランキング計算など)をイベント駆動のパイプラインで完全に分離しました。これにより、システムに高い負荷がかかってもユーザー体験(レイテンシ)を決して損なわない、実運用を見据えた高度なアーキテクチャを実現しています。
サービス全体構成図
技術スタック
- Backend / Architecture: Java (Spring Boot), PHP (Laravel), Node.js (Express), DDD, CQRS, Microservices
- Infrastructure / Cloud: Kubernetes (Minikube), Docker, Kafka, Cloudflare (CDN & Security)
- Frontend: Vue.js
- Observability / Monitoring: Prometheus, Alertmanager, Grafana, Jaeger (Tracing)
- Databases: MongoDB Atlas, PostgreSQL (Neon)
システム全体構成図
本プロジェクトでは、各サービスを独立したドメインとして分割し、同期通信とイベント駆動通信を組み合わせた構成を採用しています。
なぜこの構成にしたのか
現代のWebアプリケーションでは、モノリシックな構成は初期開発において高い生産性を発揮する一方、ユーザー数の増加や機能拡張に伴い、スケーラビリティや保守性の面で課題が発生します。
特に、ユーザーによるリアルタイムな操作処理と、データ分析やランキング計算などの負荷の高いバックグラウンド処理を同一アプリケーション内で実行すると、リソース競合によりユーザー体験へ影響を与える可能性があります。
本プロジェクトでは、この課題を解決するためにCQRSの考え方とイベント駆動型アーキテクチャを採用しました。ユーザー操作に必要な同期処理と、大量データ処理を必要とする非同期処理をKafkaによるイベントストリームで分離することで、それぞれの処理を独立してスケール可能な設計としています。
また、ドメイン駆動設計(DDD)に基づき、各ビジネス領域をBounded Contextとして分離し、認証・カタログ・貸出などを独立したマイクロサービスとして設計しました。
さらに、アプリケーション層をステートレス化し、Kubernetes上でコンテナオーケストレーションを行うことで、サービス単位でのスケーリングと障害分離を可能にしています。
単に最新技術を採用するのではなく、将来的な拡張性、運用性、障害耐性を考慮した結果、このアーキテクチャを選択しました。
コミュニケーション図(データ・リクエストの流れ)
システム全体の通信フローは、同期的なリクエスト処理と非同期のイベントストリーミングという2つの軸で構成されています。
同期通信(リクエスト / レスポンス)
外部クライアントからのリクエストは、Cloudflare Tunnel(DNS・Proxy・Security)を経由し、Kubernetes Ingress ControllerによってLibrary Gateway(API Gateway)へルーティングされます。
Library Gatewayは単一エントリーポイントとして、認証、カタログ、貸出などの各マイクロサービスのKubernetes Serviceへリクエストを振り分けます。
非同期通信(イベント駆動・メッセージング)
貸出完了や返却完了などのドメインイベントが発生した場合、サービス間は直接通信を行わず、Kafkaを介してイベントを発行(Produce)します。
Notification ServiceやAnalytics Serviceなどのコンシューマサービスは、Kafkaからイベントを購読(Consume)し、メール通知やデータ集計などのバックグラウンド処理を非同期で実行します。
データ永続化
各マイクロサービスはステートレスとして設計され、永続データはMongoDB AtlasおよびNeon(PostgreSQL)などのマネージドクラウドデータベースで管理しています。
アプリケーションからデータベースへの接続情報はKubernetes Secretで管理し、安全にアクセスできる構成としています。
今後の記事では、以下のテーマについて実装内容や設計判断を詳しく解説します。
- Kafkaによるイベント駆動アーキテクチャ
- Kubernetes上でのマイクロサービス運用
- GitHub Actionsを利用したCI/CDパイプライン
- Cloudflare Tunnelによる外部公開
- OpenTelemetry / Prometheus / GrafanaによるObservability設計