Excel VBA 逆引き集 | 実務テンプレ完全版(超再利用部品) – 大規模マクロ基盤(Framework)

Excel VBA Excel VBA
スポンサーリンク

ねらい:一発マクロから「育てられるマクロ基盤」へ

大規模マクロ基盤(Framework)のゴールは、こうです。
「その場しのぎの1本マクロ」を卒業して、
機能が増えても壊れにくく、直しやすく、流用しやすい“土台”を先に作ってしまうことです。

ここで言う「大規模」は、行数ではなく「機能数」と「関係者の数」です。
JOINツール、集計ツール、差分ツール、API連携、DB連携…と増えていっても、
同じ型で増築できるようにする——それがFrameworkです。


全体構造テンプレ:マクロ基盤を「層」で考える

3層構造で考える(UI/アプリ/共通基盤)

大規模マクロは、ざっくり次の3層に分けると整理しやすくなります。

UI層(フォームやメニュー、ボタン)
アプリ層(JOIN・集計・差分など“業務処理”)
共通基盤層(ログ、設定読み込み、エラー処理、ユーティリティ)

初心者がやりがちなのは、「ボタンのクリックイベントの中に全部書く」ことです。
そうすると、機能が増えた瞬間に収拾がつかなくなります。

Frameworkでは、「UIは“どの処理を呼ぶか”だけ」「中身はアプリ層」「土台は共通基盤」と役割を分けます。

例:エントリーポイントの型

標準モジュールに「入口専用」のSubを置きます。

' ModEntry.bas
Option Explicit

Public Sub RunMain()
    Const MODULE_NAME As String = "RunMain"
    On Error GoTo ErrHandler
    
    SpeedOn
    LogStart MODULE_NAME, "メイン処理開始"
    
    ShowMainMenu   ' フォームを出す or メイン処理を呼ぶ
    
    LogEnd MODULE_NAME, "メイン処理終了"
    SpeedOff
    Exit Sub
    
ErrHandler:
    SpeedOff
    LogError MODULE_NAME, "MAIN", Err
End Sub
VB

ここでやっていることは、たった3つです。
高速化ラッパ(SpeedOn/Off)、ログ開始・終了、エラー時のLogError。
この「入口の型」を全マクロで共通にしておくと、どこから動かしても挙動が揃います。


共通基盤テンプレ:ログ・エラー・設定・ユーティリティ

ログ出力テンプレ(処理の見える化)

大規模になるほど、「何がいつ動いたか」が分からないと怖くなります。
そこで、処理ログを必ず残す型を作ります。

' ModLog.bas
Option Explicit

Public Sub LogStart(ByVal moduleName As String, ByVal message As String)
    WriteLog "START", moduleName, message
End Sub

Public Sub LogEnd(ByVal moduleName As String, ByVal message As String)
    WriteLog "END", moduleName, message
End Sub

Public Sub LogInfo(ByVal moduleName As String, ByVal message As String)
    WriteLog "INFO", moduleName, message
End Sub

Public Sub LogError(ByVal moduleName As String, ByVal procName As String, ByVal er As ErrObject)
    Dim msg As String
    msg = "Error " & er.Number & " in " & moduleName & "." & procName & ": " & er.Description
    WriteLog "ERROR", moduleName, msg
    MsgBox msg, vbExclamation
End Sub

Private Sub WriteLog(ByVal level As String, ByVal moduleName As String, ByVal message As String)
    Dim ws As Worksheet
    On Error Resume Next
    Set ws = ThisWorkbook.Worksheets("Log")
    On Error GoTo 0
    
    If ws Is Nothing Then
        Set ws = ThisWorkbook.Worksheets.Add
        ws.Name = "Log"
    End If
    
    Dim r As Long
    r = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row + 1
    
    ws.Cells(r, 1).Value = Now
    ws.Cells(r, 2).Value = level
    ws.Cells(r, 3).Value = moduleName
    ws.Cells(r, 4).Value = message
End Sub
VB

ポイントは、「どのモジュールからでも同じLog○○を呼ぶ」ことです。
これだけで、「いつ・どの処理が・どんなメッセージを出したか」が一枚のシートに溜まっていきます。

高速化ラッパテンプレ

大規模マクロでは、ScreenUpdatingや再計算を毎回書くのは面倒なので、共通化します。

' ModSpeed.bas
Option Explicit

Public Sub SpeedOn()
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    Application.Calculation = xlCalculationManual
End Sub

Public Sub SpeedOff()
    Application.Calculation = xlCalculationAutomatic
    Application.EnableEvents = True
    Application.ScreenUpdating = True
End Sub
VB

これを入口Subから必ず呼ぶ——これが「基盤としての約束」です。

設定シート読み込みテンプレ

設定シート方式テンプレと同じ考え方で、「Config」シートから値を読む共通関数を用意します。

' ModConfig.bas
Option Explicit

Public Function GetConfig(ByVal keyName As String) As String
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Worksheets("Config")
    
    Dim rng As Range
    Set rng = ws.Range("A:A").Find(What:=keyName, LookAt:=xlWhole)
    
    If rng Is Nothing Then
        GetConfig = ""
    Else
        GetConfig = CStr(rng.Offset(0, 1).Value)
    End If
End Function
VB

これを使えば、どの処理からでも「GetConfig(“DataFolder”)」のように呼べます。
「設定は全部Configシートに集約する」というルールが、Frameworkの大事な柱になります。


モジュール構成テンプレ:役割ごとにファイルを分ける

モジュールを「役割」で分ける考え方

大規模マクロでは、「標準モジュール1つに全部」は絶対にNGです。
最低限、次のような分け方を意識します。

ModEntry(入口)
ModLog(ログ)
ModSpeed(高速化)
ModConfig(設定)
ModUtil(共通ユーティリティ)
ModJoin(JOIN系処理)
ModAggregate(集計系処理)
ModDiff(差分系処理)
ModApi(API連携)
ModDb(DB連携)

名前は多少違っても構いませんが、「何を担当しているモジュールか」が名前で分かることが重要です。

例:JOIN処理モジュールの型

' ModJoin.bas
Option Explicit

Public Sub RunJoinCustomer()
    Const MODULE_NAME As String = "RunJoinCustomer"
    On Error GoTo ErrHandler
    
    LogStart MODULE_NAME, "顧客JOIN開始"
    
    CleanCustomerSheet
    If Not ValidateCustomerSheet() Then
        LogInfo MODULE_NAME, "顧客シートバリデーションNG"
        Exit Sub
    End If
    
    ExecuteJoinCustomer   ' 実際のJOINロジック
    
    LogEnd MODULE_NAME, "顧客JOIN終了"
    Exit Sub
    
ErrHandler:
    LogError MODULE_NAME, "MAIN", Err
End Sub

Private Sub ExecuteJoinCustomer()
    ' ここに辞書×配列JOINの中身を書く
End Sub
VB

ここで大事なのは、「Publicの入口Sub」と「Privateな実処理Sub」を分けていることです。
外から呼ばれるのは RunJoinCustomer だけ。
中身の実装はいつでも差し替えられる——これが“基盤としての守り方”です。


フォームUIテンプレ:メニューからFrameworkを呼び出す

メインメニューのUserFormを1つ持つ

大規模マクロでは、「どこから何を動かすか」が分かりにくくなりがちです。
そこで、「メインメニュー用のUserForm」を1つ用意して、そこから各処理を呼ぶ形にします。

UserForm名:frmMainMenu
ボタン例:
btnJoinCustomer(顧客JOIN)
btnAggregateSales(売上集計)
btnDiffCustomer(顧客差分)

コードは、極力「入口Subを呼ぶだけ」にします。

' frmMainMenu コード
Option Explicit

Private Sub btnJoinCustomer_Click()
    RunJoinCustomer
End Sub

Private Sub btnAggregateSales_Click()
    RunAggregateSales
End Sub

Private Sub btnDiffCustomer_Click()
    RunDiffCustomer
End Sub
VB

UI層は「どの機能を呼ぶか」だけ。
中身は全部、モジュール側のFrameworkに任せる——この分離がとても大事です。


大規模マクロ基盤で一番大事な考え方

「1本の長いマクロ」を絶対に作らない

大規模になるときの一番の敵は、「1,000行超えの巨大Sub」です。
Frameworkでは、次のルールを徹底します。

入口Subは「ログ・高速化・バリデーション・本処理呼び出し」だけ
本処理Subは「1つの責務」に絞る(JOINならJOINだけ、集計なら集計だけ)
共通処理は必ず別モジュール・別関数に切り出す

これを守るだけで、「読める」「直せる」「流用できる」コードに近づきます。

「名前で役割が分かる」ことを最優先にする

大規模マクロでは、細かいテクニックよりも「名前」が効きます。

RunJoinCustomer
ValidateCustomerSheet
CleanCustomerSheet
LogStart
GetConfig

これらは、コードを見なくても「何をするか」が分かります。
逆に、Sub Test1() や Sub Macro1() のような名前は、Frameworkでは即NGです。

「全部を一気に完璧に」ではなく「少しずつFramework化」

今あるマクロをいきなり全部Frameworkに載せ替える必要はありません。
おすすめは、次の順番です。

入口に SpeedOn/Off と LogStart/End/LogError を入れる
設定をConfigシート+GetConfigに寄せる
JOIN・集計・差分などをモジュールごとに分ける
フォームメニューを1つ作って、入口Subをボタンに割り当てる

これだけでも、「場当たりマクロ」から「基盤の上に乗ったマクロ」に一歩進みます。


まとめ:Frameworkは「未来の自分と他人のための土台」

大規模マクロ基盤(Framework)を一言で言うと、
「未来の自分と、まだ見ぬ同僚のために、先に用意しておく土台」です。

入口の型(Speed+Log+Error)
共通基盤(Log/Config/Util)
役割ごとのモジュール分割
フォームメニューからの呼び出し

この4つを“お約束”として決めてしまえば、
JOINツール、集計ツール、差分ツール、API連携、DB連携…どれを増やしても、
同じリズムでコードを書き足していけます。

タイトルとURLをコピーしました