blueprinteventbus-integration
BlueprintEventBusを用いて、モジュール間の連携を疎結合にし、ドメインイベントを通知・購読することで、イベント駆動型の通信を実装し、適切なライフサイクル管理と自動クリーンアップを行うSkill。
📜 元の英語説明(参考)
Implement event-driven communication using BlueprintEventBus for cross-module coordination. Use this skill when modules need to communicate without tight coupling, broadcasting domain events (task.created, member.added), subscribing to events with proper lifecycle management, and implementing event-driven workflows. Ensures events follow naming conventions ([module].[action]), include Blueprint context, and use takeUntilDestroyed() for automatic cleanup.
🇯🇵 日本人クリエイター向け解説
BlueprintEventBusを用いて、モジュール間の連携を疎結合にし、ドメインイベントを通知・購読することで、イベント駆動型の通信を実装し、適切なライフサイクル管理と自動クリーンアップを行うSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o blueprinteventbus-integration.zip https://jpskill.com/download/16773.zip && unzip -o blueprinteventbus-integration.zip && rm blueprinteventbus-integration.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/16773.zip -OutFile "$d\blueprinteventbus-integration.zip"; Expand-Archive "$d\blueprinteventbus-integration.zip" -DestinationPath $d -Force; ri "$d\blueprinteventbus-integration.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
blueprinteventbus-integration.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
blueprinteventbus-integrationフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
[Skill 名] blueprinteventbus-integration
BlueprintEventBus 統合 Skill
この Skill は、BlueprintEventBus を使用したイベント駆動型アーキテクチャの実装を支援します。
中核となる原則
イベント駆動型アーキテクチャ
- 疎結合: モジュールは直接呼び出しではなく、イベントを介して通信します。
- Blueprint コンテキスト: すべてのイベントに Blueprint 情報が含まれます。
- 型安全性: イベントは TypeScript で厳密に型付けされています。
- ライフサイクル管理: 自動的なサブスクリプションのクリーンアップ。
EventBus を使用するべき時
✅ EventBus を使用すべき場合:
- モジュール間の通信
- 複数のリスナーへの状態変更のブロードキャスト
- 監査ログとアクティビティ追跡
- 通知トリガー
- ワークフローオーケストレーション
❌ EventBus を使用すべきでない場合:
- 単純な親子コンポーネント間の通信 (@Output を使用)
- 同じモジュール内の直接的なサービス間呼び出し
- 同期的なデータフェッチ
- リクエスト-レスポンスパターン (Services を使用)
イベント構造
基本イベントインターフェース
interface BlueprintEvent<T = any> {
type: string; // Format: [module].[action]
blueprintId: string;
timestamp: Date;
actor: string; // イベントをトリガーしたユーザー ID
data: T;
metadata?: Record<string, any>;
}
イベント命名規則
[module].[action]
例:
task.createdtask.updatedtask.deletedtask.assignedmember.addedmember.removedfile.uploadedblueprint.archived
イベントの発行
基本的なイベント発行
import { inject } from '@angular/core';
import { BlueprintEventBus } from '@core/services/blueprint-event-bus.service';
@Injectable({ providedIn: 'root' })
export class TaskService {
private eventBus = inject(BlueprintEventBus);
private taskRepository = inject(TaskRepository);
async createTask(blueprintId: string, task: CreateTaskDto): Promise<Task> {
// 1. ビジネスロジックを実行
const created = await this.taskRepository.create(blueprintId, task);
// 2. ドメインイベントを発行
this.eventBus.publish({
type: 'task.created',
blueprintId,
timestamp: new Date(),
actor: this.getCurrentUserId(),
data: created
});
return created;
}
async updateTask(taskId: string, updates: Partial<Task>): Promise<Task> {
const task = await this.taskRepository.findById(taskId);
if (!task) throw new Error('Task not found');
const updated = await this.taskRepository.update(taskId, updates);
// before/after データとともに更新イベントを発行
this.eventBus.publish({
type: 'task.updated',
blueprintId: task.blueprintId,
timestamp: new Date(),
actor: this.getCurrentUserId(),
data: updated,
metadata: {
before: task,
changes: updates
}
});
return updated;
}
}
型付きイベント発行
// イベント型を定義
interface TaskCreatedEvent extends BlueprintEvent<Task> {
type: 'task.created';
}
interface TaskAssignedEvent extends BlueprintEvent<{
task: Task;
assignee: string;
assigneeType: 'user' | 'team' | 'partner';
}> {
type: 'task.assigned';
}
// 型安全性のある発行
this.eventBus.publish<TaskCreatedEvent>({
type: 'task.created',
blueprintId: created.blueprintId,
timestamp: new Date(),
actor: this.getCurrentUserId(),
data: created
});
イベントのサブスクライブ
基本的なサブスクリプション
import { Component, inject, signal, DestroyRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { BlueprintEventBus } from '@core/services/blueprint-event-bus.service';
@Component({
selector: 'app-task-list',
template: `...`
})
export class TaskListComponent {
private eventBus = inject(BlueprintEventBus);
private destroyRef = inject(DestroyRef);
tasks = signal<Task[]>([]);
ngOnInit(): void {
// task.created イベントをサブスクライブ
this.eventBus.subscribe('task.created')
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(event => {
console.log('New task created:', event.data);
this.tasks.update(tasks => [...tasks, event.data]);
});
}
}
フィルタリングされたサブスクリプション (Blueprint 固有)
import { filter } from 'rxjs/operators';
@Component({
selector: 'app-blueprint-tasks',
template: `...`
})
export class BlueprintTasksComponent {
private eventBus = inject(BlueprintEventBus);
private destroyRef = inject(DestroyRef);
blueprintId = input.required<string>();
tasks = signal<Task[]>([]);
ngOnInit(): void {
// 現在の Blueprint のイベントのみをリッスン
this.eventBus.subscribe('task.created')
.pipe(
filter(event => event.blueprintId === this.blueprintId()),
takeUntilDestroyed(this.destroyRef)
)
.subscribe(event => {
this.tasks.update(tasks => [...tasks, event.data]);
});
// 更新をリッスン
this.eventBus.subscribe('task.updated')
.pipe(
filter(event => event.blueprintId === this.blueprintId()),
takeUntilDestroyed(this.destroyRef)
)
.subscribe(event => {
this.tasks.update(tasks =>
tasks.map(t => t.id === event.data.id ? event.data : t)
);
});
// 削除をリッスン
this.eventBus.subscribe('task.deleted')
.pipe(
filter(event => event.blueprintId === this.blueprintId()),
takeUntilDestroyed(this.destroyRef)
)
.subscribe(event => {
this.tasks.update(tasks =>
tasks.filter(t => t.id !== event.data.id)
);
});
}
}
複数のイベント型
import { merge } from 'rxjs';
ngOnInit(): void {
// 複数のイベント型をリッスン
merge(
this.eventBus.subscribe('task.created'),
this.eventBus.subscribe('task.updated'),
this.eventBus.subscribe('task.deleted')
)
.pipe(
filter(event => event.blueprintId === this.blueprintId()),
takeUntilDestroyed(this.destroyRef)
)
.subscribe(event => {
console.log('Task event:', event.type, eve 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
BlueprintEventBus Integration Skill
This skill helps implement event-driven architecture using BlueprintEventBus.
Core Principles
Event-Driven Architecture
- Decoupling: Modules communicate via events, not direct calls
- Blueprint Context: All events include Blueprint information
- Type Safety: Events are strongly typed with TypeScript
- Lifecycle Management: Automatic subscription cleanup
When to Use EventBus
✅ DO use EventBus for:
- Cross-module communication
- Broadcasting state changes to multiple listeners
- Audit logging and activity tracking
- Notification triggers
- Workflow orchestration
❌ DON'T use EventBus for:
- Simple parent-child component communication (use @Output)
- Direct service-to-service calls within same module
- Synchronous data fetching
- Request-response patterns (use Services)
Event Structure
Base Event Interface
interface BlueprintEvent<T = any> {
type: string; // Format: [module].[action]
blueprintId: string;
timestamp: Date;
actor: string; // User ID who triggered event
data: T;
metadata?: Record<string, any>;
}
Event Naming Convention
[module].[action]
Examples:
task.createdtask.updatedtask.deletedtask.assignedmember.addedmember.removedfile.uploadedblueprint.archived
Publishing Events
Basic Event Publishing
import { inject } from '@angular/core';
import { BlueprintEventBus } from '@core/services/blueprint-event-bus.service';
@Injectable({ providedIn: 'root' })
export class TaskService {
private eventBus = inject(BlueprintEventBus);
private taskRepository = inject(TaskRepository);
async createTask(blueprintId: string, task: CreateTaskDto): Promise<Task> {
// 1. Execute business logic
const created = await this.taskRepository.create(blueprintId, task);
// 2. Publish domain event
this.eventBus.publish({
type: 'task.created',
blueprintId,
timestamp: new Date(),
actor: this.getCurrentUserId(),
data: created
});
return created;
}
async updateTask(taskId: string, updates: Partial<Task>): Promise<Task> {
const task = await this.taskRepository.findById(taskId);
if (!task) throw new Error('Task not found');
const updated = await this.taskRepository.update(taskId, updates);
// Publish update event with before/after data
this.eventBus.publish({
type: 'task.updated',
blueprintId: task.blueprintId,
timestamp: new Date(),
actor: this.getCurrentUserId(),
data: updated,
metadata: {
before: task,
changes: updates
}
});
return updated;
}
}
Typed Event Publishing
// Define event types
interface TaskCreatedEvent extends BlueprintEvent<Task> {
type: 'task.created';
}
interface TaskAssignedEvent extends BlueprintEvent<{
task: Task;
assignee: string;
assigneeType: 'user' | 'team' | 'partner';
}> {
type: 'task.assigned';
}
// Publish with type safety
this.eventBus.publish<TaskCreatedEvent>({
type: 'task.created',
blueprintId: created.blueprintId,
timestamp: new Date(),
actor: this.getCurrentUserId(),
data: created
});
Subscribing to Events
Basic Subscription
import { Component, inject, signal, DestroyRef } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { BlueprintEventBus } from '@core/services/blueprint-event-bus.service';
@Component({
selector: 'app-task-list',
template: `...`
})
export class TaskListComponent {
private eventBus = inject(BlueprintEventBus);
private destroyRef = inject(DestroyRef);
tasks = signal<Task[]>([]);
ngOnInit(): void {
// Subscribe to task.created events
this.eventBus.subscribe('task.created')
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(event => {
console.log('New task created:', event.data);
this.tasks.update(tasks => [...tasks, event.data]);
});
}
}
Filtered Subscription (Blueprint-specific)
import { filter } from 'rxjs/operators';
@Component({
selector: 'app-blueprint-tasks',
template: `...`
})
export class BlueprintTasksComponent {
private eventBus = inject(BlueprintEventBus);
private destroyRef = inject(DestroyRef);
blueprintId = input.required<string>();
tasks = signal<Task[]>([]);
ngOnInit(): void {
// Only listen to events in current Blueprint
this.eventBus.subscribe('task.created')
.pipe(
filter(event => event.blueprintId === this.blueprintId()),
takeUntilDestroyed(this.destroyRef)
)
.subscribe(event => {
this.tasks.update(tasks => [...tasks, event.data]);
});
// Listen to updates
this.eventBus.subscribe('task.updated')
.pipe(
filter(event => event.blueprintId === this.blueprintId()),
takeUntilDestroyed(this.destroyRef)
)
.subscribe(event => {
this.tasks.update(tasks =>
tasks.map(t => t.id === event.data.id ? event.data : t)
);
});
// Listen to deletions
this.eventBus.subscribe('task.deleted')
.pipe(
filter(event => event.blueprintId === this.blueprintId()),
takeUntilDestroyed(this.destroyRef)
)
.subscribe(event => {
this.tasks.update(tasks =>
tasks.filter(t => t.id !== event.data.id)
);
});
}
}
Multiple Event Types
import { merge } from 'rxjs';
ngOnInit(): void {
// Listen to multiple event types
merge(
this.eventBus.subscribe('task.created'),
this.eventBus.subscribe('task.updated'),
this.eventBus.subscribe('task.deleted')
)
.pipe(
filter(event => event.blueprintId === this.blueprintId()),
takeUntilDestroyed(this.destroyRef)
)
.subscribe(event => {
console.log('Task event:', event.type, event.data);
this.refreshTasks();
});
}
Common Use Cases
1. Audit Logging
@Injectable({ providedIn: 'root' })
export class AuditLogService {
private eventBus = inject(BlueprintEventBus);
private auditLogRepository = inject(AuditLogRepository);
constructor() {
// Listen to ALL events for audit trail
this.eventBus.subscribeAll()
.pipe(takeUntilDestroyed())
.subscribe(event => {
this.logEvent(event);
});
}
private async logEvent(event: BlueprintEvent): Promise<void> {
await this.auditLogRepository.create({
eventType: event.type,
blueprintId: event.blueprintId,
actor: event.actor,
timestamp: event.timestamp,
data: event.data,
metadata: event.metadata
});
}
}
2. Notification System
@Injectable({ providedIn: 'root' })
export class NotificationService {
private eventBus = inject(BlueprintEventBus);
private notificationRepository = inject(NotificationRepository);
constructor() {
// Listen to events that trigger notifications
this.setupNotificationListeners();
}
private setupNotificationListeners(): void {
// Task assigned → notify assignee
this.eventBus.subscribe('task.assigned')
.pipe(takeUntilDestroyed())
.subscribe(async event => {
await this.notifyUser(event.data.assignee, {
title: 'New Task Assigned',
message: `You have been assigned: ${event.data.task.title}`,
blueprintId: event.blueprintId
});
});
// Member added → notify member
this.eventBus.subscribe('member.added')
.pipe(takeUntilDestroyed())
.subscribe(async event => {
await this.notifyUser(event.data.userId, {
title: 'Added to Blueprint',
message: `You have been added to ${event.data.blueprintName}`,
blueprintId: event.blueprintId
});
});
}
}
3. Real-time UI Updates
@Component({
selector: 'app-activity-feed',
template: `
<div class="activity-feed">
@for (activity of activities(); track activity.id) {
<div class="activity-item">
<span class="timestamp">{{ activity.timestamp | date }}</span>
<span class="message">{{ activity.message }}</span>
</div>
}
</div>
`
})
export class ActivityFeedComponent {
private eventBus = inject(BlueprintEventBus);
private destroyRef = inject(DestroyRef);
blueprintId = input.required<string>();
activities = signal<Activity[]>([]);
ngOnInit(): void {
// Listen to all events in Blueprint
this.eventBus.subscribeAll()
.pipe(
filter(event => event.blueprintId === this.blueprintId()),
takeUntilDestroyed(this.destroyRef)
)
.subscribe(event => {
this.addActivity({
id: crypto.randomUUID(),
type: event.type,
message: this.formatEventMessage(event),
timestamp: event.timestamp
});
});
}
private formatEventMessage(event: BlueprintEvent): string {
switch (event.type) {
case 'task.created':
return `Task "${event.data.title}" was created`;
case 'task.assigned':
return `Task assigned to ${event.data.assignee}`;
case 'member.added':
return `${event.data.userName} joined the Blueprint`;
default:
return `Event: ${event.type}`;
}
}
private addActivity(activity: Activity): void {
this.activities.update(activities => [activity, ...activities].slice(0, 50));
}
}
4. Workflow Orchestration
@Injectable({ providedIn: 'root' })
export class TaskWorkflowService {
private eventBus = inject(BlueprintEventBus);
private taskRepository = inject(TaskRepository);
private notificationService = inject(NotificationService);
constructor() {
this.setupWorkflows();
}
private setupWorkflows(): void {
// When task is completed → trigger follow-up actions
this.eventBus.subscribe('task.completed')
.pipe(takeUntilDestroyed())
.subscribe(async event => {
const task = event.data;
// 1. Check if task has dependencies
const dependentTasks = await this.taskRepository
.findDependentTasks(task.id);
// 2. Update dependent tasks
for (const depTask of dependentTasks) {
await this.taskRepository.update(depTask.id, {
status: 'ready'
});
}
// 3. Notify stakeholders
await this.notificationService.notifyTaskCompletion(task);
});
}
}
Event Bus Service Implementation
Reference implementation:
import { Injectable } from '@angular/core';
import { Subject, Observable } from 'rxjs';
import { filter } from 'rxjs/operators';
export interface BlueprintEvent<T = any> {
type: string;
blueprintId: string;
timestamp: Date;
actor: string;
data: T;
metadata?: Record<string, any>;
}
@Injectable({ providedIn: 'root' })
export class BlueprintEventBus {
private eventStream = new Subject<BlueprintEvent>();
/**
* Publish event to all subscribers
*/
publish<T = any>(event: BlueprintEvent<T>): void {
this.eventStream.next(event);
}
/**
* Subscribe to specific event type
*/
subscribe<T = any>(eventType: string): Observable<BlueprintEvent<T>> {
return this.eventStream.asObservable().pipe(
filter(event => event.type === eventType)
);
}
/**
* Subscribe to all events
*/
subscribeAll(): Observable<BlueprintEvent> {
return this.eventStream.asObservable();
}
/**
* Subscribe to events in specific Blueprint
*/
subscribeToBlueprintEvents(blueprintId: string): Observable<BlueprintEvent> {
return this.eventStream.asObservable().pipe(
filter(event => event.blueprintId === blueprintId)
);
}
}
Testing EventBus Integration
describe('EventBus Integration', () => {
let eventBus: BlueprintEventBus;
let taskService: TaskService;
beforeEach(() => {
eventBus = TestBed.inject(BlueprintEventBus);
taskService = TestBed.inject(TaskService);
});
it('should publish event when task is created', (done) => {
// Subscribe to event
eventBus.subscribe('task.created').subscribe(event => {
expect(event.type).toBe('task.created');
expect(event.data.title).toBe('Test Task');
done();
});
// Create task (triggers event)
taskService.createTask('blueprint1', { title: 'Test Task' });
});
it('should filter events by Blueprint', (done) => {
let receivedEvents = 0;
eventBus.subscribeAll()
.pipe(filter(event => event.blueprintId === 'blueprint1'))
.subscribe(() => {
receivedEvents++;
});
// Publish events to different Blueprints
eventBus.publish({
type: 'task.created',
blueprintId: 'blueprint1',
timestamp: new Date(),
actor: 'user1',
data: {}
});
eventBus.publish({
type: 'task.created',
blueprintId: 'blueprint2',
timestamp: new Date(),
actor: 'user1',
data: {}
});
setTimeout(() => {
expect(receivedEvents).toBe(1);
done();
}, 100);
});
});
Checklist
When integrating EventBus:
- [ ] Events follow naming convention ([module].[action])
- [ ] Events include blueprintId
- [ ] Events include timestamp and actor
- [ ] Subscriptions use takeUntilDestroyed()
- [ ] Filter events by Blueprint when needed
- [ ] Use typed events for type safety
- [ ] Publish events AFTER successful operations
- [ ] Don't use EventBus for request-response
- [ ] Test event publishing and subscription
- [ ] Document event types and data structures
References
- Architecture Guide
- Blueprint Integration
- [Global Event Bus Documentation](docs/⭐️/Global Event Bus.md)