標準モジュールに処理を書きすぎて、気づけば1つのファイルが3000行を超えていた——そんな反省から、ThisWorkbookとクラスモジュールへの責務分割を見直すことになりました。役割ごとに置き場所を決めておくと、半年後の自分が読み返すときの負担がまるで違います。

モジュール種別の一覧

VBAモジュール設計図

図1: VBAモジュール種別の役割分担

種別VBEでの表示主な用途
標準モジュールModule1 など汎用プロシージャ・ユーティリティ関数
ThisWorkbookThisWorkbookブック全体のイベント(Open・BeforeClose等)
シートモジュールSheet1 などシート固有のイベント(Change・SelectionChange等)
クラスモジュールClass1 などオブジェクト指向的な設計・カプセル化
UserFormUserForm1 などダイアログUI・入力フォーム

標準モジュール

最も一般的なモジュールです。Public Sub / Public Function で宣言するとプロジェクト全体から呼び出せます。

VBA — 標準モジュール(modUtils)
Option Explicit

' ── modUtils.bas ──────────────────────────────
' 役割:汎用ユーティリティ関数を集約する

' ファイルが存在するか確認
Public Function FileExists(ByVal path As String) As Boolean
    FileExists = (Dir(path) <> "")
End Function

' 文字列をゼロパディング
Public Function ZeroPad(ByVal num As Long, ByVal digits As Integer) As String
    ZeroPad = Right(String(digits, "0") & CStr(num), digits)
End Function

💡 命名規則の推奨

標準モジュールには mod プレフィックスをつけて役割を明確にするのが一般的です(例: modUtilsmodFileIOmodConfig)。

ThisWorkbook モジュール

ブック全体に関わるイベントを記述します。Workbook_OpenWorkbook_BeforeClose など、自動実行が必要な処理の起点になります。

VBA — ThisWorkbook
Option Explicit

' ── ThisWorkbook ──────────────────────────────
' ブックを開いたとき
Private Sub Workbook_Open()
    modSetup.InitializeApp   ' 初期化処理を標準モジュールに委譲
End Sub

' ブックを閉じる前
Private Sub Workbook_BeforeClose(Cancel As Boolean)
    modSetup.CleanupApp      ' クリーンアップを標準モジュールに委譲
End Sub

ThisWorkbook は薄く保つ

イベントの「入口」として機能させ、実際の処理は標準モジュールに委譲するのがベストプラクティスです。ThisWorkbook にロジックを詰め込むと他からテストしにくくなります。

シートモジュール

各シートオブジェクトに紐づくモジュールです。シート固有のイベントを実装します。

VBA — シートモジュール(Sheet1)
Option Explicit

' ── Sheet1(データ入力シート)────────────────
' セル変更時にバリデーション
Private Sub Worksheet_Change(ByVal Target As Range)
    If Target.Column = 3 Then   ' C列が変更された場合
        If Not IsNumeric(Target.Value) Then
            MsgBox "数値を入力してください", vbExclamation
            Application.Undo
        End If
    End If
End Sub

クラスモジュール(概要)

オブジェクト指向的な設計を実現します。詳細は PART 07 で解説しますが、ここでは概念だけ押さえておきましょう。

VBA — クラスモジュール(clsProduct)概要
' ── clsProduct.cls ──────────────────────────
Option Explicit

Private m_Name  As String
Private m_Price As Long

Public Property Get Name() As String:  Name = m_Name:  End Property
Public Property Let Name(v As String): m_Name = v:     End Property
Public Property Get Price() As Long:   Price = m_Price: End Property
Public Property Let Price(v As Long):  m_Price = v:     End Property

' ── 呼び出し側 ──
Sub UseProduct()
    Dim p As New clsProduct
    p.Name  = "テスト商品"
    p.Price = 1000
    MsgBox p.Name & " : ¥" & p.Price
End Sub

UserForm モジュール

UIとロジックを分離するのが重要です。フォームモジュールにはUI操作だけを記述し、ビジネスロジックは標準モジュールへ委譲します。

VBA — UserForm(薄く保つ例)
Option Explicit

' ── frmInput ──────────────────────────────────
Private Sub btnOK_Click()
    ' フォームはデータ収集のみ、処理は標準モジュールに委譲
    Dim inputVal As String
    inputVal = Me.txtValue.Text

    If inputVal = "" Then
        MsgBox "入力してください", vbExclamation
        Exit Sub
    End If

    modProcess.ExecuteWithInput inputVal
    Me.Hide
End Sub

Private Sub btnCancel_Click()
    Me.Hide
End Sub

責務分担の設計指針

何を書くかどこに書くか
汎用関数・共通処理標準モジュール(modUtils など)
定数・設定値標準モジュール(modConst など)
ブックの起動・終了処理ThisWorkbook(処理本体は標準モジュールへ委譲)
シート固有の入力チェックシートモジュール
データ構造・ビジネスオブジェクトクラスモジュール
UI操作UserForm(ロジックは標準モジュールへ委譲)

まとめ

モジュールを適切に分割すると、「どこに何が書いてあるか」が自然に分かり、デバッグや機能追加のコストが大幅に下がります。特に ThisWorkbook や UserForm は薄く保ち、ロジックは標準モジュールへ という原則が重要です。

次の章では…

PART 04 では CreateObjectによる別プロセス起動 を解説します。WorkBook.Open 時の画面ちらつきを根本解決する方法を紹介します。

→ PART 04 — 別プロセス起動・ちらつき解消へ

VBAモジュール設計でよくある誤り

モジュール分割を誤るとコードが肥大化し、保守や再利用が困難になります。設計段階でよくある誤りを確認しておきましょう。

誤り/失敗パターン何が起きるか正しい対処/防止策
全処理をThisWorkbookや1つの標準モジュールに書くコードが数千行に膨れ上がり、どこに何があるか分からなくなる機能・目的ごとにモジュールを分割し、ThisWorkbookはイベント処理のみ、ロジックは標準モジュールに分ける
グローバル変数を多用して状態管理する変数の変更箇所が特定できず、意図しない値変更でバグが混入しやすくなるグローバル変数は最小限に留め、モジュール内変数(Private)や引数・戻り値でデータを受け渡す
同じ処理をコピー&ペーストで複数箇所に書く修正が必要になった際に全箇所を修正し忘れ、バグが残る共通処理はサブプロシージャや関数として切り出し、呼び出し側からCall/参照するように設計する