インフラストラクチャアーキテクチャ設計
概要
本ドキュメントは、財務会計システムのインフラストラクチャアーキテクチャを定義します。
開発環境から本番環境まで、一貫した構成管理とデプロイメント戦略を提供します。
システム構成
全体構成図

開発環境
Docker Compose 構成

docker-compose.yml
services:
# PostgreSQL データベース
postgres:
image: postgres:16-alpine
container_name: accounting-postgres
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
POSTGRES_DB: ${POSTGRES_DB:-accounting_system}
TZ: 'Asia/Tokyo'
ports:
- "${POSTGRES_PORT:-5432}:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./ops/docker/postgres/init:/docker-entrypoint-initdb.d
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- accounting-network
# MySQL データベース(代替オプション)
mysql:
image: mysql:8.0
container_name: accounting-mysql
restart: unless-stopped
profiles:
- mysql
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root}
MYSQL_DATABASE: ${MYSQL_DATABASE:-accounting_system}
MYSQL_USER: ${MYSQL_USER:-user}
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-password}
TZ: 'Asia/Tokyo'
ports:
- "${MYSQL_PORT:-3306}:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./ops/docker/mysql/init:/docker-entrypoint-initdb.d
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
networks:
- accounting-network
# データベース管理ツール
adminer:
image: adminer:latest
container_name: accounting-adminer
restart: unless-stopped
ports:
- "${ADMINER_PORT:-8080}:8080"
environment:
ADMINER_DEFAULT_SERVER: postgres
networks:
- accounting-network
depends_on:
- postgres
# バックエンドアプリケーション
backend:
build:
context: ./backend
dockerfile: Dockerfile.dev
container_name: accounting-backend
restart: unless-stopped
environment:
SPRING_PROFILES_ACTIVE: dev
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/accounting_system
SPRING_DATASOURCE_USERNAME: postgres
SPRING_DATASOURCE_PASSWORD: postgres
ports:
- "${BACKEND_PORT:-8081}:8080"
volumes:
- ./backend:/app
- gradle_cache:/home/gradle/.gradle
depends_on:
postgres:
condition: service_healthy
networks:
- accounting-network
# フロントエンドアプリケーション
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.dev
container_name: accounting-frontend
restart: unless-stopped
environment:
VITE_API_BASE_URL: http://backend:8080/api
ports:
- "${FRONTEND_PORT:-3000}:3000"
volumes:
- ./frontend:/app
- /app/node_modules
depends_on:
- backend
networks:
- accounting-network
volumes:
postgres_data:
mysql_data:
gradle_cache:
networks:
accounting-network:
driver: bridge
環境変数設定
.env:
# PostgreSQL
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=accounting_system
POSTGRES_PORT=5432
# MySQL(代替オプション)
MYSQL_ROOT_PASSWORD=root
MYSQL_DATABASE=accounting_system
MYSQL_USER=user
MYSQL_PASSWORD=password
MYSQL_PORT=3306
# Application Ports
BACKEND_PORT=8081
FRONTEND_PORT=3000
ADMINER_PORT=8080
# Application Settings
SPRING_PROFILES_ACTIVE=dev
データベース設計
Flyway マイグレーション管理

マイグレーションファイル構成
src/main/resources/db/migration/
├── V001__create_account_master.sql
├── V002__create_account_structure.sql
├── V003__create_journal_tables.sql
├── V004__create_balance_tables.sql
├── V005__create_user_tables.sql
├── V006__create_audit_log.sql
└── V007__insert_initial_data.sql
マイグレーションファイル例
-- V001__create_account_master.sql
-- 勘定科目種別の enum 型
DO $$ BEGIN
CREATE TYPE account_type AS ENUM ('資産', '負債', '純資産', '収益', '費用');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
-- 勘定科目マスタ
CREATE TABLE IF NOT EXISTS account (
account_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
account_code VARCHAR(20) UNIQUE NOT NULL,
account_name VARCHAR(100) NOT NULL,
account_abbr VARCHAR(50),
account_kana VARCHAR(100),
account_type account_type NOT NULL,
bs_pl_type CHAR(1) NOT NULL CHECK (bs_pl_type IN ('B', 'P')),
debit_credit_type CHAR(1) NOT NULL CHECK (debit_credit_type IN ('借', '貸')),
display_order INT NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
version INT NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- インデックス
CREATE INDEX idx_account_code ON account(account_code);
CREATE INDEX idx_account_type ON account(account_type);
CREATE INDEX idx_account_active ON account(is_active);
-- テーブルコメント
COMMENT ON TABLE account IS '勘定科目マスタ';
COMMENT ON COLUMN account.account_id IS '勘定科目ID(主キー)';
COMMENT ON COLUMN account.account_code IS '勘定科目コード';
COMMENT ON COLUMN account.account_name IS '勘定科目名';
COMMENT ON COLUMN account.account_type IS '勘定科目種別(資産/負債/純資産/収益/費用)';
COMMENT ON COLUMN account.bs_pl_type IS 'BS/PL区分(B:貸借対照表、P:損益計算書)';
COMMENT ON COLUMN account.debit_credit_type IS '貸借区分(借:借方、貸:貸方)';
COMMENT ON COLUMN account.version IS '楽観的ロック用バージョン';
CI/CD パイプライン
GitHub Actions ワークフロー

バックエンド CI
# .github/workflows/backend-ci.yml
name: Backend CI
on:
push:
branches: [main, develop]
paths:
- 'backend/**'
pull_request:
branches: [main]
paths:
- 'backend/**'
jobs:
build:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: test_db
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Set up JDK 25
uses: actions/setup-java@v4
with:
java-version: '25'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/gradle-build-action@v3
- name: Build with Gradle
working-directory: ./backend
run: ./gradlew build -x test
- name: Run tests
working-directory: ./backend
run: ./gradlew test
env:
SPRING_DATASOURCE_URL: jdbc:postgresql://localhost:5432/test_db
SPRING_DATASOURCE_USERNAME: postgres
SPRING_DATASOURCE_PASSWORD: postgres
- name: Run static analysis
working-directory: ./backend
run: ./gradlew check
- name: Generate coverage report
working-directory: ./backend
run: ./gradlew jacocoTestReport
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: backend-coverage-report
path: backend/build/reports/jacoco/test/html
フロントエンド CI
# .github/workflows/frontend-ci.yml
name: Frontend CI
on:
push:
branches: [main, develop]
paths:
- 'frontend/**'
- 'openapi.yaml'
pull_request:
branches: [main]
paths:
- 'frontend/**'
- 'openapi.yaml'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
working-directory: ./frontend
run: npm ci
- name: Generate API client
working-directory: ./frontend
run: npm run api:generate
- name: Lint check
working-directory: ./frontend
run: npm run lint
- name: Type check
working-directory: ./frontend
run: npm run type-check
- name: Run tests
working-directory: ./frontend
run: npm run test:coverage
- name: Build
working-directory: ./frontend
run: npm run build
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: frontend-build
path: frontend/dist
本番環境構成(デモ用)
Heroku Container 構成
本環境はデモ用途で、フロントエンドとバックエンドを別々の Heroku アプリとしてデプロイします。
バックエンドは H2 インメモリデータベースを使用し、Dyno 再起動時にデータはリセットされます。

デプロイ対象
| アプリケーション |
Heroku アプリ名 |
URL |
| フロントエンド |
case-study-accounting-frontend |
https://case-study-accounting-frontend-2cb4e7e16f2f.herokuapp.com |
| バックエンド |
case-study-accounting-backend |
https://case-study-accounting-backend-8d23bb5e8bbe.herokuapp.com |
デモ環境の特徴
| 項目 |
内容 |
| フロントエンド |
React SPA + nginx |
| バックエンド |
Spring Boot + H2 |
| データベース |
H2 インメモリ |
| データ永続性 |
なし(再起動でリセット) |
| 初期データ |
起動時に自動投入 |
| 用途 |
デモ、プレゼンテーション、機能確認 |
| Dyno タイプ |
Eco または Basic |
Dockerfile(バックエンド)
# apps/backend/Dockerfile
FROM gradle:ubi10 AS builder
WORKDIR /app
COPY ./ ./
RUN gradle build -x test --no-daemon
FROM eclipse-temurin:25-jre-alpine
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
EXPOSE 8080
CMD ["java", "-Dserver.port=${PORT:-8080}", "-jar", "app.jar"]
Dockerfile(フロントエンド)
# apps/frontend/Dockerfile
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf.template
COPY start.sh /start.sh
RUN sed -i 's/\r$//' /start.sh && chmod +x /start.sh
EXPOSE 80
CMD ["/bin/sh", "/start.sh"]
GitHub Actions デプロイ
main ブランチへの push 時、または手動実行で自動デプロイされます。
| ワークフロー |
ファイル |
トリガー |
| Backend Deploy |
.github/workflows/backend-deploy.yml |
apps/backend/** 変更時 |
| Frontend Deploy |
.github/workflows/frontend-deploy.yml |
apps/frontend/** 変更時 |
必要な GitHub Secrets
| シークレット名 |
内容 |
HEROKU_API_KEY |
Heroku API キー |
手動デプロイ(Gulp タスク)
# バックエンドのみ
npx gulp deploy:backend
# フロントエンドのみ
npx gulp deploy:frontend
# 両方
npx gulp deploy:all
# デプロイ状態確認
npx gulp deploy:status
# ログ確認
npx gulp deploy:backend:logs
npx gulp deploy:frontend:logs
# ブラウザで開く
npx gulp deploy:open
手動デプロイ(Docker コマンド)
# Heroku Container Registry にログイン
heroku container:login
# バックエンド
docker build -t registry.heroku.com/case-study-accounting-backend/web ./apps/backend
docker push registry.heroku.com/case-study-accounting-backend/web
heroku container:release web -a case-study-accounting-backend
# フロントエンド
docker build -t registry.heroku.com/case-study-accounting-frontend/web ./apps/frontend
docker push registry.heroku.com/case-study-accounting-frontend/web
heroku container:release web -a case-study-accounting-frontend
環境変数(Config Vars)
バックエンド
heroku config:set SPRING_PROFILES_ACTIVE=demo -a case-study-accounting-backend
heroku config:set JWT_SECRET=$(openssl rand -base64 32) -a case-study-accounting-backend
フロントエンド
heroku config:set API_URL=https://case-study-accounting-backend-8d23bb5e8bbe.herokuapp.com/api -a case-study-accounting-frontend
ログ確認
# バックエンド
heroku logs --tail -a case-study-accounting-backend
# フロントエンド
heroku logs --tail -a case-study-accounting-frontend
Dyno 再起動(データリセット)
heroku restart -a case-study-accounting-backend
デモ環境の制約
| 制約 |
説明 |
| データ非永続 |
Dyno 再起動でデータはリセット |
| シングル Dyno |
複数 Dyno でのデータ共有不可 |
| 同時アクセス |
大量アクセスには非対応 |
| 本番利用不可 |
あくまでデモ・検証用途 |
関連ドキュメント
監視・ログ(デモ用)
シンプルなログ構成
デモ環境では Heroku 標準のログ機能のみを使用します。

ログ確認コマンド
# リアルタイムログ
heroku logs --tail --app accounting-demo
# 直近のログ(100行)
heroku logs -n 100 --app accounting-demo
# アプリケーションログのみ
heroku logs --source app --app accounting-demo
Spring Boot Actuator(デモ用)
# application-demo.yml に追加
management:
endpoints:
web:
exposure:
include: health,info
base-path: /actuator
endpoint:
health:
show-details: always
# ヘルスチェック
curl https://accounting-demo.herokuapp.com/actuator/health
セキュリティ
セキュリティ対策

セキュリティチェックリスト
| カテゴリ |
対策 |
実装 |
| 認証 |
JWT トークン |
Spring Security |
| 認可 |
ロールベースアクセス制御 |
@PreAuthorize |
| 通信 |
HTTPS 強制 |
TLS 1.3 |
| 入力検証 |
バリデーション |
Bean Validation |
| SQL インジェクション |
パラメータバインド |
MyBatis |
| XSS |
出力エスケープ |
React 自動エスケープ |
| CSRF |
トークン検証 |
Spring Security |
| 監査 |
操作ログ |
AOP + DB |
バックアップ・リカバリ(デモ用)
デモ環境のデータ管理
デモ環境では H2 インメモリデータベースを使用するため、バックアップは不要です。
| 項目 |
内容 |
| バックアップ |
不要(インメモリ DB) |
| データリセット |
Dyno 再起動で自動リセット |
| 初期データ復元 |
起動時に自動投入 |
データリセット手順
# Dyno 再起動でデータをリセット
heroku restart --app accounting-demo
# または Heroku Dashboard から Restart Dyno
注意事項
- デモ中に入力したデータは Dyno 再起動で消失します
- 重要なデータは別途保存してください
- 本番運用にはこの構成を使用しないでください
技術スタック
| 技術 |
用途 |
| Docker |
コンテナ化 |
| Docker Compose |
ローカル開発環境 |
| PostgreSQL 16 |
開発・テスト用データベース |
| H2 Database |
デモ環境データベース(インメモリ) |
| Flyway |
DB マイグレーション(開発環境) |
| GitHub Actions |
CI/CD |
| Heroku Container |
デモ環境ホスティング |
| Heroku Logplex |
ログ管理 |
運用チェックリスト
デプロイ前チェック
デプロイ後チェック