導入
モノリシックアーキテクチャにモジュール設計を組み込むアプローチは、マイクロサービスと比較して開発の難易度が低く、コードの再利用性と拡張性に優れています。モジュラー開発の課題として、モジュールのライフサイクル管理やモジュール間の依存関係・通信の設計が挙げられます。ASP.NET Core ABPフレームワークは、これらの課題を解決する強力なモジュラー機能を提供します。本記事では、ABP(バージョン6.x系)とドメイン駆動設計(DDD)を適用した「ワークオーダー(保守・修理チケット)管理モジュール」の設計思想と、ホストアプリケーションへの統合方法について解説します。
基本概念
本システムでは、モジュールを以下の3種類に分類します。
- ABPモジュール: フレームワークが提供する基本的なモジュール単位。
- 共通モジュール: データディクショナリなど、特定の業務に依存しない汎用的な機能を提供するモジュール。
- 業務モジュール: ワークオーダー管理や在庫管理など、具体的なビジネスロジックを実装するモジュール。
ビジネスシナリオ
設備保守業務を例に、ワークオーダーの主要なフローを定義します。
- ユーザーがポータルから障害を報告し、ワークオーダーを作成する。
- 管理者が報告内容を確認し、チケットを承認する。
- 承認済みチケットを特定の技術者に割り当てる、または技術者が自らチケットを取得する。
- 技術者が現場に到着後、チケットのステータスを「作業中」に更新する。
- 作業完了後、ステータスを「完了」にし、作業報告を登録する。
この他にも、ステータスのスキップや巻き戻し、イベント駆動による自動チケット生成(例:定期点測の自動発生)など、様々なエッジケースが存在します。また、チケットの発生元もユーザーからの報告だけでなく、システム内の他のイベント(在庫入库時の検査など)と紐づくケースも想定されます。このような汎用性の高い業務ロジックは、独立した業務モジュールとして切り出すことで、他システムでの再利用が容易になります。
設計目標
再利用性とアップグレードの容易さ
モジュールはNuGetパッケージとして配布されます。ホスト側はパッケージをインストールし、最小限の設定を行うだけで機能を導入できます。将来モジュールが更新された場合も、NuGetパッケージのバージョンを更新するだけでスムーズにアップグレード可能です。
独立性とインターフェース分離
ワークオーダーモジュールは「従業員(技術者)」の概念を必要としますが、モジュール自体は従業員管理機能を提供しません。ホストシステムが独自のユーザー管理を持っている場合、モジュール側が従業員管理を持ってしまうと衝突が発生します。したがって、ホスト側でアダプターを実装し、モジュールが必要とする最小限のインターフェースを提供します。
一部のフレームワークでは、モジュール間の依存を解決するために共通の契約(Contract)層を定義しますが、これには注意が必要です。契約層で過度に汎用的なインターフェースやDTOを定義してしまうと、参照側のモジュールが不要なメソッドやプロパティを引きずってしまう問題が発生します。独立した業務モジュールは、「外部に何を提供するか」だけでなく、「自身は何を必要とするか」を明示的に定義し、依存の方向を制御すべきです。
拡張性
ABPの機能を最大限に活用し、以下の拡張性を確保します。
- 動的プロパティシステムによるエンティティの拡張。
- チケットのCRUDやステータス遷移に伴うドメインイベントの発行。
- ドメインサービスやアプリケーションサービスの置き換えによるロジックのカスタマイズ。
- 抽象化されたエンティティやサービスを継承し、独自のチケットタイプを追加。
DDDの適用
コアとなるビジネスロジックはドメインエンティティにカプセル化します。エンティティの内部メソッドを通じて状態を変更し、必要に応じてドメインイベントをトリガーすることで、エンティティの整合性を保証します。例えば、「完了済みチケットに担当者が未設定である」といった不正な状態を防ぎます。アプリケーションサービスはフロントエンドからのリクエストを受け取り、ドメインエンティティとサービスを協調させてユースケースを実現します。
システム統合の実装
モジュールは抽象化されていますが、デフォルトで「標準ワークオーダー」の実装を提供しています。以下は、ABPホストアプリケーションへの統合手順です。
パッケージの導入
前提として、依存するパッケージのバージョンを適切に更新しておきます。各レイヤーに対応するNuGetパッケージをインストールします。
データベースコンテキストの設定
モジュールは独立したDbContextを使用しないため、ホストのDbContextにエンティティを登録します。
public virtual DbSet<Enterprise.WorkOrder.Maintenance.CategoryEntity> MaintenanceCategories { get; set; }
public virtual DbSet<Enterprise.WorkOrder.Maintenance.TicketEntity> MaintenanceTickets { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyEnterpriseMaintenanceConfigurations();
}
権限とナビゲーションの登録
モジュールが提供する拡張メソッドを使用し、権限とメニューをホストのツリーに追加します。
// 権限プロバイダーでの登録
tenantAdmin.AddEnterpriseMaintenancePermissions();
// ナビゲーションプロバイダーでの登録
navigationContext.Manager.MainMenu.AddEnterpriseMaintenanceNavigation();
デフォルトではルートメニューとサブメニューが登録されますが、拡張メソッドを個別に呼び出すことで、必要な権限やメニューのみを選択的に登録することも可能です。
動的APIとモジュール依存関係
アプリケーションサービスが自動的にWeb APIとして公開されるように設定します。
// 管理画面用APIの登録
Configuration.Modules.AbpAspNetCore().CreateControllersForAppServices(
typeof(Enterprise.WorkOrder.ApplicationModule).Assembly,
"enterpriseMaintenance"
);
// 従業員・共通APIの登録
Configuration.Modules.AbpAspNetCore().CreateControllersForAppServices(
typeof(Enterprise.WorkOrder.StaffApplicationModule).Assembly,
"enterpriseMaintenanceStaff"
);
次に、ホストのApplicationモジュールに依存関係を追加します。
[DependsOn(
typeof(AbpKernelModule),
typeof(Enterprise.WorkOrder.ApplicationModule),
typeof(Enterprise.WorkOrder.StaffApplicationModule)
)]
public class HostApplicationModule : AbpModule
{
// ...
}
アダプターの登録
モジュールが要求する「従業員」インターフェースを、ホストのユーザータイプにマッピングします。
public override void Initialize()
{
IocManager.RegisterEnterpriseMaintenanceStaffAdapter<AppUser>();
}
これにより、モジュール内部で AppUser が従業員として扱われます。また、現在のログインユーザー情報を取得するためのセッションインターフェースも、デフォルトでABPの IAbpSession を使用するように設定されています。
エンドポイントの動作
統合が完了すると、以下のAPIコントローラーが利用可能になります。
- MaintenanceTicket: 管理者がチケットを管理し、ステータスを遷移させるためのAPI。ステータスの遷移はエンティティ内のロジックによって厳密に制御されます。
- MaintenanceCategory: チケットのカテゴリを管理するためのAPI。
- MaintenanceCommon: ステータスリストや優先度リストなど、共通のマスターデータを取得するAPI。
- MaintenanceStaff: 従業員がチケットを取得し、作業ステータスを更新するためのAPI。