Skip to content

バックエンドアーキテクチャ - 国際貨物輸送管理システム

概要

本ドキュメントでは、国際貨物輸送管理システムのバックエンドアーキテクチャを定義する。 Jakarta EE 参考実装のアーキテクチャ思想(DDD・ヘキサゴナル・イベント駆動)を継承しつつ、 Spring Boot 4.0 / Java 25 を基盤とした現代的な実装に移植する。

アーキテクチャパターン選択

業務領域カテゴリーの評価

評価軸 判定 根拠
業務領域カテゴリー 中核の業務領域 国際貨物輸送は複雑なビジネスルール(通関、積み替え、例外処理)を持つ
データ構造の複雑さ 複雑 エンティティ間の関係が多く、コンテキスト間でデータを共有・変換する必要がある
特殊要件 あり 金額を扱う(Billing Context)、監査記録が必要(荷役履歴)、状態遷移が厳密

選択したアーキテクチャパターン

上記評価から、以下の組み合わせを採用する。

  • ドメインモデル: ビジネスルールをドメインオブジェクトにカプセル化し、手続き的なロジックを排除する
  • ポートとアダプター(ヘキサゴナルアーキテクチャ): ドメインを技術的関心事から独立させ、テスト容易性を確保する
  • CQRS(コマンドクエリ責務分離): Booking / Tracking の読み書き負荷特性の違いに対応し、クエリを読み取り最適化モデルで返す

Billing Context は MoneyAmount 値オブジェクトによる金額管理を行うが、初期フェーズではイベントソーシングは適用しない。

全体アーキテクチャ

uml diagram

境界付けられたコンテキスト

コンテキストマップ

uml diagram

各コンテキストの説明

1. Booking Context(予約コンテキスト)

荷物予約の中核ロジックを担う。荷物の登録・経路割り当て・状態管理を責務とする。

要素 内容
集約ルート Cargo
主要概念 RouteSpecification, CargoItinerary, Delivery
BookingStatus PRELIMINARY / ROUTE_PROPOSED / CONFIRMED / TRACKING_ISSUED / IN_TRANSIT / DELIVERED / SETTLED / CANCELLED
アクター 荷主、営業担当者

2. Routing Context(経路コンテキスト)

航路・運航スケジュールを管理する。外部経路システムとの統合を担う。

要素 内容
集約ルート Voyage
主要概念 CarrierMovement, Schedule, VoyageNumber
アクター 経路設計者、外部経路システム

3. Tracking Context(追跡コンテキスト)

荷物の現在状態・輸送ステータスを管理する。CQRS の読み取り側最適化が特に有効なコンテキスト。

要素 内容
集約ルート TrackingActivity
主要概念 TrackingNumber, TransportStatus, TrackingExceptionEvent
TransportStatus NOT_RECEIVED / RECEIVED / LOADED / IN_TRANSIT / UNLOADED / CUSTOMS_INSPECTION / AWAITING_CLAIM / DELIVERED / MISROUTED
アクター 追跡管理者、荷主、荷受人

4. Handling Context(荷役コンテキスト)

港湾・税関での荷役作業を記録する。CargoSnapshot ACL で Booking Context への依存を吸収する。

要素 内容
集約ルート HandlingActivity
主要概念 HandlingType, CustomsDeclaration, CargoSnapshot(ACL)
アクター 荷役作業員、港湾管理システム、税関

5. Billing Context(請求コンテキスト)

運賃・請求書の管理を担う。Money 値オブジェクトで金額を厳密に管理する。

要素 内容
集約ルート Invoice
主要概念 Money, DiscountPolicy, PaymentStatus
アクター 経理担当者、荷主、決済機関

6. Shared Domain(共有ドメイン)

Location(UN/LOCODE)のみ共有カーネルとして維持する。VoyageNumber は各コンテキスト固有型として定義し、共有しない。

ヘキサゴナルアーキテクチャ(ポートとアダプター)

uml diagram

レイヤー責務一覧

レイヤー パッケージ 責務 依存方向
Domain domain/model/, domain/event/ ビジネスルール・不変条件・ドメインイベント定義 外部に依存しない
Application application/command/, application/query/ ユースケース実行・集約操作・イベント発行 Domain のみ依存
Infrastructure infrastructure/persistence/, infrastructure/web/, infrastructure/event/ 永続化・HTTP・イベント処理の技術実装 Application / Domain に依存

CQRS 設計

uml diagram

CQRS 適用方針

  • コマンド側: ドメインモデル(集約)を通じて状態変更。不変条件の検証後、MyBatis で永続化する
  • クエリ側: ドメインモデルを経由せず、MyBatis の XML マッパーで JOIN クエリを直接記述し、画面表示用 DTO を返す
  • CQRS が特に有効なコンテキスト: Booking(一覧・詳細の頻繁な参照)、Tracking(リアルタイム状態確認)

イベント駆動設計

uml diagram

ドメインイベント一覧

イベント 発生元コンテキスト 処理先コンテキスト 内容
CargoBookedEvent Booking Tracking 追跡番号の割り当てトリガー
CargoRoutedEvent Booking Tracking 経路・旅程の確定をトラッキングに通知
HandlingActivityRegisteredEvent Handling Tracking, Booking 荷役作業登録 → 輸送ステータス同期
TrackingExceptionDetectedEvent Tracking Booking, Notification 例外検知 → 関係者への通知
InvoiceCreatedEvent Billing Notification 請求書発行 → 荷主への通知

Spring ApplicationEventPublisher の実装方針

// ドメインイベントの発行(Application Service 内)
@Service
public class HandlingCommandService {
    private final ApplicationEventPublisher eventPublisher;

    public void registerHandlingActivity(RegisterHandlingCommand command) {
        // ドメインロジック実行後にイベント発行
        eventPublisher.publishEvent(new HandlingActivityRegisteredEvent(this, activity));
    }
}

// イベントリスナー(infrastructure/event/ パッケージ)
@Component
public class TrackingEventListener {
    @EventListener
    public void onHandlingActivityRegistered(HandlingActivityRegisteredEvent event) {
        trackingCommandService.updateTransportStatus(event);
    }
}

設計注意: @EventListener はデフォルトでトランザクションコミット前に実行される。 コミット前にリスナーが実行されるリスクを避けるため、ドメインイベントのリスナーには @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) を使用すること。 高可用性が必要なシステムへ移行する際は Transactional Outbox パターンへの移行を検討すること。

Jakarta EE → Spring Boot 移行マッピング

Jakarta EE 技術 Spring Boot 移行先 移行ポイント
CDI(@Inject Spring DI(@Component, @Service, @Repository アノテーション名の変更のみ。コンストラクタインジェクションを優先する
JAX-RS(@Path, @GET Spring MVC(@RestController, @GetMapping エンドポイント定義のアノテーション変更
CDI Events(Event<T>.fire() ApplicationEventPublisher.publishEvent() 同期イベントはほぼ等価。同一プロセス内通信
JPA / EclipseLink MyBatis(XML マッパー) ORM から SQL 明示管理へ変更。ドメインモデルの @Entity は不要になる
Bean Validation Spring Validation(@Valid, BindingResult アノテーションは共通(Jakarta Validation API)
Jakarta Security Spring Security 7.x フォームベース認証・RBAC を Spring Security で実装
@ApplicationScoped @Component(シングルトンがデフォルト) スコープ管理の思想は共通
@Transactional(JTA) @Transactional(Spring) アノテーション名は同じ。JTA から Spring トランザクションへ変更

パッケージ構造

apps/cargo-tracker/src/main/java/com/example/cargotracker/
├── booking/
│   ├── domain/
│   │   ├── model/             # Booking 集約、BookingId、CargoSpecification、BookingStatus 等
│   │   ├── event/             # BookingRegisteredEvent, DomainEvent
│   │   └── repository/        # BookingRepository(出力ポート)
│   ├── application/
│   │   └── internal/
│   │       ├── commandservices/   # RegisterBookingCommandService
│   │       ├── queryservices/     # FindBookingQueryService
│   │       └── outboundservices/  # ShipperExistencePort / ACL Adapter
│   └── infrastructure/
│       ├── repositories/      # BookingRepositoryImpl, BookingMapper, BookingRecord
│       ├── brokers/           # BookingEventHandler
│       └── config/            # DefaultProfileBookingSeedConfiguration
├── shipper/
│   ├── domain/
│   │   ├── model/             # Shipper 集約、ShipperName、ContactInfo 等
│   │   ├── event/             # ShipperRegisteredEvent
│   │   └── repository/        # ShipperRepository(出力ポート)
│   ├── application/
│   │   └── internal/
│   │       ├── commandservices/   # RegisterShipperCommandService
│   │       └── queryservices/     # FindShipperQueryService
│   └── infrastructure/
│       ├── repositories/      # ShipperRepositoryImpl, ShipperMapper, ShipperRecord
│       └── config/            # DefaultProfileShipperSeedConfiguration
├── routing/                   # package-info のみ(将来実装予定)
├── tracking/                  # package-info のみ(将来実装予定)
├── handling/                  # package-info のみ(将来実装予定)
├── billing/                   # package-info のみ(将来実装予定)
└── shared/
    ├── domain/
    │   └── model/             # 共有 ID 型(ShipperId など)
    └── infrastructure/
        ├── config/            # SecurityConfig, OpenApiConfig
        ├── web/               # HomeController
        └── UUIDTypeHandler    # MyBatis TypeHandler

API 設計方針

REST API 設計原則

原則 内容
リソース指向 URL はリソースを表す名詞。動詞は HTTP メソッドで表現する
バージョニング /api/v1/ プレフィックスでバージョンを管理する
レスポンス形式 JSON。エラーレスポンスは { "code": "BOOKING_NOT_FOUND", "message": "..." } 形式
ステータスコード 成功: 200/201/204、クライアントエラー: 400/404/409、サーバーエラー: 500
HATEOAS 初期フェーズでは適用しない

主要エンドポイント(例)

メソッド パス 説明
POST /api/v1/bookings 貨物予約の登録
GET /api/v1/bookings/{bookingId} 予約詳細の取得
PUT /api/v1/bookings/{bookingId}/route 経路の割り当て
GET /api/v1/tracking/{trackingNumber} 追跡情報の取得
POST /api/v1/handling 荷役作業の登録
GET /api/v1/voyages 航路一覧の取得

セキュリティ設計

Spring Security による認証・認可

uml diagram

ロール設計

ロール 権限 対象ユーザー
ROLE_SHIPPER 予約照会・追跡照会 荷主
ROLE_SALES 予約登録・経路割り当て 営業担当者
ROLE_HANDLER 荷役作業登録 荷役作業員
ROLE_TRACKER 追跡情報管理・例外対応 追跡管理者
ROLE_ACCOUNTANT 請求書管理 経理担当者
ROLE_ADMIN 全機能 システム管理者

テスト戦略

uml diagram

各層のテスト方針

テスト対象 テスト種別 使用技術 方針
ドメインモデル(集約・値オブジェクト) 単体テスト JUnit 5, AssertJ 依存なし。ビジネスルールを網羅的にテスト
Application Service 単体テスト JUnit 5, Mockito リポジトリをモック化。ユースケースのフローをテスト
MyBatis Mapper 統合テスト Testcontainers, H2 実 DB への SQL を検証。スキーマを Flyway で適用
REST Controller 統合テスト Spring MockMvc エンドポイントの入出力・バリデーションをテスト
E2E E2E テスト Selenium 主要ユーザーシナリオ(予約 → 追跡 → 配達)を検証