モダナイゼーションの進め方

技術選定調査レポート: モダンアーキテクチャによる再構築

対象: 未来明るく電力基幹システム(VB6 + COBOL, 596プログラム, 約50万行) 調査日: 2026-06-16 前提: .NET をコアスタックとして採用(04-playbook.md Pattern A)


目次


1. バッチ処理フレームワーク

1.1 未来明るく電力のバッチ処理の特性

現行システムのバッチ処理を整理すると:

特性
バッチプログラム数~400本(パターンA/B/C/D)
実行頻度日次数回 + 月初集中
データ規模数万〜数十万レコード/回
処理パターン直列パイプライン(BAT で10-17本を順次起動)
実行時間要確認(ログ調査で判明予定)
外部連携EDI受信後のトリガー実行

重要な特性: 未来明るく電力のバッチは「大規模分散処理」ではなく「中規模な定型処理の順次実行」である。Spark や Hadoop 級のスケーラビリティは不要。

1.2 候補技術の比較

A. Hangfire(推奨 - 第1候補)

項目評価
概要.NET 向けバックグラウンドジョブ処理。DB永続化ダッシュボード付き
学習コスト — NuGet追加 + 数行のコードで即動作
.NET相性最高 — ASP.NET Core にネイティブ統合
保守性 — Web ダッシュボードでジョブ状態が可視化
ライセンスOSS (LGPL) + Pro版(有償)。Proはバッチ処理に便利な機能あり
適合度
// Hangfire でのバッチ処理例(CHP060TB.BAT の置換)
public class GridBatchOrchestrator
{
    // 日次バッチ: CRY125S → CRY125 → VRY811B → ... → CRY413
    public void ExecuteDailyBatch()
    {
        var batchId = BatchJob.StartNew(batch =>
        {
            var job1 = batch.Enqueue<ICro125SService>(s => s.ExecuteAsync());
            var job2 = batch.ContinueJobWith<ICro125Service>(job1, s => s.ExecuteAsync());
            var job3 = batch.ContinueJobWith<IVro811BService>(job2, s => s.ExecuteAsync());
            // ... 順次実行
            batch.ContinueJobWith<ICro413Service>(job3, s => s.ExecuteAsync());
        });
    }
}
csharp

未来明るく電力に適する理由:

  • 1日数回のバッチ → Hangfire のジョブキューで十分
  • BATの順次実行 → ContinueJobWith で等価な依存チェーンが作れる
  • Web ダッシュボードで実行状況・エラーが一目で確認可能(現行のログファイル確認から大幅改善)
  • .NET プロジェクト内で完結(別インフラ不要)

制約:

  • Hangfire Pro(バッチ機能)は有償($500/サーバー/年〜)
  • OSS版でもRecurringJobとBackgroundJob.ContinueWithで代替可能

B. Quartz.NET

項目評価
概要Java Quartz の .NET 移植。エンタープライズ級スケジューラ
学習コスト — Trigger/Job/Scheduler の概念理解が必要
.NET相性 — .NET 8+ 対応、クラスタリング可能
保守性 — ダッシュボードなし(別途構築 or CrystalQuartz等のOSSアドオン)
ライセンスApache 2.0(完全OSS)
適合度

未来明るく電力への評価: Cron式による精密なスケジューリングが強み。ただし、未来明るく電力の用途(EDI受信後のトリガー+日次定時)にはやや重厚。ダッシュボードが標準で付かない点が運用上のデメリット。

C. Azure Functions (Timer Trigger) / Azure Durable Functions

項目評価
概要サーバーレスFaaS。Timer Triggerで定時実行、Durable Functionsで複雑なワークフロー
学習コスト中〜高 — Azure の概念理解、Durable Functions のオーケストレーターパターン
.NET相性 — Microsoft 純正
保守性 — Azure Portal で監視・ログ統合
コスト従量課金(未来明るく電力の規模なら月数千円程度)
適合度(Azure採用が確定している場合は高)
// Azure Durable Functions でのパイプライン例
[FunctionName("GridBatchOrchestrator")]
public static async Task RunOrchestrator(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    await context.CallActivityAsync("CRY125S_Execute", null);
    await context.CallActivityAsync("CRY125_Execute", null);
    await context.CallActivityAsync("VRY811B_Execute", null);
    // ...順次実行、自動リトライ・チェックポイント付き
    await context.CallActivityAsync("CRY413_Execute", null);
}
csharp

未来明るく電力への評価: インフラ管理不要で運用負荷が低い。ただし、Azure へのロックインと、ローカル開発・デバッグのハードルがある。AWS Lambda Durable Functions(2025年12月リリース)も同等機能を提供開始。

D. AWS Step Functions

項目評価
概要AWS のワークフローオーケストレーション。JSON/YAML でステートマシン定義
学習コスト中〜高 — Amazon States Language の習得
.NET相性 — Lambda (.NET) を呼び出す形。ネイティブ統合ではない
保守性 — ビジュアルワークフロー、CloudWatch連携
適合度低〜中(AWS採用の場合)

未来明るく電力への評価: ビジュアルなワークフロー定義が直感的。ただし .NET との統合が間接的で、開発体験はAzure Durable Functionsに劣る。

E. パイプライン系(Airflow / Dagster / Prefect / Temporal)

ツール.NET相性学習コスト未来明るく電力への適合度備考
Apache Airflow低(Python)データエンジニアリング向け。Python必須。未来明るく電力には過剰
Dagster低(Python)データアセット管理向け。未来明るく電力のバッチには不適
Prefect低(Python)Python-native。シンプルだがC#エコシステムと乖離
Temporal中(.NET SDK はベータ)分散ワークフローに強いが、未来明るく電力の規模には過剰。.NET SDKがまだベータ段階

パイプライン系の総評: これらは主にPythonエコシステムのデータパイプライン向けであり、.NETベースの電力事業バッチ処理にはオーバーキル。Airflow/Dagster/Prefect はPython必須のため、C#メインのプロジェクトでは技術スタックが分裂する。Temporalは強力だが .NET SDK がまだベータで、未来明るく電力の「日次数回のバッチ」には機能過剰。

1.3 バッチ処理の推奨

Phase 1-2

Hangfire Pro + ASP.NET Core Worker Service

既存 BAT パイプラインを Hangfire のジョブチェーンに変換 / Web ダッシュボードで運用監視 / SQL Server / PostgreSQL にジョブ状態永続化

Phase 3 以降・Azure移行時

Azure Durable Functions への移行を検討

Hangfire→Durable Functions は比較的容易(ジョブ = Activity 関数) / Azure の監視・アラート統合


2. 需要集約(系統展開)の実装パターン

2.1 未来明るく電力の需要集約の現状

仕様書と解析結果から推定される現行の需要集約の構造:

図を読み込み中…

  • 系統展開は COBOL プログラム群(CJK0G1等)で実行
  • 設備マスタ SB010 の親子関係を辿って需要量を計算
  • COBOL の PERFORM で再帰的に展開(または反復的にレベル別処理)

2.2 系統展開のアルゴリズム

方式1: 再帰ツリー走査(Depth-First)

// 再帰的系統展開 — 直感的だが深い系統構成でスタック問題の可能性
public class GridExpansionService
{
    private readonly IGridRepository _gridRepo;

    public async Task<List<MaterialRequirement>> ExplodeAsync(
        string pointCode, decimal requiredQty, DateTime dueDate)
    {
        var requirements = new List<MaterialRequirement>();
        await ExplodeRecursiveAsync(pointCode, requiredQty, dueDate, 0, requirements);
        return requirements;
    }

    private async Task ExplodeRecursiveAsync(
        string parentItem, decimal parentQty, DateTime dueDate,
        int level, List<MaterialRequirement> results)
    {
        var children = await _gridRepo.GetChildrenAsync(parentItem);

        foreach (var child in children)
        {
            var grossReq = parentQty * child.QuantityPer; // 構成数量を乗算
            var netReq = grossReq - await GetAvailableStockAsync(child.PointCode);

            results.Add(new MaterialRequirement
            {
                PointCode = child.PointCode,
                Level = level + 1,
                GrossRequirement = grossReq,
                NetRequirement = Math.Max(0, netReq),
                DueDate = dueDate.AddDays(-child.LeadTimeDays),
            });

            // 子が製造品(中間品)なら再帰展開
            if (child.FacilityType == FacilityType.Manufactured)
            {
                await ExplodeRecursiveAsync(
                    child.PointCode, Math.Max(0, netReq),
                    dueDate.AddDays(-child.LeadTimeDays),
                    level + 1, results);
            }
        }
    }
}
csharp

方式2: ローレベルコード法(Level-by-Level)— 推奨

// Low-Level Code 法: 需要集約の標準アルゴリズム
// 同一設備が複数の親に使われる場合の二重計算を防ぐ
public class GridLoadService
{
    /// <summary>
    /// Low-Level Code 法による 需要集約 展開。
    /// 各設備の「最下位レベル」を事前計算し、レベル0から順に処理する。
    /// 同一設備が複数の親に使われる場合でも正確にNetRequirementを算出。
    /// </summary>
    public async Task<GridLoadResult> RunGridLoadAsync(
        IEnumerable<DemandRecord> demands)
    {
        // Step 1: Low-Level Code の計算
        // 設備が系統ツリーの中で出現する最も深いレベルを事前計算
        var lowLevelCodes = await ComputeLowLevelCodesAsync();
        var maxLevel = lowLevelCodes.Values.Max();

        // Step 2: 需要を設備別に集約
        var requirementsByFacility = new Dictionary<string, GrossNetRecord>();
        foreach (var demand in demands)
        {
            AccumulateDemand(requirementsByItem, demand.PointCode, demand.Quantity, demand.DueDate);
        }

        // Step 3: レベル0(完成品)から順に展開
        for (int level = 0; level <= maxLevel; level++)
        {
            var itemsAtLevel = lowLevelCodes
                .Where(kv => kv.Value == level)
                .Select(kv => kv.Key);

            foreach (var pointCode in itemsAtLevel)
            {
                if (!requirementsByItem.TryGetValue(pointCode, out var record))
                    continue;

                // Gross → Net 計算(在庫・工事予定残を差し引き)
                var netReq = ComputeNetRequirement(record);

                if (netReq <= 0) continue;

                // 系統構成 下位設備への展開
                var children = await _gridRepo.GetChildrenAsync(pointCode);
                foreach (var child in children)
                {
                    var childReq = netReq * child.QuantityPer;
                    AccumulateDemand(requirementsByItem,
                        child.PointCode, childReq,
                        record.DueDate.AddDays(-child.LeadTimeDays));
                }
            }
        }

        return new GridLoadResult(requirementsByItem);
    }
}
csharp

方式3: フラット化テーブル(事前展開)

-- 系統構成をフラット化してDBに保持するパターン
-- 展開済みの構成をテーブルに持ち、JOINで一発取得
CREATE TABLE GRID_FLAT (
    ParentFacility   VARCHAR(20),
    ChildFacility    VARCHAR(20),
    Level        INT,
    CumulativeQty DECIMAL(18,6),  -- 累積構成数量(親からの乗算済み)
    LeadTimeDays INT,
    PRIMARY KEY (ParentItem, ChildItem, Level)
);

-- 系統展開がSQLの集計クエリに変換できる
SELECT ChildItem, SUM(DemandQty * CumulativeQty) AS TotalRequirement
FROM GRID_FLAT bf
JOIN Demand d ON bf.ParentFacility = d.PointCode
GROUP BY ChildItem;
sql

2.3 方式比較と推奨

方式利点欠点未来明るく電力への適合
再帰ツリー直感的、リアルタイム展開可能深い系統構成でパフォーマンス劣化、同一設備の重複計算
ローレベルコード法需要集約の標準、重複計算なし、正確Low-Level Code の事前計算が必要高(推奨)
フラット化テーブルSQL一発で高速、参照が容易系統構成変更時に再計算必要、ストレージ増加中〜高(参照用に併用可)

推奨: ローレベルコード法をメインの需要集約計算エンジンとして実装し、参照用にフラット化テーブルを併用する。

推奨構成

  • 需要集約計算: Low-Level Code 法(C# で実装)
  • 系統構成参照: フラット化テーブル(系統構成変更時にバッチで再計算)
  • 系統構成マスタ: 再帰的な親子関係テーブル(正規化されたマスタ)

2.4 電力事業特有の考慮事項

考慮事項対応
系統階層の深さ送配電系統は通常3-5階層。ローレベルコード法で十分な性能
代替供給地点番号TARIFF(料金種別)による供給地点番号バリエーション。系統構成マスタで管理
需給調整方式系統展開結果 → 調整力配分計算(CSBKxx 系の処理)
需要想定の不確実性N0TTL/N1TTL/N2TTL の3ヶ月分を段階的に確定。確定度別の計算が必要

3. データ変換パイプライン

3.1 未来明るく電力のデータ変換パターンの分析

596本中の主要パターン:

パターン本数現行実装データフロー
A: EDIデータ変換~200VB6 + COBOL固定長ファイル → パース → DB INSERT/UPDATE
B: DB間変換~150VB6 + COBOLSELECT → 計算 → INSERT/UPDATE
C: 一括更新~50COBOLSELECT * → 同一処理 → UPDATE
D: 帳票生成~60COBOLJOIN/集計 → テキスト/CSV

3.2 ETL vs ELT: 未来明るく電力のケース

基準ETLELT
定義Extract → Transform → LoadExtract → Load → Transform
変換場所ミドルウェア/アプリケーションデータベース内(SQL)
適合するケース複雑なビジネスロジック変換、レガシーシステム、データクレンジング大量データの集計・分析、クラウドDWH活用

結論: 未来明るく電力には ETL が適切

理由:

  1. 業務ロジックの複雑さ: EDI変換は連携先ごとに異なるフォーマット解析が必要 → C# で表現すべき
  2. 固定長ファイル解析: バイト位置でのフィールド切り出しはアプリケーションコードの仕事
  3. データクレンジング: マジックバリュー変換("前回"/"今回" → enum)はビジネスルール
  4. 段階的移行: 既存ロジックの1:1変換はETLの方が追跡しやすい
  5. データ規模: 数万レコード → ELT のスケーラビリティ優位は不要

ただし、パターンC(一括更新)とパターンD(帳票の集計クエリ)は SQL で完結する処理が多く、部分的に ELT(SQL内で変換)を採用できる。

3.3 データ変換の設計パターン

Pipeline パターン(推奨 - メインパターン)

// Pipeline パターン: EDI変換処理の汎用化
// パターンA(EDIデータ変換)に最適
public interface IPipelineStep<T>
{
    Task<T> ExecuteAsync(T input, CancellationToken ct);
}

// EDI処理パイプラインの例(VKNDD0B のモダン版)
public class EdiPipeline<TRecord>
{
    private readonly List<IPipelineStep<EdiContext<TRecord>>> _steps = [];

    public EdiPipeline<TRecord> AddStep(IPipelineStep<EdiContext<TRecord>> step)
    {
        _steps.Add(step);
        return this;
    }

    public async Task<EdiResult> ExecuteAsync(Stream inputFile, CancellationToken ct)
    {
        var context = new EdiContext<TRecord> { InputStream = inputFile };

        foreach (var step in _steps)
        {
            context = await step.ExecuteAsync(context, ct);
            if (context.HasError) break; // エラー時は中断
        }

        return context.ToResult();
    }
}

// 使用例: 料金単価データ処理パイプライン
var pipeline = new EdiPipeline<MeterDataRecord>()
    .AddStep(new ValidateControlTable())      // RN920 制御テーブル確認
    .AddStep(new ParseFixedLengthFile())       // 138byte 固定長解析
    .AddStep(new FilterTargetRecords())        // CTL="D", ID="1", JIGYOSHA="2903"
    .AddStep(new ClassifyPreviousCurrent())    // KUBUN による前回/今回分類
    .AddStep(new UpsertToDatabase())           // HN050 への INSERT/UPDATE/DELETE
    .AddStep(new UpdateControlStatus())        // RN920 ステータス更新
    .AddStep(new WriteProcessingLog());        // ログ出力
csharp

Strategy パターン(連携先別変換に最適)

// 連携先ごとに異なるEDIフォーマットを Strategy で切り替え
public interface IEdiFormatParser
{
    string CustomerCode { get; }  // "01"=低圧需要家, etc.
    IEnumerable<ContractRecord> Parse(Stream input);
}

public class MdmsMeterParser : IEdiFormatParser
{
    public string CustomerCode => "01";

    public IEnumerable<ContractRecord> Parse(Stream input)
    {
        // MDMSLP30: 250byte 固定長レコード
        // バイト位置: 0-9=供給地点番号, 10-15=数量, ...
        using var reader = new StreamReader(input, Encoding.GetEncoding("Shift_JIS"));
        // ...
    }
}

public class SwitchingEdiParser : IEdiFormatParser
{
    public string CustomerCode => "SWITCHING";

    public IEnumerable<ContractRecord> Parse(Stream input)
    {
        // SWREQ21: 可変長レコード
        // ...
    }
}

// DI で全パーサーを登録、連携先コードで自動選択
services.AddTransient<IEdiFormatParser, MdmsMeterParser>();
services.AddTransient<IEdiFormatParser, SwitchingEdiParser>();
services.AddTransient<IEdiFormatParser, TakusoEdiParser>();
// ... 連携先追加時は新クラスを追加するだけ
csharp

MediatR + CQRS パターン(契約/工事処理に適用可能)

// CQRS で契約コマンドと工事照会を分離
// 注意: MediatR は 2025年7月から商用ライセンス化

// Command: 契約データ登録
public record ProcessContractCommand(
    string CustomerCode,
    Stream EdiData) : IRequest<ContractResult>;

public class ProcessContractHandler : IRequestHandler<ProcessContractCommand, ContractResult>
{
    private readonly IEnumerable<IEdiFormatParser> _parsers;
    private readonly IContractRepository _contractRepo;

    public async Task<ContractResult> Handle(
        ProcessContractCommand request, CancellationToken ct)
    {
        var parser = _parsers.First(p => p.CustomerCode == request.CustomerCode);
        var contracts = parser.Parse(request.EdiData);
        await _contractRepo.BulkUpsertAsync(contracts, ct);
        return new ContractResult(contracts.Count());
    }
}

// Query: 契約照会
public record GetContractStatusQuery(string PointCode, DateRange Period)
    : IRequest<ContractStatusResult>;
csharp

MediatR の商用化について: 2025年7月にMediatR作者のJimmy BogardがLucky Penny Softwareとして商用ライセンスを開始。OSS版は引き続き利用可能だが、商用利用にはライセンス購入が必要。代替として .NET 10 の IServiceProvider ベースで自前のCQRSディスパッチャーを構築する選択肢もある。

3.4 推奨パターンのマッピング

処理パターン推奨設計パターン理由
A: EDI変換Pipeline + Strategyフォーマット解析の多様性をStrategyで吸収、処理手順をPipelineで統一
B: DB間変換Pipeline or CQRS単純な変換はPipeline、複雑な業務ロジックはCQRSで分離
C: 一括更新直接SQL (ELT的)UPDATE SET ... WHERE 一文で済む処理はSQLに寄せる
D: 帳票生成Repository + 帳票エンジンデータ取得と帳票レンダリングを分離

4. レポート/帳票生成

4.1 未来明るく電力の帳票の特性

特性
帳票プログラム数~60本(パターンD)
出力形式テキストファイル、CSV
処理内容DB JOIN/集計 → 固定幅テキスト整形
代表例需給計画表、需給調整指令リスト、需要集約リスト、月次工事計画
日本語Shift_JIS(UTF-8移行が必要)

現行の「帳票」は COBOL/VB6 でテキストファイルを生成する処理であり、GUI帳票デザイナーを使った帳票ではない。

4.2 候補技術の比較

A. QuestPDF(推奨 - 第1候補)

項目評価
概要.NET 向け PDF 生成。Fluent API でコードファーストに帳票設計
学習コスト — C# Fluent API で直感的
.NET相性最高 — ネイティブ .NET ライブラリ
保守性 — 帳票定義がC#コード = レビュー・バージョン管理可能
ライセンスCommunity(無料・OSS)/ Professional / Enterprise
日本語対応フォント埋め込みで日本語PDF生成可能
最新版2026.6.0(ARM64 対応、活発に開発中)
適合度
// QuestPDF による需給計画表の例
Document.Create(container =>
{
    container.Page(page =>
    {
        page.Size(PageSizes.A4.Landscape());
        page.Margin(1, Unit.Centimetre);

        page.Header().Text("需給計画表")
            .FontSize(16).Bold().AlignCenter();

        page.Content().Table(table =>
        {
            table.ColumnsDefinition(cols =>
            {
                cols.ConstantColumn(80);  // 供給地点番号
                cols.ConstantColumn(120); // 設備名
                cols.RelativeColumn();    // 当月計画
                cols.RelativeColumn();    // 翌月計画
                cols.RelativeColumn();    // 翌々月計画
            });

            // ヘッダー行
            table.Header(header =>
            {
                header.Cell().Text("供給地点番号").Bold();
                header.Cell().Text("設備名").Bold();
                header.Cell().Text("N0(当月)").Bold();
                header.Cell().Text("N1(翌月)").Bold();
                header.Cell().Text("N2(翌々月)").Bold();
            });

            // データ行
            foreach (var item in supplyPlan)
            {
                table.Cell().Text(item.PointCode);
                table.Cell().Text(item.FacilityName);
                table.Cell().Text(item.N0Total.ToString("#,##0"));
                table.Cell().Text(item.N1Total.ToString("#,##0"));
                table.Cell().Text(item.N2Total.ToString("#,##0"));
            }
        });
    });
}).GeneratePdf("supply-plan.pdf");
csharp

未来明るく電力に適する理由:

  • コードファースト → 帳票がソースコード管理・テスト可能
  • テキスト帳票 → PDF化による品質向上(印刷も可能)
  • 高速生成(数千ページ/秒)
  • OSS Community 版は10ページ/ドキュメントまで無料。Professional は $699〜

B. FastReport .NET

項目評価
概要ビジュアルデザイナー付きの帳票エンジン。RDLCインポート可能
学習コスト — デザイナーは直感的だが、複雑なレイアウトは学習必要
.NET相性
保守性 — 帳票定義が XML/バイナリ形式のため、コードレビューしにくい
ライセンス商用($399〜/開発者)
適合度

未来明るく電力への評価: ビジュアルデザイナーでノンプログラマーでも帳票修正可能な点は利点。ただし、未来明るく電力の帳票はプログラマーが管理するバッチ出力であり、ビジュアルデザイナーの恩恵は限定的。

C. RDLC (Microsoft Report Definition Language Client)

項目評価
概要Visual Studio 統合の帳票定義。レガシーだが安定
学習コスト
.NET相性 — .NET Framework 時代の技術。.NET Core 対応は制限あり
保守性 — Microsoft の積極的な開発は終了、レガシー扱い
ライセンス無料(Visual Studio 付属)
適合度

未来明るく電力への評価: 新規プロジェクトで採用する理由がない。既存資産がない以上、他を選ぶべき。

D. CSV/テキスト出力の汎用化

現行の帳票の多くはCSV/テキスト出力であり、PDF化が必須ではないものもある。

// 汎用 CSV 出力サービス
public class ReportExportService
{
    // CsvHelper で型安全なCSV出力
    public async Task ExportCsvAsync<T>(
        IEnumerable<T> data, Stream output, CsvConfiguration? config = null)
    {
        config ??= new CsvConfiguration(CultureInfo.GetCultureInfo("ja-JP"))
        {
            HasHeaderRecord = true,
            Encoding = Encoding.UTF8, // Shift_JIS → UTF-8
        };

        await using var writer = new StreamWriter(output, config.Encoding);
        await using var csv = new CsvWriter(writer, config);
        await csv.WriteRecordsAsync(data);
    }

    // PDF出力が必要な場合は QuestPDF
    public void ExportPdf<T>(IEnumerable<T> data, string templateName, Stream output)
    {
        var template = _templateRegistry.Get(templateName);
        template.Render(data, output);
    }
}
csharp

4.3 帳票の推奨構成

PDF帳票

QuestPDF(Community or Professional)

需給計画表、需給調整指令リスト等の定型帳票 / コードファーストで開発・テスト可能

CSV/Excel

CsvHelper + ClosedXML

データエクスポート系の帳票 / ユーザーが Excel で加工する帳票

テキスト

標準 StreamWriter

外部連携用の固定長テキスト出力(EDI送信データ等)


5. マスタメンテ画面

5.1 未来明るく電力のマスタメンテの特性

特性
画面数~30本(パターンE)
現行技術VB6 フォーム
対象テーブル177テーブル中のマスタ系
操作一覧表示、検索、新規登録、更新、削除
ユーザー数要確認(おそらく数名〜十数名)
同時アクセス要確認(低〜中程度と推定)

5.2 UI フレームワーク比較

A. Blazor Server(推奨 - 第1候補)

項目評価
概要サーバーサイドで C# を実行、UI 差分を SignalR で配信
学習コスト — C# + Razor で完結。JavaScript 不要
.NET相性最高 — Microsoft 純正、.NET 10 で進化継続
CRUD開発効率 — コンポーネントベースで再利用容易
VB6からの移行最適 — イベント駆動の画面モデルが VB6 に近い
デメリット常時接続必須(SignalR)、オフライン不可
適合度

未来明るく電力に適する理由:

  • VB6 の「ボタン押下 → サーバー処理 → 画面更新」モデルと非常に近い
  • C# 一本で完結 → JavaScript 学習不要
  • サーバーサイドレンダリング → 初期表示が速い
  • コードとUIが近い → VB6コードのロジック移植が容易
  • 社内ツール(常時接続前提)に最適

B. Blazor WebAssembly (WASM)

項目評価
概要.NET アセンブリをブラウザで直接実行
学習コスト — Blazor Server と同じ開発モデル
.NET相性最高
CRUD開発効率
デメリット初期ロード重い(~1.8秒)、.NET ランタイムのダウンロード必要
適合度

未来明るく電力への評価: オフライン動作が必要な場合に選択。未来明るく電力のマスタメンテは常時DB接続が前提のため、Server の方が適合する。Blazor Server + WASM のハイブリッド(Interactive Server + Auto render mode)も .NET 8+ で利用可能。

C. React + ASP.NET Core API

項目評価
概要React フロントエンド + ASP.NET Core バックエンド API
学習コスト — TypeScript, React, 状態管理, ビルドツール全て習得必要
.NET相性 — バックエンドのみ .NET
CRUD開発効率 — API設計 + フロントが二重工数
エコシステム最大 — React のコンポーネント資産は業界最大
適合度低〜中

未来明るく電力への評価: UIの自由度は最高だが、社内マスタメンテ画面に React のリッチなエコシステムは不要。開発工数が Blazor の1.5〜2倍になる。チームが React 経験者で構成される場合のみ検討。

D. .NET MAUI

項目評価
概要クロスプラットフォームデスクトップ/モバイルアプリ
学習コスト — XAML + C#
.NET相性
CRUD開発効率 — DataGrid等は充実(Telerik, Syncfusion 等のサードパーティ)
デメリットWeb化の利点を享受できない(デプロイ配布、ブラウザ不要等)
適合度

未来明るく電力への評価: VB6 のデスクトップ体験を忠実に再現したい場合にのみ検討。ただし「VB6 → 別のデスクトップアプリ」では近代化の恩恵が限定的。Web 化(Blazor)の方が運用・配布のメリットが大きい。

5.3 CRUD 画面の効率的構築

Radzen Blazor(推奨 - コンポーネントライブラリ)

項目評価
概要110+ Blazor コンポーネント + Visual Studio / Blazor Studio 統合
スキャフォールディング強力 — DB接続 → CRUD画面自動生成
コンポーネントDataGrid, DataList, Form, Chart, Scheduler 等
ライセンスコンポーネント: OSS(MIT)、Blazor Studio: 商用($249/月〜)
.NET対応.NET 8 / 9 / 10 対応
DB接続MSSQL, PostgreSQL, MySQL, Oracle, SQLite
適合度

推奨ワークフロー(177テーブル分の画面効率生成):

Step 1

CRUD画面を自動生成

Radzen Blazor Studio でDBスキーマから生成

Step 2

画面をカスタマイズ

自動生成された画面をベースに調整

Step 3

業務ロジックを実装

Service 層に実装

Step 4

認証・認可を追加

Microsoft Identity

自動生成で得られるもの:

  • DataGrid(ソート・フィルター・ページング付き)
  • 新規作成フォーム(バリデーション付き)
  • 編集フォーム
  • 削除確認ダイアログ
  • 検索機能

ABP Framework(大規模な場合の選択肢)

項目評価
概要フルスタックの .NET アプリケーションフレームワーク
機能認証・認可、マルチテナント、監査ログ、バックグラウンドジョブ、DDD支援
学習コスト — フレームワーク全体の理解に時間がかかる
適合度

未来明るく電力への評価: ABP は非常に包括的だが、学習コストが高い。未来明るく電力の3-5人チームでは、Blazor Server + Radzen コンポーネントで十分。ABP の全機能は過剰。

Syncfusion Blazor

項目評価
概要85+ Blazor コンポーネント。エンタープライズ向け
スキャフォールディングVisual Studio 拡張でスキャフォールディング可能
ライセンス商用($999/開発者/年〜)。Community版あり(年商$100万以下は無料)
適合度中〜高

未来明るく電力への評価: Radzen とほぼ同等の選択肢。コンポーネント品質は高いが、ライセンス費用を考慮すると Radzen OSS コンポーネント + Blazor Studio の方がコスパが良い。

5.4 177テーブル分の画面を効率的に作る戦略

全177テーブルにCRUD画面が必要なわけではない。優先度で分類する。

Tier 1 ・ 必須画面(約20テーブル)

マスタメンテ + よく使う照会画面

Radzen Blazor Studio で自動生成→カスタマイズ / 例: SB010(設備マスタ), HN050(単価マスタ), 連携先マスタ, 事業者マスタ

Tier 2 ・ 参照画面(約30テーブル)

読み取り専用のデータ確認

汎用 DataGrid コンポーネント(テーブル名をパラメータ渡し) / 自動生成のままで十分

Tier 3 ・ 管理者専用(約20テーブル)

設定・制御系テーブル

汎用 CRUD 画面(メタデータ駆動)

Tier 4 ・ 不要(約107テーブル)

アプリケーション内部テーブル

画面不要(バッチ処理やAPIでのみアクセス)

// メタデータ駆動の汎用CRUD画面(Tier 2-3 向け)
// テーブル名を渡すだけで CRUD 画面を動的に生成
@page "/admin/table/{TableName}"

<h3>@TableName データ管理</h3>
<RadzenDataGrid Data="@data" TItem="Dictionary<string, object>"
    AllowFiltering="true" AllowSorting="true" AllowPaging="true">
    @foreach (var col in columns)
    {
        <RadzenDataGridColumn Property="@col.Name" Title="@col.DisplayName"
            Type="@col.DataType" />
    }
</RadzenDataGrid>

@code {
    [Parameter] public string TableName { get; set; } = "";

    // メタデータからカラム定義を動的取得
    private List<ColumnMetadata> columns = [];
    private IEnumerable<Dictionary<string, object>> data = [];

    protected override async Task OnParametersSetAsync()
    {
        columns = await _metadataService.GetColumnsAsync(TableName);
        data = await _dataService.GetAllAsync(TableName);
    }
}
csharp

6. アーキテクチャパターン

6.1 候補の比較

A. Clean Architecture

図を読み込み中…

依存方向: 外側 → 内側のみ(Presentation → Application → Domain ← Infrastructure)

項目評価
利点依存方向が明確、テスタビリティ高、長期保守に強い
欠点クラス・ファイル爆発、小さなチームには過剰な抽象化
学習コスト — レイヤー間の責務分離の理解に時間がかかる
3-5人チームへの適合低〜中

正直な評価: Clean Architecture は「正しい」が、596プログラムを3-5人で移行するプロジェクトでは抽象化のオーバーヘッドが大きい。1つの機能追加に4レイヤー分のファイルを作成する必要があり、開発速度が低下する。

B. Vertical Slice Architecture (VSA)

Feature: 契約データ処理
    ProcessOrder.Command.cs
    ProcessOrder.Handler.cs
    ProcessOrder.Validator.cs
    ProcessOrder.Endpoint.cs

Feature: 設備データリセット
    ResetFacility.Command.cs
    ResetFacility.Handler.cs
    ResetFacility.Endpoint.cs

Feature: 設備マスタCRUD
    GetItems.Query.cs
    CreateItem.Command.cs
    UpdateItem.Command.cs
    Items.Endpoint.cs

各機能(Slice)が独立。機能間の依存を最小化。

項目評価
利点機能単位で独立、変更影響が局所化、並行開発しやすい
欠点共通処理の重複リスク、大規模化すると構造が見えにくい
学習コスト — 「1機能1フォルダ」の直感的な構造
3-5人チームへの適合

正直な評価: VSA は「各プログラムが独立した処理」である未来明るく電力の現行構造と親和性が高い。VB6/COBOLの1プログラムが1 Vertical Slice に自然にマッピングされる。ただし、共通ドメインロジック(系統展開、価格計算等)の配置に別途設計が必要。

C. Modular Monolith(推奨)

Power.Web (Host)
    共通認証・認可
    共通ミドルウェア
    モジュール登録

Power.Cis (料金CIS)
    Contracts/
        LowVolt/
        HighVolt/
        Takuso/
    Charging/
    Masters/
    Reports/

Power.Grid (設備管理)
    DemandPlan/
        MonthlyReset/
        GridExpansion/
    WorkManagement/
        DemandResponsePlan/
    Reports/

Power.SharedKernel
    Domain/ (共通Entity, ValueObject)
    Infrastructure/ (DB, EDI共通)
    Common/ (ExeOdbc後継のDB接続層)

モジュール間通信: インプロセスイベント / 共有DB(Phase 1-2)。将来: API / メッセージキュー(Phase 3 で分離可能)。

項目評価
利点サブシステム境界が明確、デプロイは1つ、将来の分離が容易
欠点モジュール境界の設計が重要、間違えると巨大モノリスに回帰
学習コスト — モジュール分割の設計判断が必要
3-5人チームへの適合

6.2 DDD は適切か?

結論: フルDDDは過剰。DDDの一部要素を実用的に採用する。

DDD の要素採用理由
Bounded Context採用料金CIS と 設備管理のサブシステム境界がそのまま Bounded Context になる
Entity / Value Object部分採用マスタテーブルは Entity、供給地点番号等の複合値は Value Object
Aggregate不採用バッチ処理主体でトランザクション整合性の複雑なパターンが少ない
Domain Event将来採用Phase 3 でモジュール分離する際にイベント駆動を導入
Repository Pattern採用EF Core の DbContext をラップして DB アクセスを抽象化
Ubiquitous Language採用テーブル名の暗号性(HN050, KY400)を業務用語に変換する用語辞書
// 実用的なDDD: フルDDDのセレモニーを避け、必要な要素だけ使う

// Value Object: 供給地点番号(VB6の ITMNO1+ITMNO2+TARIFF に対応)
public record PointCode
{
    public string Prefix { get; }   // ITMNO1 (前半5桁)
    public string Suffix { get; }   // ITMNO2 (後半5桁)
    public string TariffCode { get; }  // TARIFF  (料金種別2桁)

    public PointCode(string prefix, string suffix, string tariffCode)
    {
        // バリデーション: 桁数チェック等
        Prefix = prefix;
        Suffix = suffix;
        TariffCode = tariffCode;
    }

    public override string ToString() => $"{Prefix}-{Suffix}-{TariffCode}";
}

// Entity: 設備マスタ (SB010 → わかりやすい名前に)
public class Facility  // ← "SB010Record" ではなく業務用語で
{
    public PointCode Code { get; private set; }
    public string Name { get; private set; }
    public FacilityType Type { get; private set; }  // 製品/中間品/購入品
    public int LeadTimeDays { get; private set; }
    // ... ビジネスロジックをここに集約
}
csharp

6.3 アーキテクチャの推奨

推奨: Modular Monolith + Vertical Slice Architecture(各モジュール内)

理由:

  1. 料金CIS / 設備管理 のサブシステム境界が明確(共有テーブル11件、うち双方向書込みは1件のみ)
  2. 3-5人チームでマイクロサービスは運用負荷が高すぎる
  3. 各モジュール内は VSA で機能単位に整理 → VB6/COBOLプログラムと1:1対応
  4. 将来的にモジュールを分離してマイクロサービス化する道が残る
  5. 1つのデプロイ単位 → CI/CD・運用がシンプル

7. 総合推奨

7.1 技術スタック全体像

UI層

Blazor Server(.NET 10)

+ Radzen Blazor Components(OSS) / + Radzen Blazor Studio(CRUD自動生成)

API層

ASP.NET Core Minimal APIs

バッチ処理

Hangfire(Pro) / Worker Service

ジョブスケジューリング・チェーン実行 / 長時間バッチ処理

ビジネスロジック

Vertical Slice Architecture

Pipeline パターン(EDI変換) / Strategy パターン(連携先別処理) / 系統展開: Low-Level Code 法

データアクセス

Entity Framework Core(ORM)

Dapper(パフォーマンスクリティカルなクエリ)

帳票

QuestPDF / CsvHelper / ClosedXML

PDF生成 / CSV出力 / Excel出力

インフラ

SQL Server or PostgreSQL

認証: Microsoft Identity / ログ: Serilog + Seq or Application Insights / CI/CD: GitHub Actions or Azure DevOps

アーキテクチャ

Modular Monolith

料金CIS / 設備管理 をモジュール分離 / 各モジュール内: Vertical Slice Architecture / DDD要素: Bounded Context, Entity, Value Object, Repository

未来明るく電力 モダンスタック(層別)

7.2 決定根拠のまとめ

領域選定次点不採用理由
バッチ処理Hangfire ProAzure Durable FunctionsAirflow/Dagster は Python 必須で技術スタック分裂
系統展開Low-Level Code 法 (C#自前実装)再帰ツリーフラット化テーブルは参照用に併用
データ変換Pipeline + StrategyCQRS (MediatR)MediatR 商用化のリスク。自前 Pipeline で十分
ETL/ELTETL (アプリ側で変換)部分的ELTELT のスケーラビリティは未来明るく電力規模では不要
帳票QuestPDF + CsvHelperFastReportRDLC はレガシー化。FastReport は帳票デザイナー不要なら過剰
UIBlazor Server + RadzenReact + APIReact は学習コスト高。MAUI はWeb化の恩恵なし
アーキテクチャModular Monolith + VSAClean ArchitectureClean Architecture は3-5人チームにはファイル爆発が負担
DDD実用的部分採用フルDDDAggregate/Domain Event はバッチ主体のシステムには過剰

7.3 学習コストの見積もり

技術習得期間(経験者前提)習得期間(初学者)
C# / .NET 102-3ヶ月
Blazor Server1-2週間1ヶ月
Entity Framework Core1-2週間1ヶ月
Hangfire数日1-2週間
QuestPDF数日1週間
Radzen Components1週間2-3週間
Modular Monolith + VSA2-3週間1-2ヶ月
需要集約アルゴリズム業務知識依存業務知識依存

7.4 コスト見積もり(ライセンス)

ツールライセンス年間コスト(概算)
.NET 10無料 (MIT)$0
Hangfire Pro$500/サーバー/年$500〜$1,000
QuestPDF Professional$699/開発者$2,100〜$3,500 (3-5名)
Radzen Blazor Components無料 (MIT)$0
Radzen Blazor Studio$249/月/開発者$8,964〜$14,940 (3-5名)
Syncfusion (代替案)$999/開発者/年$2,997〜$4,995
合計(推奨構成)$11,564〜$19,440/年

: Radzen Blazor Studio は初期のCRUD自動生成フェーズ(3-6ヶ月)のみ契約し、その後は解約可能。自動生成後のカスタマイズはOSSのコンポーネントだけで行える。その場合の年間コストは$2,600〜$4,500程度。


参考資料

バッチ処理フレームワーク

需要集約 / 系統展開

データ変換 / CQRS

帳票

UI フレームワーク

アーキテクチャ

ワークフローオーケストレーション

このページはお役に立ちましたか?

未来明るく電力

システム資産可視化