面接対策:システムデザインラウンド — 顧客デプロイを前提とした設計
「システムデザインの面接対策って、やっぱり Twitter を設計できるようにしておけばいいですか?」
Arclight AI のミーティングルームで、入社1年目のユウキがそう聞いた。翌週に控えた FDE チームの採用面接でインタビュアーを務めることになったソウタは、面接官側の準備として候補者が何を聞かれるかを整理していた。その横で、ユウキが自分の転職面接のことを考え始めたのだ。
ソウタは首を横に振った。
「FDE のシステムデザイン面接は、SWE のそれとは根本的に違う。Twitter のタイムラインをどうスケールさせるかじゃない。顧客の環境にどうデプロイするかが問われるんだ」
カイが会議室に入ってきた。
「いい質問だな、ユウキ。SWE 面接のシステムデザインは自社プロダクトの設計だ。でも FDE 面接では、顧客がすでに持っている環境に、自社プロダクトをどう組み込むかが焦点になる。制約が全然違うんだよ」
INFO
FDE のシステムデザイン面接で最も重要なのは「顧客の制約を最初に聞き出す力」です。技術的に正しい設計でも、顧客のセキュリティポリシーやコンプライアンス要件を無視した設計は即不合格になります。
FDE システムデザイン面接の特徴
SWE 面接との違い
ソウタはホワイトボードに比較表を書き出した。
| 観点 | SWE システムデザイン | FDE システムデザイン |
|---|---|---|
| 設計対象 | 自社プロダクト全体 | 顧客環境への統合 |
| 主な制約 | スケーラビリティ | セキュリティ・コンプライアンス |
| データ | 自社で管理 | 顧客のデータ主権 |
| インフラ | 自由に選択可能 | 顧客の既存インフラに制約 |
| 運用 | SRE チームが担当 | 顧客の運用チームと協業 |
| 成功基準 | QPS・レイテンシ | 顧客の業務が止まらないこと |
評価軸
FDE システムデザイン面接では、以下の5つの軸で評価される。
- マルチテナント設計 — テナント分離の戦略と実装
- セキュリティ・コンプライアンス — SOC2、GDPR、HIPAA への対応
- 移行戦略 — ゼロダウンタイムでの切り替え計画
- 運用設計 — 監視、アラート、障害復旧
- 顧客コミュニケーション — 技術的判断を非技術者に説明する能力
時間配分(60分)
| フェーズ | 時間 | やること |
|---|---|---|
| 要件整理 | 15分 | 機能要件、非機能要件、顧客制約のヒアリング |
| 高レベル設計 | 15分 | コンポーネント図、データフロー |
| 深掘り設計 | 20分 | 面接官が選んだ領域を詳細に設計 |
| 運用設計 | 10分 | 監視、障害対応、ロールバック戦略 |
WARNING
最初の15分で顧客の制約を聞き出さないまま設計に入ると、後半で「実はその顧客は金融機関で、パブリッククラウドに顧客データを置けません」と言われて設計がひっくり返ります。必ず最初に確認しましょう。
頻出テーマ 6 問
問題1: マルチテナント SaaS のエンタープライズ顧客対応
問題文: あなたの会社はマルチテナント SaaS を提供しています。大手金融機関の顧客から「他のテナントとデータを完全に分離してほしい」と要求されました。既存のマルチテナントアーキテクチャを維持しつつ、この顧客にシングルテナント分離を提供する設計をしてください。
テナント分離戦略の比較
ソウタがユウキに説明した3つの分離レベルがある。
| 戦略 | 分離レベル | コスト | 運用負荷 | 適用シーン |
|---|---|---|---|---|
| 行レベル分離 | 低 | 低 | 低 | 一般テナント |
| スキーマ分離 | 中 | 中 | 中 | 準エンタープライズ |
| DB インスタンス分離 | 高 | 高 | 高 | 金融・医療 |
アーキテクチャ
Rails でのテナントルーティング実装
# app/middleware/tenant_router.rb
class TenantRouter
ISOLATED_TENANTS = Rails.application.config.isolated_tenants
def initialize(app)
@app = app
end
def call(env)
request = ActionDispatch::Request.new(env)
tenant_id = extract_tenant_id(request)
if ISOLATED_TENANTS.include?(tenant_id)
# 専用DBに接続を切り替え
ActiveRecord::Base.connected_to(
role: :writing,
shard: :"tenant_#{tenant_id}"
) do
@app.call(env)
end
else
# 共有DBで行レベル分離
Current.tenant_id = tenant_id
@app.call(env)
end
end
private
def extract_tenant_id(request)
request.headers["X-Tenant-ID"] ||
request.subdomain.presence ||
raise(TenantNotFoundError)
end
endAWS 構成のポイント
- 専用テナントは VPC Peering で Arclight AI の管理 VPC と接続
- RDS for PostgreSQL の専用インスタンスを顧客専用サブネットに配置
- AWS KMS で顧客ごとの暗号化キーを管理(CMK: Customer Managed Key)
- Security Group で共有テナントのネットワークと完全に分離
INFO
面接では「なぜスキーマ分離ではなくインスタンス分離を選んだのか」を必ず聞かれます。金融機関の場合、監査要件で「物理的に異なるインスタンス」が求められることが多い点を説明しましょう。
問題2: レガシーシステムからのデータ移行パイプライン
問題文: 顧客は20年稼働しているオンプレミスの基幹システムを使っています。このシステムのデータをあなたの SaaS に移行する設計をしてください。移行中もシステムは稼働し続ける必要があります。
移行戦略の全体像
ETL vs ELT の選択
「レガシー移行では ETL を推奨する」とソウタは言った。「20年前のシステムにはデータの不整合が大量にある。ロード前にクレンジングしないと新システムが汚染される。ELT はクラウド DWH への分析基盤構築向きだ」
CDC(Change Data Capture)を使ったゼロダウンタイム移行
# app/services/migration/cdc_processor.rb
module Migration
class CdcProcessor
def initialize(tenant:, source_config:)
@tenant = tenant
@source_config = source_config
@kinesis = Aws::Kinesis::Client.new
end
def process_change_event(event)
case event[:operation]
when "INSERT"
upsert_record(event[:after])
when "UPDATE"
upsert_record(event[:after])
when "DELETE"
soft_delete_record(event[:before])
end
record_checkpoint(event[:lsn])
end
private
def upsert_record(data)
transformed = DataTransformer.transform(
data,
source_schema: @source_config[:schema],
target_schema: :current
)
target_model = transformed[:model].constantize
target_model.upsert(
transformed[:attributes],
unique_by: transformed[:unique_key]
)
end
def record_checkpoint(lsn)
MigrationCheckpoint.upsert(
{ tenant_id: @tenant.id, lsn: lsn, recorded_at: Time.current },
unique_by: :tenant_id
)
end
end
endデータ整合性の検証
移行後のデータ検証は3段階で行う。件数検証(レコード数一致)→ サンプル検証(ランダム1%を突合)→ ハッシュ検証(テーブル全体の MD5 比較)。
WARNING
面接で「ビッグバン移行でいいのでは?」と聞かれたら、それはトラップです。FDE の面接では必ず「顧客の業務を止めない移行」を提案してください。ビッグバン移行は週末の停止が必要で、金融機関では月末処理やバッチ処理と衝突するリスクがあります。
問題3: グローバル顧客向けのマルチリージョンデプロイ
問題文: ヨーロッパに本社を置く顧客が GDPR に準拠した形であなたの SaaS を利用したいと言っています。日本にも拠点があり、個人情報保護法への対応も必要です。マルチリージョンアーキテクチャを設計してください。
アーキテクチャ
データレジデンシー戦略
「GDPR で一番大事なのは、EU 市民のデータが EU 域外に出ないことだ」とカイが補足した。
| データ種別 | EU リージョン | AP リージョン | 同期方式 |
|---|---|---|---|
| 個人情報 | プライマリ | 保存しない | — |
| 設定データ | プライマリ | リードレプリカ | Aurora Global DB |
| 分析データ | 匿名化して保存 | 匿名化して保存 | S3 Cross-Region |
| 監査ログ | プライマリ | プライマリ(各自) | 同期しない |
Rails でのリージョンルーティング
# app/middleware/region_router.rb
class RegionRouter
REGION_MAP = {
"eu" => { database: :eu_primary, s3_bucket: "arclight-eu" },
"ap" => { database: :ap_primary, s3_bucket: "arclight-ap" }
}.freeze
def initialize(app)
@app = app
end
def call(env)
request = ActionDispatch::Request.new(env)
region = determine_region(request)
config = REGION_MAP.fetch(region)
ActiveRecord::Base.connected_to(
role: :writing,
shard: config[:database]
) do
Current.region = region
Current.s3_bucket = config[:s3_bucket]
@app.call(env)
end
end
private
def determine_region(request)
# 顧客のデータレジデンシー設定を優先
tenant = Tenant.find_by(
subdomain: request.subdomain
)
tenant&.data_residency_region || "ap"
end
endAWS 構成
- Route 53 Geolocation Routing でユーザーの地理的位置に基づくルーティング
- Aurora Global Database でリージョン間の低レイテンシレプリケーション(1秒未満)
- S3 Object Lock で GDPR の「忘れられる権利」対応時の監査証跡保持
- CloudFront のキャッシュポリシーで個人情報を含むレスポンスはキャッシュしない
INFO
面接で「全データを EU に集約すればシンプルでは?」と聞かれたら、レイテンシのトレードオフを説明しましょう。日本の拠点ユーザーが EU にアクセスすると RTT が 250ms 以上になり、UX が大幅に低下します。設定データのリードレプリカを AP に置くことで、読み取りレイテンシを 10ms 以下に抑えられます。
問題4: リアルタイム AI 推論パイプラインの顧客環境デプロイ
問題文: 顧客が「AI の推論は自社 VPC 内で完結させたい。データが外部に出るのは許可できない」と言っています。リアルタイムの AI 推論エンドポイントを顧客の VPC 内にデプロイする設計をしてください。
アーキテクチャ
「ポイントは、推論は顧客 VPC 内で完結するが、モデルの更新は Arclight 側から制御できること」とソウタは言った。
コールドスタート対策
| 対策 | レイテンシ改善 | コスト |
|---|---|---|
| Provisioned Concurrency | P99: 50ms以下 | 高 |
| ウォームプール | P99: 200ms以下 | 中 |
| モデル最適化(ONNX) | 全体20%改善 | なし |
| レスポンスキャッシュ | ヒット時0ms | 低 |
モデルバージョニングと A/B テスト
# app/services/ai/inference_router.rb
module AI
class InferenceRouter
def initialize(tenant:)
@tenant = tenant
@sagemaker = Aws::SageMakerRuntime::Client.new(
endpoint_url: tenant.sagemaker_endpoint_url
)
end
def predict(input)
variant = select_variant
response = @sagemaker.invoke_endpoint(
endpoint_name: @tenant.endpoint_name,
body: input.to_json,
content_type: "application/json",
target_variant: variant.name
)
result = JSON.parse(response.body.read)
record_prediction(variant, input, result)
result
end
private
def select_variant
variants = ModelVariant.where(
tenant: @tenant,
status: :active
)
# トラフィック分配に基づいて選択
roll = SecureRandom.random_number(100)
cumulative = 0
variants.find do |v|
cumulative += v.traffic_percentage
roll < cumulative
end
end
def record_prediction(variant, input, result)
PredictionLog.create!(tenant: @tenant, variant: variant,
input_hash: Digest::SHA256.hexdigest(input.to_json),
latency_ms: result["latency_ms"], model_version: variant.model_version)
end
end
endAWS PrivateLink 構成
- VPC Endpoint Service で Arclight の SageMaker エンドポイントを顧客 VPC に公開
- PrivateLink により、トラフィックは AWS バックボーンを経由し、インターネットに出ない
- S3 VPC Endpoint でモデルアーティファクトのダウンロードもプライベート接続
- CloudWatch Metrics は Arclight の管理アカウントにクロスアカウント共有
WARNING
「SageMaker のマルチモデルエンドポイントを使えばコスト削減できるのでは?」と聞かれたら、セキュリティ上の懸念を述べましょう。マルチモデルエンドポイントでは異なるテナントのモデルが同一インスタンスに載るため、金融機関の顧客には受け入れられないことが多いです。
問題5: 顧客向け監査ログ・コンプライアンスシステム
問題文: SOC2 と ISO27001 の認証を取得している顧客に、改ざん不可能な監査ログシステムを提供してください。ログは7年間保持し、リアルタイム検索と長期アーカイブの両方に対応する必要があります。
アーキテクチャ
改ざん防止の仕組み
監査ログで最も重要なのは 改ざん不可能性(Immutability)だ。
# app/models/audit_log.rb
class AuditLog < ApplicationRecord
# 更新・削除を禁止
def readonly?
persisted?
end
before_destroy { raise ActiveRecord::ReadOnlyRecord }
before_create :generate_integrity_hash
before_create :set_sequence_number
scope :for_tenant, ->(tenant) { where(tenant_id: tenant.id) }
scope :in_period, ->(from, to) { where(occurred_at: from..to) }
private
def generate_integrity_hash
previous = AuditLog.where(tenant_id: tenant_id)
.order(sequence_number: :desc)
.first
payload = {
tenant_id: tenant_id,
action: action,
actor_id: actor_id,
resource: resource_type,
resource_id: resource_id,
changes: changes_json,
occurred_at: occurred_at.iso8601,
previous_hash: previous&.integrity_hash || "GENESIS"
}
self.integrity_hash = OpenSSL::Digest::SHA256.hexdigest(
payload.to_json
)
end
def set_sequence_number
last_seq = AuditLog.where(tenant_id: tenant_id)
.maximum(:sequence_number) || 0
self.sequence_number = last_seq + 1
end
end保持期間とストレージ階層
0-30日は OpenSearch(ミリ秒応答)、31-365日は S3 Standard(秒応答)、1-7年は S3 Glacier(時間応答)に自動階層化する。S3 Object Lock(Governance モード)で管理者でも削除不可とし、Athena で長期アーカイブも SQL 検索可能にする。テナントごとに KMS CMK で暗号化を分離する。
INFO
面接で「ブロックチェーンを使って改ざん防止すべきでは?」と聞かれることがあります。ハッシュチェーン(前のログのハッシュを次のログに含める)で十分な改ざん検知が可能であり、ブロックチェーンのような分散合意は不要です。オーバーエンジニアリングを避ける判断力も評価されます。
問題6: 顧客の既存認証基盤との統合
問題文: エンタープライズ顧客が「自社の Active Directory でシングルサインオン(SSO)したい。ユーザーのプロビジョニングも自動化してほしい」と要求しています。SAML 2.0 / OIDC 対応の SSO と SCIM によるユーザー同期を設計してください。
認証フロー
「SSO 統合で一番ハマるのは、顧客ごとに IdP の設定が微妙に違うこと」とカイが言った。「SAML のクレーム名が違ったり、グループの属性名が違ったり。テンプレで対応できると思ったら痛い目を見る」
SAML / OIDC プロトコルの比較
| 項目 | SAML 2.0 | OIDC |
|---|---|---|
| トークン形式 | XML | JWT |
| 主な利用者 | エンタープライズ | モダンアプリ |
| 実装複雑度 | 高 | 中 |
| モバイル対応 | 困難 | 良好 |
| FDE での遭遇率 | 非常に高い | 高い |
Rails での OmniAuth 統合
# config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
# テナントごとに動的に SAML 設定を読み込む
provider :saml,
setup: ->(env) {
request = Rack::Request.new(env)
tenant = Tenant.find_by!(
subdomain: request.subdomain
)
sso = tenant.sso_configuration
env["omniauth.strategy"].options.merge!(
issuer: "https://#{tenant.subdomain}.arclight.ai",
idp_sso_service_url: sso.idp_sso_url,
idp_cert_fingerprint: sso.idp_cert_fingerprint,
name_identifier_format:
"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
attribute_statements: {
email: [sso.email_attribute || "email"],
name: [sso.name_attribute || "displayName"],
groups: [sso.groups_attribute || "memberOf"]
}
)
}
endJIT プロビジョニングとグループマッピング
# app/services/sso/jit_provisioner.rb
module SSO
class JitProvisioner
def initialize(tenant:)
@tenant = tenant
end
def provision(auth_hash)
user = find_or_create_user(auth_hash)
sync_group_memberships(user, auth_hash)
sync_permissions(user)
user
end
private
def find_or_create_user(auth_hash)
email = auth_hash.info.email.downcase
user = @tenant.users.find_by(email: email)
if user
update_user_attributes(user, auth_hash)
else
create_user(auth_hash)
end
end
def create_user(auth_hash)
@tenant.users.create!(
email: auth_hash.info.email.downcase,
name: auth_hash.info.name,
sso_uid: auth_hash.uid,
sso_provider: "saml",
password: SecureRandom.hex(32) # SSO専用なので不使用
)
end
def sync_group_memberships(user, auth_hash)
idp_groups = auth_hash.extra.raw_info
.attributes["groups"] || []
mappings = @tenant.group_mappings
mapped_roles = mappings.select { |m|
idp_groups.include?(m.idp_group_name)
}.map(&:app_role)
user.update!(roles: mapped_roles)
end
def sync_permissions(user)
permissions = user.roles.flat_map { |role|
RolePermission.where(role: role).pluck(:permission) }.uniq
user.update!(permissions: permissions)
end
end
endSCIM によるユーザー自動同期
SCIM(System for Cross-domain Identity Management)は、IdP 側でのユーザー変更を自動的に SaaS に反映する仕組みだ。作成(POST)、属性更新(PATCH)、無効化(PATCH active=false)の3操作をサポートし、JIT プロビジョニングと組み合わせることで、ユーザーライフサイクル全体を自動化できる。
WARNING
面接で SSO の設計をするとき、「パスワードログインも残すべきか」は頻出の深掘り質問です。答えは「テナント管理者に選択させる」です。SSO 強制モードと SSO + パスワード併用モードを設定可能にし、金融機関では SSO 強制を推奨します。ただし、SSO 障害時のブレークグラス(緊急アクセス)手順も必ず設計に含めましょう。
システムデザイン面接のフレームワーク「DEPLOY」
6つの問題を見てきたユウキに、ソウタはすべての問題に共通するフレームワークを伝えた。
「どんな問題が出ても、この DEPLOY フレームワークに沿って進めればいい」
D: Define requirements(要件定義)
最初の15分で以下を明確にする。
- 機能要件: 何を実現するのか
- 非機能要件: レイテンシ、スループット、可用性
- コンプライアンス要件: GDPR、SOC2、HIPAA、PCI DSS
- 顧客の既存環境: どのクラウド?オンプレ?ネットワーク構成は?
「面接官に質問をしない候補者は、ここで大きく減点される」とカイが言った。
E: Estimate scale(規模見積もり)
具体的な数字で議論する。DAU・同時接続数、初期/日次/年間データ量、QPS・ピーク倍率、レイテンシ要件(P50/P95/P99)を押さえる。
P: Propose high-level design(高レベル設計)
コンポーネント図を描く。主要コンポーネントの特定、データフローの方向、外部サービスとの境界、テナント分離の境界線を示す。この段階では詳細に入りすぎない。
L: Layer in details(詳細設計)
面接官が深掘りしたい領域に焦点を当てる。データモデル設計、API 設計、キャッシュ戦略、エラーハンドリングなど。
O: Operationalize(運用設計)
FDE 面接ではここが特に重要。監視(SLI/SLO)、アラート(誰にどう通知)、障害対応(ランブック、エスカレーション)、ロールバック(デプロイ失敗時の復旧)を設計する。
Y: Your customer's constraints(顧客制約)
DEPLOY フレームワークの最後にして最も FDE らしいステップ。顧客のセキュリティポリシー、変更管理プロセス(CAB 承認)、メンテナンスウィンドウ、ステークホルダー(CISO、コンプライアンス担当)を考慮する。
INFO
DEPLOY フレームワークは直線的に進むものではありません。特に「Y: 顧客制約」は最初の「D: 要件定義」の段階から意識すべきです。フレームワークの最後に置いているのは、設計全体を顧客制約の視点でレビューする最終チェックとしての意味があります。
面接官が見ているポイント TOP 5
ソウタは最後に、自分が面接官をするときの評価基準をユウキに教えた。
1. 顧客制約を最初に確認するか
「設計に入る前に、顧客の業界、規制環境、既存インフラを確認する候補者は強い。逆に、いきなりアーキテクチャを描き始める候補者は、どんなに技術力が高くても FDE には向いていない」
2. セキュリティ・コンプライアンスを自発的に言及するか
「面接官が聞く前に、データの暗号化、アクセス制御、監査ログに言及できるか。FDE にとってセキュリティは後付けではなく、設計の出発点だ」
3. 移行戦略を含めた設計か
「新システムの設計だけでなく、既存システムからの移行パスを含めて設計できるか。ビッグバン移行ではなく、段階的な移行を提案できるか」
4. 運用のことを考えているか
「デプロイして終わりではない。障害が起きたとき誰が対応するのか、ログをどう見るのか、ロールバックはどうするのか。顧客の運用チームのスキルレベルも考慮に入れた設計ができるか」
5. トレードオフを明確に説明できるか
「すべての選択にはトレードオフがある。コストとセキュリティ、パフォーマンスと一貫性、シンプルさとスケーラビリティ。正解を出すことよりも、トレードオフを明確に説明して、顧客の状況に応じた判断ができるかが重要だ」
章のまとめ
ユウキはノートを閉じて、深く息を吐いた。
「SWE の面接対策とは全然違いますね。技術力だけじゃなくて、顧客の立場で考える力が問われるんだ」
ソウタはうなずいた。「その通り。FDE のシステムデザイン面接で問われているのは、技術的に最適な設計ではなく、顧客にとって最適な設計ができるかだ」
カイが締めくくった。「DEPLOY フレームワークを体に染み込ませろ。特に D と Y だ。要件定義で顧客の制約を聞き出し、最後に顧客の視点で設計をレビューする。この習慣がついていれば、どんな問題が出ても対応できる」
ユウキは立ち上がった。面接まであと2週間。DEPLOY フレームワークを使って、6つの頻出テーマを何度も設計し直す日々が始まる。だが、もう漠然とした不安はなかった。何を聞かれても、まず「顧客の制約は何か」と問うことから始めればいい。
INFO
次章では、FDE 面接のもう一つの山場「ビヘイビア面接ラウンド」を攻略します。顧客との修羅場をどう語るか、STAR フレームワークの FDE 版を学びましょう。