ワンクリックで
domain-modeling
DDD practical guide: Event Storming, Bounded Contexts, Aggregates and domain-driven design patterns
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
DDD practical guide: Event Storming, Bounded Contexts, Aggregates and domain-driven design patterns
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
**Architecture Decision Record (ADR) — Philosophy-First**: Creates well-structured ADRs using a 3-phase process: (1) Define the architectural philosophy and identity of the system, (2) Explore options through the lens of that philosophy, (3) Adversarial review by a devil's advocate agent. Use this skill whenever the user wants to document a technical decision, create an ADR, record why a technology or approach was chosen, compare architectural alternatives, or mentions 'ADR', 'architecture decision', 'decision record', 'technical decision', 'why did we choose', or 'document this decision'. Also trigger when the user is evaluating trade-offs between technical approaches and wants to formalize the reasoning.
**AI/ML Engineering Review**: Reviews AI/ML systems for production readiness — model serving, MLOps pipelines, LLM integration patterns, prompt engineering, evaluation frameworks, and responsible AI. Covers model deployment, feature stores, experiment tracking, monitoring/drift detection, and AI safety. Use when the user mentions ML, AI, machine learning, model, LLM, GPT, Claude, embeddings, RAG, fine-tuning, MLOps, model serving, feature store, or any AI/ML infrastructure.
**API Documentation & Design (OpenAPI/Swagger)**: Helps write and review API documentation, generate OpenAPI/Swagger specs, design RESTful APIs, and document GraphQL schemas. Use whenever the user mentions 'API docs', 'Swagger', 'OpenAPI', 'API specification', 'API design', 'REST API', 'endpoint documentation', 'API contract', 'API versioning', 'GraphQL schema', 'API reference', or asks to document their API, generate a Swagger spec, design API endpoints, or review API contracts for consistency.
API Gateway design patterns including rate limiting, authentication, versioning, BFF and circuit breaking
ARB process design, RFC governance, decision log and technical review workflows
**Backend Code Review (Node.js, Java, Microservices)**: Expert review of backend code focusing on Node.js, Java, Clean Architecture, SOLID principles, microservices patterns, SQL/database design, messaging (Kafka, SQS, SNS), and payment flows. Use whenever the user wants a review of backend code, API design, service architecture, database queries, or mentions Node, Java, Spring, NestJS, Express, microservices, REST API, gRPC, or asks to review server-side code. Also trigger for database schema reviews, query optimization, and message queue patterns.
| name | domain-modeling |
| description | DDD practical guide: Event Storming, Bounded Contexts, Aggregates and domain-driven design patterns |
| triggers | {"anti-patterns":["anemic_domain","god_class"],"dimensions":{"cohesion":60}} |
| preferred-model | opus |
| min-confidence | 0.4 |
| depends-on | ["design-patterns"] |
| category | architecture |
| estimated-tokens | 7000 |
| tags | ["ddd","domain","event-storming","aggregates"] |
1. Invite: developers + domain experts + product
2. Orange stickies: Domain Events (past tense)
"Abastecimento Validado", "Ciclo de Faturamento Fechado"
3. Blue stickies: Commands (what triggers events)
"Validar Abastecimento", "Fechar Ciclo"
4. Yellow stickies: Aggregates (who handles commands)
"Abastecimento", "CicloFaturamento"
5. Pink stickies: External Systems
"Gateway Pagamento", "Emissor NF-e"
6. Group into Bounded Contexts
┌─── Refueling Context ──────────────────────────┐
│ Events: │
│ RefeuelingCodeGenerated │
│ RefuelingValidated │
│ RefuelingCancelled │
│ Aggregates: │
│ RefuelingCode, Refueling │
│ Commands: │
│ GenerateCode, ValidateRefueling, CancelCode │
└─────────────────────────────────────────────────┘
┌─── Billing Context ────────────────────────────┐
│ Events: │
│ BillingCycleOpened │
│ FeeCalculated │
│ CycleClosed │
│ InvoiceGenerated │
│ Aggregates: │
│ BillingCycle, Invoice │
│ Commands: │
│ CalculateFee, CloseCycle, GenerateInvoice │
└─────────────────────────────────────────────────┘
Rules:
1. Each context owns its data (no shared database tables)
2. Same word can mean different things in different contexts
"User" in Auth = credentials + session
"User" in Billing = payment info + billing address
3. Communication between contexts via events or APIs
4. One team per context (ideally)
// Aggregate = consistency boundary
// One transaction per aggregate
// Reference other aggregates by ID, not by object
// ✅ Good aggregate design
class BillingCycle {
private id: string;
private stationId: string; // Reference by ID
private status: CycleStatus;
private transactions: Transaction[]; // Owned by this aggregate
private totalFees: Money;
// Business logic lives HERE
addTransaction(refueling: RefuelingEvent): void {
if (this.status !== 'ACTIVE') {
throw new DomainError('Cannot add to closed cycle');
}
const fee = this.calculateFee(refueling);
this.transactions.push(new Transaction(refueling.id, fee));
this.totalFees = this.totalFees.add(fee);
}
close(): void {
if (this.transactions.length === 0) {
throw new DomainError('Cannot close empty cycle');
}
this.status = 'CLOSED';
// Raises domain event
this.raise(new BillingCycleClosed(this.id, this.totalFees));
}
}
// ❌ Bad: anemic domain model
class BadBillingCycle {
id: string;
stationId: string;
status: string; // Public, no validation
transactions: any[]; // No encapsulation
totalFees: number; // Primitive obsession
}
// All logic in BillingCycleService → anemic!
// Immutable, compared by value (not by ID)
class Money {
constructor(
private readonly amount: number,
private readonly currency: string = 'BRL',
) {
if (amount < 0) throw new DomainError('Amount cannot be negative');
// Store as integer cents to avoid float precision
this.amount = Math.round(amount * 100) / 100;
}
add(other: Money): Money {
if (this.currency !== other.currency) {
throw new DomainError('Cannot add different currencies');
}
return new Money(this.amount + other.amount, this.currency);
}
equals(other: Money): boolean {
return this.amount === other.amount && this.currency === other.currency;
}
}
class CPF {
constructor(private readonly value: string) {
if (!this.isValid(value)) {
throw new DomainError('Invalid CPF');
}
this.value = value.replace(/\D/g, '');
}
private isValid(cpf: string): boolean {
// Validation logic
return true;
}
masked(): string {
return `***.${this.value.slice(3, 6)}.${this.value.slice(6, 9)}-**`;
}
}
// Event = something that happened (past tense, immutable)
class RefuelingValidated {
constructor(
public readonly refuelingId: string,
public readonly stationId: string,
public readonly amount: Money,
public readonly occurredAt: Date = new Date(),
) {}
}
// Handler in another bounded context
@EventHandler(RefuelingValidated)
class BillingEventHandler {
async handle(event: RefuelingValidated): Promise<void> {
const cycle = await this.cycleRepo.findActive(event.stationId);
cycle.addTransaction(event);
await this.cycleRepo.save(cycle);
}
}