第8回:統合アーキテクチャ
~フレームワーク間の連携とビルド管理~
はじめに
これまでに解説した各フレームワークを効果的に統合し、一貫したシステムとして機能させる方法を解説します。
フレームワーク間の連携
// フレームワーク統合レイヤー
pub struct IntegratedSystem {
ohama: OhamaFramework,
cusinart: CusinartRuntime,
core: GennethleiaCore,
app: GennethleiaApp,
mediator: SystemMediator,
}
impl IntegratedSystem {
pub async fn new(config: SystemConfig) -> Result<Self> {
// 各フレームワークの初期化
let ohama = OhamaFramework::new(config.ohama_config)?;
let cusinart = CusinartRuntime::new(config.cusinart_config).await?;
let core = GennethleiaCore::new(config.core_config)?;
let app = GennethleiaApp::new(config.app_config)?;
// メディエーターの設定
let mediator = SystemMediator::new(
ohama.event_sender(),
cusinart.event_sender(),
core.event_sender(),
app.event_sender(),
);
Ok(Self {
ohama,
cusinart,
core,
app,
mediator,
})
}
pub async fn start(&mut self) -> Result<()> {
// 起動順序の制御
self.core.initialize().await?;
self.ohama.initialize().await?;
self.cusinart.initialize().await?;
self.app.initialize().await?;
// メディエーターの開始
self.mediator.start().await?;
Ok(())
}
}
// フレームワーク間通信の仲介
pub struct SystemMediator {
event_handlers: HashMap<EventType, Vec<Box<dyn EventHandler>>>,
event_queue: EventQueue,
}
impl SystemMediator {
pub async fn handle_event(&self, event: SystemEvent) -> Result<()> {
let handlers = self.event_handlers.get(&event.event_type())
.ok_or(Error::NoHandlerFound)?;
for handler in handlers {
handler.handle(event.clone()).await?;
}
Ok(())
}
}
依存関係の管理
// 依存関係グラフ
pub struct DependencyGraph {
nodes: HashMap<NodeId, Vec<NodeId>>,
reverse_deps: HashMap<NodeId, Vec<NodeId>>,
}
impl DependencyGraph {
pub fn add_dependency(&mut self, from: NodeId, to: NodeId) -> Result<()> {
// 循環依存のチェック
if self.would_create_cycle(from, to) {
return Err(Error::CyclicDependency);
}
self.nodes.entry(from)
.or_default()
.push(to);
self.reverse_deps.entry(to)
.or_default()
.push(from);
Ok(())
}
pub fn topological_sort(&self) -> Result<Vec<NodeId>> {
let mut result = Vec::new();
let mut visited = HashSet::new();
let mut temp = HashSet::new();
for &node in self.nodes.keys() {
if !visited.contains(&node) {
self.visit(node, &mut visited, &mut temp, &mut result)?;
}
}
Ok(result)
}
}
// ビルド依存関係の管理
pub struct BuildDependencies {
graph: DependencyGraph,
features: HashMap<String, Vec<String>>,
}
impl BuildDependencies {
pub fn resolve_build_order(&self) -> Result<Vec<Package>> {
let order = self.graph.topological_sort()?;
let mut packages = Vec::new();
for node_id in order {
let package = self.resolve_package(node_id)?;
packages.push(package);
}
Ok(packages)
}
fn resolve_package(&self, node_id: NodeId) -> Result<Package> {
let features = self.features.get(&node_id.to_string())
.cloned()
.unwrap_or_default();
Ok(Package {
id: node_id,
features,
})
}
}
ビルドパイプライン
// ビルドシステム
pub struct BuildSystem {
workspace: Workspace,
dependencies: BuildDependencies,
cargo: CargoWrapper,
}
impl BuildSystem {
pub async fn build_all(&self) -> Result<()> {
let packages = self.dependencies.resolve_build_order()?;
for package in packages {
self.build_package(&package).await?;
}
Ok(())
}
async fn build_package(&self, package: &Package) -> Result<()> {
println!("Building package: {}", package.id);
let mut command = self.cargo.new_command();
command
.arg("build")
.arg("--package")
.arg(&package.id.to_string());
// フィーチャーの追加
for feature in &package.features {
command.arg("--feature").arg(feature);
}
command.execute().await?;
Ok(())
}
}
// ワークスペース管理
pub struct Workspace {
root: PathBuf,
packages: HashMap<String, PackageConfig>,
}
impl Workspace {
pub fn load(path: PathBuf) -> Result<Self> {
let manifest = CargoManifest::load(&path.join("Cargo.toml"))?;
let mut packages = HashMap::new();
for member in manifest.workspace_members() {
let config = PackageConfig::load(&path.join(member))?;
packages.insert(member.to_string(), config);
}
Ok(Self {
root: path,
packages,
})
}
}
実装例:マルチプロジェクトの構成
// プロジェクト構成の例
pub struct ProjectStructure {
config: ProjectConfig,
build_system: BuildSystem,
integrated_system: IntegratedSystem,
}
impl ProjectStructure {
pub async fn initialize(config_path: &Path) -> Result<Self> {
let config = ProjectConfig::load(config_path)?;
// ビルドシステムの設定
let workspace = Workspace::load(config.workspace_path.clone())?;
let dependencies = BuildDependencies::new(&config)?;
let build_system = BuildSystem::new(workspace, dependencies);
// 統合システムの初期化
let system_config = SystemConfig::from(&config);
let integrated_system = IntegratedSystem::new(system_config).await?;
Ok(Self {
config,
build_system,
integrated_system,
})
}
pub async fn build_and_run(&mut self) -> Result<()> {
// ビルドの実行
self.build_system.build_all().await?;
// システムの起動
self.integrated_system.start().await?;
Ok(())
}
}
// 使用例
#[tokio::main]
async fn main() -> Result<()> {
let mut project = ProjectStructure::initialize(
Path::new("config.toml")
).await?;
project.build_and_run().await?;
Ok(())
}
プロジェクト構成例
# Cargo.toml
[workspace]
members = [
"ohama",
"cusinart",
"gennethleia-core",
"gennethleia-app",
"examples",
]
[workspace.dependencies]
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
# プロジェクト固有の依存関係
[dependencies]
ohama = { path = "./ohama" }
cusinart = { path = "./cusinart" }
gennethleia-core = { path = "./gennethleia-core" }
gennethleia-app = { path = "./gennethleia-app" }
今回のまとめ
- フレームワーク間の効果的な統合方法
- 依存関係の管理と循環依存の防止
- 効率的なビルドパイプラインの構築
- 実用的なマルチプロジェクト構成
次回予告
第9回では、loquatのインテリジェントシェルの基本設計について解説します。シェル環境の基本機能とコマンド体系の実装について詳しく見ていきます。
参考資料
- Rust Workspaces Documentation
- Building Large-Scale Systems
- Dependency Management Patterns
- Continuous Integration Best Practices