枯れた言語VB.NETで実現する「拡張性」:動的アセンブリロードによるプラグイン設計の極意
多くの開発者がVB.NETを「レガシー」と呼ぶ。だが、アーキテクトの視点から言えば、それは誤解だ。VB.NETは.NETの完全なパワーを享受できる言語であり、その堅牢な型システムとLINQの強力な抽象化能力は、ビジネスロジックを分離する「プラグイン型アーキテクチャ」において、極めて高い保守性を発揮する。
今日は、メインアプリケーションを一行も書き換えることなく、DLLを配置するだけで機能が拡張される「プロフェッショナルなアドインシステム」の構築法を伝授する。
—
1. なぜ「静的参照」ではいけないのか
業務効率化ツールを開発する際、多くの初心者は参照設定(Reference)で直接DLLを紐付ける。しかし、これでは機能追加のたびに再コンパイルが必要となり、メインアプリのバイナリが肥大化し、依存関係の地獄(DLL Hell)に陥る。
真のプロフェッショナルは、「インターフェース」を定義した共通ライブラリのみを静的に持ち、実装は実行時に動的ロードするという疎結合(Loose Coupling)を選択する。
—
2. アーキテクチャの要:インターフェースの分離
まず、メインアプリとプラグインが共通して参照する「契約(Contract)」ライブラリを作成する。ここには機能の定義のみを記述する。
‘ IPlugin.vb (共通ライブラリ)
Public Interface IPlugin
ReadOnly Property Name As String
Sub Execute()
End Interface
—
3. 「Assembly.LoadFrom」による動的ロードの実装
ここが本題だ。`Assembly.LoadFrom` を使えば、実行時に外部ファイルをメモリにマッピングし、インスタンス化できる。ただし、ここで注意が必要なのは「例外ハンドリング」と「型安全性の担保」である。
Imports System.Reflection
Imports System.IO
Public Class PluginLoader
”’
”’
Public Sub LoadAndRunPlugins(folderPath As String)
If Not Directory.Exists(folderPath) Then Exit Sub
For Each dllPath As String In Directory.GetFiles(folderPath, “.dll”)
Try
‘ アセンブリをロード
Dim asm As Assembly = Assembly.LoadFrom(dllPath)
‘ インターフェースを実装している型を探す
For Each t As Type In asm.GetExportedTypes()
If t.GetInterface(GetType(IPlugin).FullName) IsNot Nothing Then
‘ インスタンス生成
Dim plugin As IPlugin = DirectCast(Activator.CreateInstance(t), IPlugin)
‘ 実行
Console.WriteLine($”実行中: {plugin.Name}”)
plugin.Execute()
End If
Next
Catch ex As Exception
‘ 本番環境ではここでログを吐き、個別のプラグインエラーが全体を停止させないようにする
Debug.WriteLine($”プラグインロード失敗: {dllPath} – {ex.Message}”)
End Try
Next
End Sub
End Class
—
4. 堅牢な設計のために:プロフェッショナルが守る3つの鉄則
動的ロードを扱う際、以下の3点に妥協してはならない。
① アセンブリのアンロードを考慮する
`Assembly.LoadFrom` でロードしたDLLは、デフォルトのロードコンテキストに居座り続ける。頻繁に読み書きが発生する場合は、`AssemblyLoadContext`(.NET Core/.NET 5+以降)を使用し、不要になったメモリを解放する設計が必須だ。
② ファイルロックの回避
DLLが使用中であると更新ができない。開発中、頻繁にDLLを差し替える必要があるなら、`Assembly.Load(File.ReadAllBytes(path))` を使い、一度メモリにロードしてから反射させる方法を検討せよ。これによりファイルハンドルが解放される。
③ データベース連携時のトランザクション境界
プラグイン内でDB操作を行う場合、メインアプリ側の接続を渡すのか、プラグイン側で完結させるのかを明確に定義せよ。「プラグインごとにDBコネクション文字列を読み込ませる」設計にしておけば、メインアプリに依存しない独立したテストが可能になる。
—
最後に:コードは「資産」ではなく「負債」になり得る
私がVB.NETを好む理由は、その冗長な構文が「何をしているか」を明示的にし、後任者が修正を加える際の認知負荷を下げてくれるからだ。
今回紹介したプラグイン機構を導入すれば、現場のエンジニアは「メインアプリの開発者に頼まずに、自分たちで機能追加できる」ようになる。これこそが、業務自動化ツールにおける「スケールする設計」である。
迷わず実装せよ。ただし、インターフェースの契約だけは、魂を込めて設計することだ。
