vb-di-patterns — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited vb-di-patterns (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
次の場面で使用する。
整理しないと、Program.vbは管理不能になる。
' BAD: 200行以上の無秩序な登録
Dim builder = WebApplication.CreateBuilder(args)
builder.Services.AddScoped(Of IUserRepository, UserRepository)()
builder.Services.AddScoped(Of IOrderRepository, OrderRepository)()
builder.Services.AddScoped(Of IProductRepository, ProductRepository)()
builder.Services.AddScoped(Of IUserService, UserService)()
' ... さらに150行 ...問題点:関連する登録を見つけにくい、境界が不明瞭、テストで再利用できない、マージコンフリクトが発生しやすい。
関連する登録を拡張メソッドにグループ化する。
' GOOD: クリーンでコンポーザブルなProgram.vb
Dim builder = WebApplication.CreateBuilder(args)
builder.Services _
.AddUserServices() _
.AddOrderServices() _
.AddEmailServices() _
.AddPaymentServices() _
.AddValidators()
Dim app = builder.Build()Imports System.Runtime.CompilerServices
Imports Microsoft.Extensions.DependencyInjection
Namespace MyApp.Users
Public Module UserServiceCollectionExtensions
<Extension>
Public Function AddUserServices(services As IServiceCollection) As IServiceCollection
services.AddScoped(Of IUserRepository, UserRepository)()
services.AddScoped(Of IUserReadStore, UserReadStore)()
services.AddScoped(Of IUserWriteStore, UserWriteStore)()
services.AddScoped(Of IUserService, UserService)()
services.AddScoped(Of IUserValidationService, UserValidationService)()
Return services
End Function
End Module
End NamespaceImports System.Runtime.CompilerServices
Imports Microsoft.Extensions.DependencyInjection
Namespace MyApp.Email
Public Module EmailServiceCollectionExtensions
<Extension>
Public Function AddEmailServices(
services As IServiceCollection,
Optional configSectionName As String = "EmailSettings") As IServiceCollection
' VB.NET における OptionsBuilder の起動時検証
' ---
' 単純な ASP.NET Core / Hosting 環境では、以下の従来チェーンで動作する:
'
' services.AddOptions(Of EmailOptions)() _
' .BindConfiguration(configSectionName) _
' .ValidateDataAnnotations() _
' .ValidateOnStart()
'
' ただし VB.NET + Akka.Hosting 共存時は BC30521(オーバーロード解決失敗)が
' 発生することがある。Microsoft.Extensions.Options と Microsoft.Extensions.Hosting
' の両方が OptionsBuilderExtensions を拡張しており、VB.NET の厳格な
' オーバーロード解決では同名拡張メソッドの衝突と判定されるため。
' C# では発生しない(緩い解決規則のため)。
'
' 回避策として、Microsoft.Extensions.Hosting が提供する
' `AddOptionsWithValidateOnStart(Of T)()` を使えばチェーン末尾の
' `.ValidateOnStart()` を省略できる。こちらは .NET 8 以降で利用可能。
services.AddOptionsWithValidateOnStart(Of EmailOptions)() _
.BindConfiguration(configSectionName) _
.ValidateDataAnnotations()
services.AddSingleton(Of IMjmlTemplateRenderer, MjmlTemplateRenderer)()
services.AddSingleton(Of IEmailLinkGenerator, EmailLinkGenerator)()
services.AddScoped(Of IUserEmailComposer, UserEmailComposer)()
services.AddScoped(Of IEmailSender, SmtpEmailSender)()
Return services
End Function
End Module
End Namespace拡張メソッドは、登録するサービスの近くに配置する。
src/
MyApp.Api/
Program.vb # 全Add*メソッドを組み合わせる
MyApp.Users/
Services/
UserService.vb
UserServiceCollectionExtensions.vb # AddUserServices()
MyApp.Orders/
OrderServiceCollectionExtensions.vb # AddOrderServices()
MyApp.Email/
EmailServiceCollectionExtensions.vb # AddEmailServices()命名規則: {Feature}ServiceCollectionExtensions.vb をそのフィーチャーのサービスと同じ場所に置く。
| パターン | 用途 |
|---|---|
Add{Feature}Services() | 汎用フィーチャー登録 |
Add{Feature}() | 明確な場合の短縮形 |
Configure{Feature}() | 主にオプション設定を行う場合 |
Use{Feature}() | ミドルウェア(IApplicationBuilder用) |
Add* パターンを使うと本番の設定をテストで再利用でき、差異のある部分だけ上書きできる。WebApplicationFactory、Akka.Hosting.TestKit、スタンドアロンのServiceCollectionと組み合わせて使用できる。完全なテスト例についてはadvanced-patterns.mdの該当セクションを参照。
大規模なアプリケーションでは、拡張メソッドを階層的に組み合わせる。
Imports Microsoft.Extensions.DependencyInjection
Imports System.Runtime.CompilerServices
Public Module AppServiceCollectionExtensions
<Extension>
Public Function AddAppServices(services As IServiceCollection) As IServiceCollection
Return services _
.AddDomainServices() _
.AddInfrastructureServices() _
.AddApiServices()
End Function
End Module
Public Module DomainServiceCollectionExtensions
<Extension>
Public Function AddDomainServices(services As IServiceCollection) As IServiceCollection
Return services _
.AddUserServices() _
.AddOrderServices() _
.AddProductServices()
End Function
End Module同じパターンはAkka.NETアクター設定にも使用できる。
Public Module OrderActorExtensions
<Extension>
Public Function AddOrderActors(builder As AkkaConfigurationBuilder) As AkkaConfigurationBuilder
Return builder _
.WithActors(Sub(system, registry, resolver)
Dim orderProps = resolver.Props(Of OrderActor)()
Dim orderRef = system.ActorOf(orderProps, "orders")
registry.Register(Of OrderActor)(orderRef)
End Sub)
End Function
End Module
' Program.vbでの使用例
builder.Services.AddAkka("MySystem", Sub(akkaBuilder, sp)
akkaBuilder _
.AddOrderActors() _
.AddInventoryActors() _
.AddNotificationActors()
End Sub)完全なAkka.Hostingパターンはakka-hosting-actor-patternsスキルを参照。
' BAD: 200行以上の登録コードが並ぶ肥大化したProgram.vb' BAD: 名前が曖昧で何が登録されるか伝わらない
<Extension>
Public Function AddServices(services As IServiceCollection) As IServiceCollection
' ...
End Function' BAD: 設定が埋もれている
<Extension>
Public Function AddDatabase(services As IServiceCollection) As IServiceCollection
services.AddDbContext(Of AppDbContext)(Sub(options)
options.UseSqlServer("hardcoded-connection-string") ' 隠蔽!
End Sub)
Return services
End Function
' GOOD: 設定を明示的に受け取る
<Extension>
Public Function AddDatabase(
services As IServiceCollection,
connectionString As String) As IServiceCollection
services.AddDbContext(Of AppDbContext)(Sub(options)
options.UseSqlServer(connectionString)
End Sub)
Return services
End Function| プラクティス | 効果 |
|---|---|
関連サービスをAdd*メソッドにグループ化する | Program.vbをクリーンに保ち、境界を明確にする |
| 拡張メソッドを登録するサービスの近くに置く | 見つけやすく保守しやすい |
IServiceCollectionを返してチェーン可能にする | FluentなAPI |
| 設定パラメータを受け取る | 柔軟性の確保 |
命名規則を統一する(Add{Feature}Services) | 発見しやすさの向上 |
| テストで本番の拡張メソッドを再利用する | 信頼性向上、重複削減 |
| ライフタイム | 使用場面 | 例 |
|---|---|---|
| シングルトン(Singleton) | ステートレス、スレッドセーフ、生成コストが高い | 設定、HttpClientファクトリ、キャッシュ |
| スコープ(Scoped) | リクエストごとのステート、データベースコンテキスト | DbContext、リポジトリ、ユーザーコンテキスト |
| 一時(Transient) | 軽量、ステートフル、生成コストが低い | バリデータ、短命ヘルパー |
' SINGLETON: ステートレスサービス、安全に共有
services.AddSingleton(Of IMjmlTemplateRenderer, MjmlTemplateRenderer)()
' SCOPED: データベースアクセス、リクエストごとのステート
services.AddScoped(Of IUserRepository, UserRepository)()
' TRANSIENT: 安価で短命
services.AddTransient(Of CreateUserRequestValidator)()スコープサービスはスコープを必要とする。 ASP.NET CoreはHTTPリクエストごとにスコープを作成する。バックグラウンドサービスやアクター内では手動でスコープを作成する必要がある。
アクタースコープ管理パターンはadvanced-patterns.mdを参照。
' BAD: シングルトンがスコープサービスをキャプチャ — 古くなったDbContext!
Public Class CacheService ' シングルトンとして登録
Private ReadOnly _repo As IUserRepository ' スコープ — 起動時にキャプチャされる!
End Class
' GOOD: IServiceProviderを注入し、操作ごとにスコープを作成する
Public Class CacheService
Private ReadOnly _serviceProvider As IServiceProvider
Public Async Function GetUserAsync(id As String) As Task(Of User)
Using scope = _serviceProvider.CreateScope()
Dim repo = scope.ServiceProvider.GetRequiredService(Of IUserRepository)()
Return Await repo.GetByIdAsync(id)
End Using
End Function
End Class' BAD: スコープサービスにスコープなし
Public Class BadBackgroundService
Inherits BackgroundService
Private ReadOnly _orderService As IOrderService ' スコープ — 例外が発生する!
End Class
' GOOD: 処理単位ごとにスコープを作成する
Public Class GoodBackgroundService
Inherits BackgroundService
Private ReadOnly _scopeFactory As IServiceScopeFactory
Protected Overrides Async Function ExecuteAsync(ct As CancellationToken) As Task
Using scope = _scopeFactory.CreateScope()
Dim orderService = scope.ServiceProvider.GetRequiredService(Of IOrderService)()
' ...
End Using
End Function
End Classmicrosoft-extensions-configurationスキルを参照~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.