第7回:GennethleiaApp - アプリケーションフレームワーク
~ビジネスロジックとプラグイン機構の実装~
はじめに
GennethleiaAppは、アプリケーション層の抽象化とプラグインベースの拡張性を提供します。ビジネスロジックの分離と再利用性に焦点を当てた設計を解説します。
アプリケーション層の抽象化
// アプリケーションコンテキスト
pub struct AppContext {
services: ServiceRegistry,
config: AppConfig,
plugins: PluginManager,
event_bus: EventBus,
}
impl AppContext {
pub fn new(config: AppConfig) -> Self {
Self {
services: ServiceRegistry::new(),
config,
plugins: PluginManager::new(),
event_bus: EventBus::new(),
}
}
pub async fn initialize(&mut self) -> Result<()> {
// サービスの初期化
self.services.initialize().await?;
// プラグインの読み込みと初期化
self.plugins.load_plugins(&self.config.plugin_path).await?;
self.plugins.initialize_all(&self).await?;
// イベントバスの設定
self.setup_event_handlers().await?;
Ok(())
}
}
// サービスレジストリ
pub struct ServiceRegistry {
services: HashMap<ServiceId, Box<dyn Service>>,
dependencies: DependencyGraph,
}
impl ServiceRegistry {
pub async fn initialize(&mut self) -> Result<()> {
// 依存関係に基づいて初期化順序を決定
let init_order = self.dependencies.topological_sort()?;
for service_id in init_order {
let service = self.services.get_mut(&service_id)
.ok_or(Error::ServiceNotFound(service_id))?;
service.initialize().await?;
}
Ok(())
}
}
ビジネスロジックの分離
// ビジネスロジック層
pub trait BusinessLogic: Send + Sync {
type Input;
type Output;
type Error;
fn execute(&self, input: Self::Input) -> Result<Self::Output, Self::Error>;
fn validate(&self, input: &Self::Input) -> Result<(), Self::Error>;
}
// ドメインサービスの実装
pub struct UserService {
repository: Arc<dyn UserRepository>,
validator: Arc<dyn UserValidator>,
event_publisher: Arc<dyn EventPublisher>,
}
impl BusinessLogic for UserService {
type Input = CreateUserCommand;
type Output = User;
type Error = UserError;
fn execute(&self, input: Self::Input) -> Result<Self::Output, Self::Error> {
// 入力の検証
self.validate(&input)?;
// ユーザーの作成
let user = User::new(input.name, input.email);
self.repository.save(&user).await?;
// イベントの発行
self.event_publisher.publish(
UserCreatedEvent::from(&user)
).await?;
Ok(user)
}
fn validate(&self, input: &Self::Input) -> Result<(), Self::Error> {
self.validator.validate_name(&input.name)?;
self.validator.validate_email(&input.email)?;
Ok(())
}
}
// トランザクション管理
pub struct TransactionManager<T: BusinessLogic> {
logic: T,
tx_provider: Arc<dyn TransactionProvider>,
}
impl<T: BusinessLogic> TransactionManager<T> {
pub async fn execute_in_transaction(
&self,
input: T::Input
) -> Result<T::Output, T::Error> {
let tx = self.tx_provider.begin().await?;
match self.logic.execute(input) {
Ok(output) => {
tx.commit().await?;
Ok(output)
}
Err(e) => {
tx.rollback().await?;
Err(e)
}
}
}
}
プラグイン機構
// プラグインシステム
pub trait Plugin: Send + Sync {
fn id(&self) -> PluginId;
fn name(&self) -> &str;
fn version(&self) -> Version;
async fn initialize(&mut self, context: &AppContext) -> Result<()>;
async fn start(&mut self) -> Result<()>;
async fn stop(&mut self) -> Result<()>;
}
pub struct PluginManager {
plugins: HashMap<PluginId, Box<dyn Plugin>>,
loader: PluginLoader,
dependencies: PluginDependencyGraph,
}
impl PluginManager {
pub async fn load_plugins(&mut self, path: &Path) -> Result<()> {
let plugin_files = self.loader.discover_plugins(path).await?;
for file in plugin_files {
let plugin = self.loader.load_plugin(&file).await?;
self.register_plugin(plugin).await?;
}
Ok(())
}
pub async fn register_plugin(&mut self, plugin: Box<dyn Plugin>) -> Result<()> {
let id = plugin.id();
self.validate_dependencies(&plugin)?;
self.plugins.insert(id, plugin);
Ok(())
}
async fn initialize_all(&mut self, context: &AppContext) -> Result<()> {
let init_order = self.dependencies.initialization_order()?;
for plugin_id in init_order {
if let Some(plugin) = self.plugins.get_mut(&plugin_id) {
plugin.initialize(context).await?;
plugin.start().await?;
}
}
Ok(())
}
}
実装例:プラグイン型アプリケーションの実装
// プラグイン型アプリケーションの例
#[derive(Default)]
pub struct DataProcessorPlugin {
config: ProcessorConfig,
processors: Vec<Box<dyn DataProcessor>>,
}
impl Plugin for DataProcessorPlugin {
fn id(&self) -> PluginId {
PluginId::new("data-processor")
}
fn name(&self) -> &str {
"Data Processor Plugin"
}
fn version(&self) -> Version {
Version::new(1, 0, 0)
}
async fn initialize(&mut self, context: &AppContext) -> Result<()> {
// 設定の読み込み
self.config = context.config()
.get_plugin_config(self.id())?
.parse()?;
// プロセッサーの初期化
self.initialize_processors().await?;
Ok(())
}
async fn start(&mut self) -> Result<()> {
for processor in &mut self.processors {
processor.start().await?;
}
Ok(())
}
}
// プラグインの使用例
async fn run_application() -> Result<()> {
let config = AppConfig::load("config.toml")?;
let mut app = AppContext::new(config);
// プラグインの登録
app.plugins.register_plugin(Box::new(DataProcessorPlugin::default())).await?;
// アプリケーションの初期化と起動
app.initialize().await?;
// イベントループ
loop {
app.process_events().await?;
tokio::time::sleep(Duration::from_millis(100)).await;
}
}
今回のまとめ
- アプリケーション層の効果的な抽象化
- ビジネスロジックの明確な分離
- 柔軟なプラグインシステムの実現
- 実用的なプラグイン型アプリケーションの実装
次回予告
第8回では、統合アーキテクチャについて解説します。フレームワーク間の連携や依存関係の管理、ビルドパイプラインの実装について詳しく見ていきます。
参考資料
- Clean Architecture
- Plugin-Based Software Architecture
- Domain-Driven Design
- Event-Driven Architecture Patterns