Claude Code के साथ Designing and Implementing Event-Driven Architecture
Claude Code का उपयोग करके designing and implementing event-driven architecture सीखें। Practical tips और code examples शामिल हैं।
event駆動アーキテクチャ क्या है
event駆動アーキテクチャ(EDA)は、eventの発行と購読 सेシステムを疎結合に設計するpattern है।Claude Code का उपयोग करके、EDAの設計 सेimplementation तकefficiently進められ है।
型safeなeventバス
type EventMap = {
"user.created": { userId: string; email: string };
"user.updated": { userId: string; changes: Record<string, unknown> };
"order.created": { orderId: string; userId: string; total: number };
"order.completed": { orderId: string; completedAt: Date };
"order.cancelled": { orderId: string; reason: string };
"payment.processed": { paymentId: string; amount: number };
};
type EventName = keyof EventMap;
type EventHandler<T extends EventName> = (payload: EventMap[T]) => Promise<void>;
class EventBus {
private handlers = new Map<string, Set<Function>>();
on<T extends EventName>(event: T, handler: EventHandler<T>) {
if (!this.handlers.has(event)) {
this.handlers.set(event, new Set());
}
this.handlers.get(event)!.add(handler);
// unsubscribefunctionを返す
return () => {
this.handlers.get(event)?.delete(handler);
};
}
async emit<T extends EventName>(event: T, payload: EventMap[T]) {
const handlers = this.handlers.get(event);
if (!handlers) return;
const results = await Promise.allSettled(
Array.from(handlers).map((handler) => handler(payload))
);
const failures = results.filter((r) => r.status === "rejected");
if (failures.length > 0) {
console.error(
`${failures.length} handlers failed for ${event}:`,
failures
);
}
}
}
const eventBus = new EventBus();
eventハンドラの登録
// usercreate時のprocessing
eventBus.on("user.created", async ({ userId, email }) => {
// ウェルカムメール送信
await emailQueue.add("send", {
to: email,
subject: " तरहこそ!",
template: "welcome",
data: { userId },
});
});
eventBus.on("user.created", async ({ userId }) => {
// デフォルトsettingsのcreate
await prisma.userSettings.create({
data: {
userId,
theme: "light",
language: "ja",
notifications: true,
},
});
});
// 注文完了時のprocessing
eventBus.on("order.completed", async ({ orderId }) => {
const order = await prisma.order.findUnique({
where: { id: orderId },
include: { user: true, items: true },
});
if (!order) return;
// 在庫のupdate
for (const item of order.items) {
await prisma.product.update({
where: { id: item.productId },
data: { stock: { decrement: item.quantity } },
});
}
});
domaineventpattern
abstract class DomainEvent {
readonly occurredAt: Date;
readonly eventId: string;
constructor() {
this.occurredAt = new Date();
this.eventId = crypto.randomUUID();
}
}
class OrderCreatedEvent extends DomainEvent {
constructor(
public readonly orderId: string,
public readonly userId: string,
public readonly items: Array<{ productId: string; quantity: number }>,
public readonly total: number
) {
super();
}
}
// Aggregate Root
class Order {
private domainEvents: DomainEvent[] = [];
static create(params: {
userId: string;
items: Array<{ productId: string; quantity: number; price: number }>;
}): Order {
const order = new Order();
const orderId = crypto.randomUUID();
const total = params.items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
order.domainEvents.push(
new OrderCreatedEvent(orderId, params.userId, params.items, total)
);
return order;
}
pullDomainEvents(): DomainEvent[] {
const events = [...this.domainEvents];
this.domainEvents = [];
return events;
}
}
CQRS pattern
// Command側(書き込み)
interface Command<T = void> {
execute(): Promise<T>;
}
class CreateOrderCommand implements Command<string> {
constructor(
private userId: string,
private items: Array<{ productId: string; quantity: number }>
) {}
async execute(): Promise<string> {
// validation
for (const item of this.items) {
const product = await prisma.product.findUnique({
where: { id: item.productId },
});
if (!product || product.stock < item.quantity) {
throw new Error(`Insufficient stock: ${item.productId}`);
}
}
// 注文create
const order = await prisma.order.create({
data: {
userId: this.userId,
status: "pending",
items: {
create: this.items.map((item) => ({
productId: item.productId,
quantity: item.quantity,
})),
},
},
});
// event発行
await eventBus.emit("order.created", {
orderId: order.id,
userId: this.userId,
total: 0,
});
return order.id;
}
}
// Query側(読み取り)
interface Query<T> {
execute(): Promise<T>;
}
class GetOrdersQuery implements Query<OrderSummary[]> {
constructor(
private userId: string,
private page: number = 1
) {}
async execute(): Promise<OrderSummary[]> {
// 読み取り専用のビュー सेfetch
return prisma.orderView.findMany({
where: { userId: this.userId },
orderBy: { createdAt: "desc" },
take: 20,
skip: (this.page - 1) * 20,
});
}
}
eventストア
interface StoredEvent {
id: string;
aggregateId: string;
aggregateType: string;
eventType: string;
payload: Record<string, unknown>;
version: number;
occurredAt: Date;
}
class EventStore {
async append(
aggregateId: string,
aggregateType: string,
events: DomainEvent[],
expectedVersion: number
) {
// 楽観的ロック
const currentVersion = await this.getVersion(aggregateId);
if (currentVersion !== expectedVersion) {
throw new Error("Concurrency conflict");
}
const storedEvents = events.map((event, i) => ({
id: event.eventId,
aggregateId,
aggregateType,
eventType: event.constructor.name,
payload: event as any,
version: expectedVersion + i + 1,
occurredAt: event.occurredAt,
}));
await prisma.event.createMany({ data: storedEvents });
// eventを発行
for (const event of events) {
await eventBus.emit(
event.constructor.name as any,
event as any
);
}
}
async getEvents(aggregateId: string): Promise<StoredEvent[]> {
return prisma.event.findMany({
where: { aggregateId },
orderBy: { version: "asc" },
});
}
private async getVersion(aggregateId: string): Promise<number> {
const last = await prisma.event.findFirst({
where: { aggregateId },
orderBy: { version: "desc" },
});
return last?.version ?? 0;
}
}
Claude Code सेのutilization
event駆動アーキテクチャのimplementationをClaude Code को requestする例 है।asyncprocessingके बारे मेंはjobqueue・asyncprocessing、बाहर部integrationはWebhookimplementationpatternもदेखें。
event駆動アーキテクチャをintroductionして。
- 型safeなeventバスのimplementation
- domaineventpattern
- CQRS: commandとqueryの分離
- eventストアのimplementation
- 既存のservice層をrefactoringしてintegration
event駆動設計के details के लिएMartin Fowler - Event-Driven Architectureをदेखें。Claude Codeのuse करने का तरीकाはofficial documentationでconfirmでき है।
Summary
event駆動アーキテクチャはシステムの疎結合性とスケーラビリティを高め है।Claude Code का उपयोग करके、型safeなeventバス सेCQRS、eventソーシング तक、段階的にEDAをintroductionでき है।
Related Posts
Claude Code Hooks में Mastery: Auto-Format, Auto-Test, और बहुत कुछ
Claude Code hooks से auto-formatting और auto-testing setup करना सीखें। Practical configuration examples और real-world use cases शामिल हैं।
Claude Code MCP Server Setup और Practical Use Cases
Claude Code की MCP server capabilities की comprehensive guide। External tools connect करना, servers configure करना, और real-world integration examples सीखें।
CLAUDE.md लिखने की Complete Guide: Project Configuration की Best Practices
Effective CLAUDE.md files लिखने की thorough guide। अपना tech stack, conventions, और project structure communicate करना सीखें और Claude Code की output quality maximize करें।