【実話】レガシーシステムから.NET Aspireへ - ある物流企業の挑戦と成功
プロローグ:金曜日の夜、また障害通知が...
2024年7月の暑い金曜日の夜。時計は午後9時を回り、私はようやく帰宅の途についていました。電車の中でスマートフォンが振動し、見慣れたアラートが画面に表示されます。
「システム障害:在庫管理システム応答なし」
またか...と思いながら、急いでノートPCを取り出します。VPN接続、リモートデスクトップ、古いWindows Server 2003の画面が表示されます。20年前に構築されたこのシステムは、今や月間1000万件の配送を管理する基幹システムとなっていました。
クライアントの課題:限界に達したレガシーシステム
株式会社ロジスティクスX(仮名)の状況
私たちのクライアントである株式会社ロジスティクスXは、全国に15の物流センターを持つ中堅物流企業です。2004年に導入したシステムは、当時としては先進的でしたが、20年の歳月を経て、様々な問題を抱えていました。
システムの現状:
- Windows Server 2003 + SQL Server 2005(サポート終了)
- ASP.NET Web Forms(.NET Framework 2.0)
- 単一のモノリシックアプリケーション
- 垂直スケーリングの限界(すでに最大スペックのサーバー)
- 平均応答時間:5秒以上(ピーク時は30秒超)
「もう限界です」- IT部長の悲痛な叫び
「正直、もう限界なんです」
初回の打ち合わせで、IT部長の田中さん(仮名)は疲れ切った表情でそう語りました。
「毎週のように障害が発生し、その度に徹夜で対応。新機能の追加どころか、現状維持すら困難。でも、このシステムが止まれば、全国の配送がストップしてしまう...」
転機:.NET Aspireとの出会い
なぜ.NET Aspireを選んだのか
様々な選択肢を検討する中で、.NET Aspireに注目した理由は明確でした:
- 既存の.NET資産を活かせる - 20年間蓄積したビジネスロジックを段階的に移行可能
- 開発者の学習曲線が緩やか - 既存のC#開発者がすぐに開発開始できる
- ローカル開発環境の簡便性 - Docker Composeの複雑な設定が不要
- 組み込みの観測可能性 - 最初から監視・ログ・トレーシングが統合
「これなら、うちでもできるかもしれない」
技術検証のデモを見た開発チームの反応は予想以上でした。
「Docker Composeで苦労していたサービス間の接続が、こんなに簡単に...」 「ローカルでデバッグできるのは大きい。今までは本番環境でしか再現しない問題に悩まされていた」
移行プロジェクト:6ヶ月間の軌跡
Phase 1:パイロットプロジェクト(1-2ヶ月目)
最初のターゲットは「在庫照会機能」でした。比較的独立性が高く、読み取り専用のため、リスクが低いと判断しました。
// 旧システム:1つのメソッドで全てを処理
public DataTable GetInventory(string warehouseId, string productCode)
{
string sql = @"
SELECT * FROM Inventory
WHERE WarehouseID = @warehouseId
AND ProductCode LIKE '%' + @productCode + '%'";
// SQLインジェクションの脆弱性も...
}
// 新システム:クリーンアーキテクチャで実装
public class InventoryService : IInventoryService
{
private readonly IInventoryRepository _repository;
private readonly IDistributedCache _cache;
private readonly ILogger<InventoryService> _logger;
public async Task<InventoryDto> GetInventoryAsync(
string warehouseId,
string productCode)
{
var cacheKey = $"inventory:{warehouseId}:{productCode}";
return await _cache.GetOrCreateAsync(cacheKey, async () =>
{
var inventory = await _repository.GetByWarehouseAndProductAsync(
warehouseId, productCode);
return inventory.ToDto();
}, TimeSpan.FromMinutes(5));
}
}
最初の成功体験
パイロットプロジェクトの結果は劇的でした:
- 応答時間: 5秒 → 200ミリ秒(25倍高速化)
- 同時接続数: 100 → 1000(10倍向上)
- サーバーコスト: 月額30万円 → 月額8万円(73%削減)
「これは...本当にうちのシステムですか?」
田中部長の驚きの表情は今でも忘れられません。
Phase 2:段階的マイグレーション(3-5ヶ月目)
成功を確信した私たちは、Strangler Figパターンを使って段階的な移行を開始しました。
# API Gatewayでの段階的切り替え
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: logistics-gateway
spec:
rules:
- http:
paths:
# 新システムに移行済みのパス
- path: /api/inventory
backend:
service:
name: inventory-service
port: 80
- path: /api/shipping
backend:
service:
name: shipping-service
port: 80
# レガシーシステムへのフォールバック
- path: /
backend:
service:
name: legacy-proxy
port: 80
最大の危機:データ整合性の問題
4ヶ月目、大きな問題が発生しました。新旧システム間でデータの不整合が発生したのです。
「出荷指示が重複している!」 「在庫数が合わない!」
緊急ミーティングが開かれ、プロジェクト中止の声も上がりました。しかし、.NET Aspireの分散トレーシング機能が問題解決の鍵となりました。
// 問題のあったコード
public async Task ProcessShippingAsync(ShippingRequest request)
{
// 新システムで処理
await _newSystem.CreateShippingOrderAsync(request);
// レガシーシステムに同期...のつもりが
await _legacySync.SyncShippingAsync(request); // ここでタイムアウト
}
// 改善後:Sagaパターンで確実な同期を実現
public class ShippingSaga : IStateMachine<ShippingSagaState>
{
public State Submitted { get; private set; }
public State ProcessedInNew { get; private set; }
public State SyncedToLegacy { get; private set; }
public State Completed { get; private set; }
public State Failed { get; private set; }
// 各ステップで確実に状態を管理
}
Phase 3:完全移行(6ヶ月目)
最終的に、6ヶ月ですべての機能を移行することができました。
驚きの結果:数字が物語る成功
パフォーマンスの劇的改善
指標 | 移行前 | 移行後 | 改善率 |
---|---|---|---|
平均応答時間 | 5.2秒 | 0.3秒 | 94%削減 |
ピーク時応答時間 | 32秒 | 1.2秒 | 96%削減 |
同時処理可能数 | 100件 | 5,000件 | 50倍 |
システム稼働率 | 98.2% | 99.95% | 大幅改善 |
コスト削減の実現
【移行前】
- サーバー費用: 月額120万円(物理サーバー4台)
- 保守人件費: 月額200万円(障害対応の残業代含む)
- 合計: 月額320万円
【移行後】
- クラウド費用: 月額45万円(Azure Container Apps)
- 保守人件費: 月額80万円(自動化により大幅削減)
- 合計: 月額125万円
年間コスト削減額: 2,340万円(61%削減)
開発生産性の向上
最も印象的だったのは、開発チームの変化でした。
「新機能の追加が1週間でできるようになった。以前は1ヶ月かかっていたのに」 「障害対応で徹夜することがなくなった。家族との時間が増えました」 「コードを書くのが楽しくなった」
技術的な学びと工夫
1. 観測可能性がすべての基盤
// カスタムメトリクスで業務KPIを可視化
public class BusinessMetrics
{
private readonly Counter<long> _ordersProcessed;
private readonly Histogram<double> _orderProcessingTime;
private readonly Gauge<int> _pendingOrders;
public void RecordOrderProcessed(Order order)
{
_ordersProcessed.Add(1,
new KeyValuePair<string, object?>("warehouse", order.WarehouseId),
new KeyValuePair<string, object?>("priority", order.Priority));
_orderProcessingTime.Record(
order.ProcessingTime.TotalMilliseconds,
new KeyValuePair<string, object?>("order_type", order.Type));
}
}
2. レガシーシステムとの共存戦略
// Anti-Corruption Layerパターンで新旧の違いを吸収
public class LegacyInventoryAdapter : IInventoryRepository
{
private readonly LegacyInventoryService _legacyService;
private readonly IMapper _mapper;
public async Task<Inventory> GetByIdAsync(string id)
{
// レガシーシステムのデータ構造を変換
var legacyData = await _legacyService.GetInventoryData(id);
return _mapper.Map<Inventory>(legacyData);
}
}
3. 段階的な性能改善
// Feature Flagで新機能を段階的に有効化
if (await _featureFlags.IsEnabledAsync("UseRedisCache"))
{
return await _cache.GetOrCreateAsync(key, factory);
}
else
{
return await factory();
}
お客様の声:1年後のインタビュー
IT部長 田中さん
「正直、最初は半信半疑でした。20年物のシステムを本当に移行できるのか。でも、Enhancedさんのサポートと.NET Aspireの力で、想像以上の結果を得られました。何より、うちの開発チームが生き生きと働いているのが嬉しいです」
開発リーダー 山田さん
「.NET Aspireの学習曲線の緩やかさに助けられました。既存の.NETの知識を活かしながら、最新のクラウドネイティブ技術を習得できた。今では、新しい要求にも素早く対応できる自信があります」
経営層の評価
「IT投資のROIがこれほど明確に出たプロジェクトは初めてです。コスト削減だけでなく、ビジネスの俊敏性が格段に向上しました。競合他社に対する大きなアドバンテージを得られました」
エピローグ:新たな金曜日の夜
2025年6月、同じ金曜日の夜。私は定時で帰宅し、家族と夕食を楽しんでいます。スマートフォンは静かで、アラートは鳴りません。
ダッシュボードを確認すると、すべてのシステムは正常。自動スケーリングが需要の増加に対応し、エラー率は0.01%以下を維持しています。
「パパ、今日は早いね」
娘の言葉に、このプロジェクトの本当の価値を実感します。技術は人を幸せにするためにある。.NET Aspireは、それを可能にしてくれました。
まとめ:あなたの組織でも実現可能
この事例から学べること:
- レガシーシステムの移行は不可能ではない - 適切な戦略と技術選定で実現可能
- .NET Aspireは実戦で証明済み - 月間1000万件の処理に耐える堅牢性
- 段階的移行がリスクを最小化 - すべてを一度に変える必要はない
- 投資対効果は明確 - 年間2,340万円のコスト削減を実現
- 人の幸せが最終目標 - 技術は手段、目的は生産性と生活の質の向上
もし、あなたの組織でも同様の課題を抱えているなら、.NET Aspireという選択肢を検討してみてはいかがでしょうか。私たちEnhancedは、その挑戦を全力でサポートします。
お問い合わせ: レガシーシステムのモダナイゼーションについてのご相談は、お問い合わせフォームからお気軽にどうぞ。