[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6E367TSFQYMBF7JYYZFMK8X":3,"skill-same:01M6E367TSFQYMBF7JYYZFMK8X":79},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":28,"publishedAt":49,"linkOnly":50,"markdownContent":51,"markdownBody":52,"frontMatter":53,"refName":57,"commitSha":58,"skillDirectory":59,"forksCount":60,"repositoryHtmlUrl":61,"repositoryHomepageUrl":6,"ownerHtmlUrl":62,"ownerBlogUrl":6,"ownerDescription":63,"updatedAt":64,"install":65,"resources":78},"01M6E367TSFQYMBF7JYYZFMK8X","domain-driven-design",null,"Expert-level Domain-Driven Design (DDD). Use when applying DDD principles, identifying bounded contexts, aggregates, entities, value objects, domain events, repositories, application services, or ubiquitous language. Also use when the user mentions 'bounded context', 'aggregate', 'value object', 'domain event', 'ubiquitous language', 'context map', 'DDD', 'domain model', or 'aggregate root'.","luokai0\u002Fai-agent-skills-by-luo-kai","luokai0","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F266954973?v=4","01M6E16VNT42JB8B3X3254QWJ7","luo kai","01M6E16VTFVP6S5XJF1V1MPV90",14,"MIT","https:\u002F\u002Fgithub.com\u002Fluokai0\u002Fai-agent-skills-by-luo-kai\u002Fblob\u002Fmain\u002Fai-agent-skills\u002F08-architecture-and-patterns (by Luo Kai)\u002F04-domain-driven-design\u002Fdomain-driven-design-expert\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000015","视觉设计","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000032","设计师","PROFESSION","适用职业",[29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],"domain driven design","driven","domain","design","expert","ddd principles","events","map","domain model","aggregates","objects","language","use","user","coding","model","ubiquitous language","services","context map","ddd","2026-11-10 10:12:30",false,"---\nauthor: luo-kai\nname: domain-driven-design\ndescription: Expert-level Domain-Driven Design (DDD). Use when applying DDD principles, identifying bounded contexts, aggregates, entities, value objects, domain events, repositories, application services, or ubiquitous language. Also use when the user mentions 'bounded context', 'aggregate', 'value object', 'domain event', 'ubiquitous language', 'context map', 'DDD', 'domain model', or 'aggregate root'.\nlicense: MIT\nmetadata:\n  author: luokai0\n  version: \"1.0\"\n  category: coding\n---\n\n# Domain-Driven Design Expert\n\nYou are an expert in Domain-Driven Design with deep knowledge of strategic design, tactical patterns, and applying DDD to real-world software projects.\n\n## Before Starting\n\n1. **Domain complexity** — simple CRUD or complex business rules?\n2. **Team context** — single team or multiple teams working on same domain?\n3. **Problem type** — strategic design (bounded contexts), tactical (aggregates, entities), or both?\n4. **Current state** — greenfield design or refactoring existing code?\n5. **Language\u002Fframework** — TypeScript, Python, Java, C#?\n\n---\n\n## Core Expertise Areas\n\n- **Strategic design**: bounded contexts, context maps, subdomains, ubiquitous language\n- **Tactical design**: aggregates, entities, value objects, domain events, domain services\n- **Repositories**: aggregate persistence, unit of work, collection-oriented vs persistence-oriented\n- **Application services**: orchestrating use cases, transaction boundaries, command\u002Fquery separation\n- **Domain events**: capturing domain occurrences, eventual consistency, integration events\n- **Context mapping**: shared kernel, customer-supplier, conformist, anti-corruption layer\n- **Event storming**: collaborative domain discovery workshop technique\n- **Anemic vs rich domain model**: recognizing and fixing anemic models\n\n---\n\n## Key Patterns & Code\n\n### Strategic Design — Core Concepts\n```\nDomain:\n  The sphere of knowledge and activity the software is about\n  Example: Insurance, Banking, E-commerce, Healthcare\n\nSubdomain types:\n  Core domain:       Your competitive advantage — invest most here\n                     Example: Recommendation engine for Netflix\n  Supporting domain: Necessary but not differentiating\n                     Example: User management, notifications\n  Generic domain:    Solved problems — buy or use off-the-shelf\n                     Example: Authentication, billing, email delivery\n\nBounded Context:\n  A boundary within which a domain model is defined and applicable\n  The same word can mean different things in different contexts\n  Example: 'Customer' in Sales context vs 'Customer' in Support context\n\nUbiquitous Language:\n  Shared vocabulary between developers and domain experts\n  Used in code, documentation, conversations, tests\n  No translation between business language and code\n  WRONG: user_account, person_record, contact_entry\n  RIGHT: Customer, Policy, Claim, Premium (use domain terms)\n\nRule of thumb:\n  One bounded context = one team ownership\n  One bounded context = one ubiquitous language\n  One bounded context = one deployable unit (microservice or module)\n```\n\n### Context Map Patterns\n```\nPartnership:\n  Two teams coordinate changes together\n  Use when: teams are closely aligned, mutual success\n\nShared Kernel:\n  Two contexts share a subset of the domain model\n  Use when: teams are closely aligned, sharing is worth the coupling\n  Risk: changes require coordination between teams\n\nCustomer-Supplier:\n  Upstream (supplier) provides API for downstream (customer)\n  Customer can influence roadmap but supplier decides\n  Use when: clear dependency direction, some influence\n\nConformist:\n  Downstream conforms to upstream model without influence\n  Use when: upstream does not care about downstream needs\n  Example: Using a third-party API, conforming to its model\n\nAnti-Corruption Layer (ACL):\n  Downstream creates translation layer to protect its model\n  Use when: upstream model is messy or incompatible\n  MOST IMPORTANT pattern for keeping domain clean\n\nOpen Host Service:\n  Provider publishes a protocol for integration\n  Use when: many consumers, published API\n\nPublished Language:\n  Well-documented shared language for integration\n  Example: CloudEvents, OpenAPI specs, AsyncAPI\n```\n\n### Aggregates — The Core Tactical Pattern\n```typescript\n\u002F\u002F Aggregate: cluster of domain objects treated as a unit\n\u002F\u002F Aggregate Root: the single entry point to the aggregate\n\u002F\u002F Rules:\n\u002F\u002F   1. All access to the aggregate goes through the root\n\u002F\u002F   2. External objects hold references only to the root\n\u002F\u002F   3. The aggregate maintains its own invariants\n\u002F\u002F   4. Aggregate boundaries define transaction scope\n\n\u002F\u002F Example: Order aggregate\n\u002F\u002F OrderItem is INSIDE the Order aggregate\n\u002F\u002F Customer is OUTSIDE — referenced by ID only\n\nexport class Order {  \u002F\u002F Aggregate Root\n  private readonly _id: OrderId;\n  private readonly _customerId: CustomerId;  \u002F\u002F reference by ID, not object\n  private _items: OrderItem[] = [];\n  private _status: OrderStatus;\n  private _domainEvents: DomainEvent[] = [];\n\n  private constructor(id: OrderId, customerId: CustomerId) {\n    this._id = id;\n    this._customerId = customerId;\n    this._status = OrderStatus.Draft;\n  }\n\n  static create(customerId: CustomerId): Order {\n    const order = new Order(OrderId.generate(), customerId);\n    order.addDomainEvent(new OrderCreatedEvent(order._id, customerId));\n    return order;\n  }\n\n  \u002F\u002F All business operations go through the aggregate root\n  addItem(productId: ProductId, quantity: Quantity, price: Money): void {\n    this.guardAgainstConfirmedOrder();\n\n    const existingItem = this._items.find(i => i.productId.equals(productId));\n    if (existingItem) {\n      existingItem.increaseQuantity(quantity);\n    } else {\n      this._items.push(OrderItem.create(productId, quantity, price));\n    }\n  }\n\n  removeItem(productId: ProductId): void {\n    this.guardAgainstConfirmedOrder();\n    this._items = this._items.filter(i => !i.productId.equals(productId));\n  }\n\n  confirm(): void {\n    if (this._status !== OrderStatus.Draft) {\n      throw new DomainError('Only draft orders can be confirmed');\n    }\n    if (this._items.length === 0) {\n      throw new DomainError('Cannot confirm empty order');\n    }\n    this._status = OrderStatus.Confirmed;\n    this.addDomainEvent(new OrderConfirmedEvent(this._id, this.total()));\n  }\n\n  cancel(reason: string): void {\n    if (this._status === OrderStatus.Shipped) {\n      throw new DomainError('Cannot cancel shipped order');\n    }\n    this._status = OrderStatus.Cancelled;\n    this.addDomainEvent(new OrderCancelledEvent(this._id, reason));\n  }\n\n  \u002F\u002F Computed value — derived from state\n  total(): Money {\n    return this._items.reduce(\n      (sum, item) => sum.add(item.subtotal()),\n      Money.zero('USD')\n    );\n  }\n\n  \u002F\u002F Invariant guard\n  private guardAgainstConfirmedOrder(): void {\n    if (this._status !== OrderStatus.Draft) {\n      throw new DomainError('Cannot modify a confirmed order');\n    }\n  }\n\n  \u002F\u002F Domain events\n  private addDomainEvent(event: DomainEvent): void {\n    this._domainEvents.push(event);\n  }\n\n  pullDomainEvents(): DomainEvent[] {\n    const events = [...this._domainEvents];\n    this._domainEvents = [];\n    return events;\n  }\n\n  \u002F\u002F Getters\n  get id(): OrderId { return this._id; }\n  get customerId(): CustomerId { return this._customerId; }\n  get status(): OrderStatus { return this._status; }\n  get items(): ReadonlyArray\u003COrderItem> { return this._items; }\n}\n\n\u002F\u002F OrderItem is INSIDE the Order aggregate\n\u002F\u002F It cannot be accessed directly from outside\nexport class OrderItem {\n  private _quantity: Quantity;\n\n  private constructor(\n    readonly productId: ProductId,\n    quantity: Quantity,\n    readonly unitPrice: Money,\n  ) {\n    this._quantity = quantity;\n  }\n\n  static create(productId: ProductId, quantity: Quantity, price: Money): OrderItem {\n    if (quantity.value \u003C= 0) {\n      throw new DomainError('Quantity must be positive');\n    }\n    return new OrderItem(productId, quantity, price);\n  }\n\n  increaseQuantity(by: Quantity): void {\n    this._quantity = this._quantity.add(by);\n  }\n\n  subtotal(): Money {\n    return this.unitPrice.multiply(this._quantity.value);\n  }\n\n  get quantity(): Quantity { return this._quantity; }\n}\n```\n\n### Value Objects\n```typescript\n\u002F\u002F Value Object: defined by its attributes, not identity\n\u002F\u002F Immutable — no setters, create new instance for changes\n\u002F\u002F Equality by value, not reference\n\nexport class Money {\n  private constructor(\n    readonly amount: number,   \u002F\u002F in cents to avoid float issues\n    readonly currency: string,\n  ) {\n    if (amount \u003C 0) throw new DomainError('Amount cannot be negative');\n    if (!currency) throw new DomainError('Currency is required');\n  }\n\n  static of(amount: number, currency: string): Money {\n    return new Money(Math.round(amount * 100), currency);\n  }\n\n  static zero(currency: string): Money {\n    return new Money(0, currency);\n  }\n\n  add(other: Money): Money {\n    this.guardSameCurrency(other);\n    return new Money(this.amount + other.amount, this.currency);\n  }\n\n  subtract(other: Money): Money {\n    this.guardSameCurrency(other);\n    const result = this.amount - other.amount;\n    if (result \u003C 0) throw new DomainError('Insufficient funds');\n    return new Money(result, this.currency);\n  }\n\n  multiply(factor: number): Money {\n    return new Money(Math.round(this.amount * factor), this.currency);\n  }\n\n  equals(other: Money): boolean {\n    return this.amount === other.amount && this.currency === other.currency;\n  }\n\n  isGreaterThan(other: Money): boolean {\n    this.guardSameCurrency(other);\n    return this.amount > other.amount;\n  }\n\n  toString(): string {\n    return (this.amount \u002F 100).toFixed(2) + ' ' + this.currency;\n  }\n\n  private guardSameCurrency(other: Money): void {\n    if (this.currency !== other.currency) {\n      throw new DomainError('Cannot operate on different currencies: ' + this.currency + ' vs ' + other.currency);\n    }\n  }\n}\n\n\u002F\u002F Strong ID types prevent mixing IDs of different aggregates\nexport class OrderId {\n  private constructor(readonly value: string) {\n    if (!value) throw new DomainError('OrderId cannot be empty');\n  }\n\n  static generate(): OrderId {\n    return new OrderId(crypto.randomUUID());\n  }\n\n  static from(value: string): OrderId {\n    return new OrderId(value);\n  }\n\n  equals(other: OrderId): boolean {\n    return this.value === other.value;\n  }\n\n  toString(): string { return this.value; }\n}\n\nexport class Quantity {\n  private constructor(readonly value: number) {\n    if (!Number.isInteger(value) || value \u003C= 0) {\n      throw new DomainError('Quantity must be a positive integer');\n    }\n  }\n\n  static of(value: number): Quantity { return new Quantity(value); }\n\n  add(other: Quantity): Quantity {\n    return new Quantity(this.value + other.value);\n  }\n\n  equals(other: Quantity): boolean { return this.value === other.value; }\n}\n```\n\n### Domain Events\n```typescript\n\u002F\u002F Domain Event: something significant that happened in the domain\n\u002F\u002F Named in past tense, immutable, carry relevant data\n\ninterface DomainEvent {\n  readonly eventId: string;\n  readonly occurredAt: Date;\n  readonly eventType: string;\n}\n\nexport class OrderCreatedEvent implements DomainEvent {\n  readonly eventId = crypto.randomUUID();\n  readonly occurredAt = new Date();\n  readonly eventType = 'order.created';\n\n  constructor(\n    readonly orderId: OrderId,\n    readonly customerId: CustomerId,\n  ) {}\n}\n\nexport class OrderConfirmedEvent implements DomainEvent {\n  readonly eventId = crypto.randomUUID();\n  readonly occurredAt = new Date();\n  readonly eventType = 'order.confirmed';\n\n  constructor(\n    readonly orderId: OrderId,\n    readonly total: Money,\n  ) {}\n}\n\nexport class OrderCancelledEvent implements DomainEvent {\n  readonly eventId = crypto.randomUUID();\n  readonly occurredAt = new Date();\n  readonly eventType = 'order.cancelled';\n\n  constructor(\n    readonly orderId: OrderId,\n    readonly reason: string,\n  ) {}\n}\n\n\u002F\u002F Domain event dispatcher\nexport class DomainEventDispatcher {\n  private handlers = new Map\u003Cstring, Function[]>();\n\n  register(eventType: string, handler: Function): void {\n    const existing = this.handlers.get(eventType) ?? [];\n    this.handlers.set(eventType, [...existing, handler]);\n  }\n\n  async dispatch(events: DomainEvent[]): Promise\u003Cvoid> {\n    for (const event of events) {\n      const eventHandlers = this.handlers.get(event.eventType) ?? [];\n      await Promise.all(eventHandlers.map(h => h(event)));\n    }\n  }\n}\n\n\u002F\u002F Dispatch after saving aggregate\nclass OrderApplicationService {\n  constructor(\n    private orderRepo: OrderRepository,\n    private eventDispatcher: DomainEventDispatcher,\n  ) {}\n\n  async confirmOrder(orderId: string): Promise\u003Cvoid> {\n    const order = await this.orderRepo.findById(OrderId.from(orderId));\n    if (!order) throw new Error('Order not found');\n\n    order.confirm();\n\n    await this.orderRepo.save(order);\n\n    \u002F\u002F Pull and dispatch events AFTER successful persistence\n    const events = order.pullDomainEvents();\n    await this.eventDispatcher.dispatch(events);\n  }\n}\n```\n\n### Anti-Corruption Layer\n```typescript\n\u002F\u002F Protect your domain from external systems\n\u002F\u002F Translate external model to your domain model\n\n\u002F\u002F External payment provider model (messy, not your domain)\ninterface StripePaymentIntent {\n  id: string;\n  amount: number;           \u002F\u002F in cents\n  currency: string;\n  status: string;           \u002F\u002F 'succeeded', 'processing', 'requires_payment_method'\n  payment_method_types: string[];\n  metadata: Record\u003Cstring, string>;\n}\n\n\u002F\u002F Your domain model\nexport class Payment {\n  constructor(\n    readonly id: PaymentId,\n    readonly orderId: OrderId,\n    readonly amount: Money,\n    readonly status: PaymentStatus,\n    readonly completedAt: Date | null,\n  ) {}\n}\n\nexport enum PaymentStatus {\n  Pending = 'pending',\n  Completed = 'completed',\n  Failed = 'failed',\n}\n\n\u002F\u002F ACL: translates Stripe model to domain model\nexport class StripePaymentAdapter {\n  private stripe: Stripe;\n\n  constructor(apiKey: string) {\n    this.stripe = new Stripe(apiKey);\n  }\n\n  async chargeOrder(orderId: OrderId, amount: Money): Promise\u003CPayment> {\n    \u002F\u002F Call external system\n    const intent = await this.stripe.paymentIntents.create({\n      amount: amount.amount,  \u002F\u002F already in cents\n      currency: amount.currency.toLowerCase(),\n      metadata: { order_id: orderId.value },\n      confirm: true,\n    });\n\n    \u002F\u002F Translate to domain model\n    return this.toDomain(intent, orderId);\n  }\n\n  private toDomain(intent: StripePaymentIntent, orderId: OrderId): Payment {\n    return new Payment(\n      PaymentId.from(intent.id),\n      orderId,\n      Money.of(intent.amount \u002F 100, intent.currency.toUpperCase()),\n      this.translateStatus(intent.status),\n      intent.status === 'succeeded' ? new Date() : null,\n    );\n  }\n\n  private translateStatus(stripeStatus: string): PaymentStatus {\n    switch (stripeStatus) {\n      case 'succeeded': return PaymentStatus.Completed;\n      case 'processing': return PaymentStatus.Pending;\n      default: return PaymentStatus.Failed;\n    }\n  }\n}\n```\n\n### Event Storming — Discovery Technique\n```\nEvent Storming is a collaborative workshop to explore a domain\n\nSteps:\n1. Domain Events (orange sticky):\n   - What happened? Past tense\n   - OrderPlaced, PaymentFailed, ItemShipped, UserRegistered\n\n2. Commands (blue sticky):\n   - What triggered the event? Imperative\n   - PlaceOrder, ProcessPayment, ShipItem, RegisterUser\n\n3. Actors (yellow sticky):\n   - Who issues the command?\n   - Customer, Admin, System, Scheduler\n\n4. Aggregates (pale yellow sticky):\n   - What handles the command and produces the event?\n   - Order, Payment, Shipment, User\n\n5. Policies (purple sticky):\n   - When Event X happens, do Command Y\n   - When OrderConfirmed, then SendConfirmationEmail\n   - When PaymentFailed, then CancelOrder\n\n6. External Systems (pink sticky):\n   - Stripe, SendGrid, Warehouse API\n\n7. Read Models (green sticky):\n   - What data does the actor need to make decisions?\n   - Order summary, Inventory levels, Customer history\n\nResult: natural bounded contexts emerge around clusters of aggregates\nThese become your microservices or modules\n```\n\n---\n\n## Best Practices\n\n- Spend time on strategic design before writing code — wrong boundaries are expensive\n- Use ubiquitous language everywhere — code, tests, docs, conversations\n- Keep aggregates small — one to three entities max, load eagerly\n- Reference other aggregates by ID only — never hold object references across boundaries\n- Prefer value objects over primitives — Money not float, Email not string\n- Domain events should be facts, not commands — past tense, immutable\n- Push logic into the domain — avoid anemic models with all logic in services\n- Use anti-corruption layers when integrating with external systems\n\n---\n\n## Common Pitfalls\n\n| Pitfall | Problem | Fix |\n|---|---|---|\n| Anemic domain model | Entities are data bags, logic is in services | Move business rules into entity methods |\n| Giant aggregates | Lock contention, slow loads, complex invariants | Keep aggregates small, 1-3 entities |\n| Object references across aggregates | Tight coupling, loading too much | Reference by ID only |\n| Primitive obsession | string for email, float for money | Create value objects for domain concepts |\n| Ignoring ubiquitous language | Code diverges from business language | Use domain terms directly in code |\n| One bounded context for everything | God context, unmaintainable | Split by subdomain and team ownership |\n| No anti-corruption layer | External model pollutes domain | Always translate at context boundaries |\n| Skipping strategic design | Wrong service boundaries | Event storm first, code second |\n\n---\n\n## Related Skills\n\n- **clean-architecture**: For structuring code within a bounded context\n- **microservices-expert**: For aligning services with bounded contexts\n- **event-driven-expert**: For domain events and integration events\n- **design-patterns**: For tactical DDD implementation patterns\n- **testing-expert**: For testing domain logic in isolation\n- **system-design**: For strategic design at the system level","# Domain-Driven Design Expert\n\nYou are an expert in Domain-Driven Design with deep knowledge of strategic design, tactical patterns, and applying DDD to real-world software projects.\n\n## Before Starting\n\n1. **Domain complexity** — simple CRUD or complex business rules?\n2. **Team context** — single team or multiple teams working on same domain?\n3. **Problem type** — strategic design (bounded contexts), tactical (aggregates, entities), or both?\n4. **Current state** — greenfield design or refactoring existing code?\n5. **Language\u002Fframework** — TypeScript, Python, Java, C#?\n\n---\n\n## Core Expertise Areas\n\n- **Strategic design**: bounded contexts, context maps, subdomains, ubiquitous language\n- **Tactical design**: aggregates, entities, value objects, domain events, domain services\n- **Repositories**: aggregate persistence, unit of work, collection-oriented vs persistence-oriented\n- **Application services**: orchestrating use cases, transaction boundaries, command\u002Fquery separation\n- **Domain events**: capturing domain occurrences, eventual consistency, integration events\n- **Context mapping**: shared kernel, customer-supplier, conformist, anti-corruption layer\n- **Event storming**: collaborative domain discovery workshop technique\n- **Anemic vs rich domain model**: recognizing and fixing anemic models\n\n---\n\n## Key Patterns & Code\n\n### Strategic Design — Core Concepts\n```\nDomain:\n  The sphere of knowledge and activity the software is about\n  Example: Insurance, Banking, E-commerce, Healthcare\n\nSubdomain types:\n  Core domain:       Your competitive advantage — invest most here\n                     Example: Recommendation engine for Netflix\n  Supporting domain: Necessary but not differentiating\n                     Example: User management, notifications\n  Generic domain:    Solved problems — buy or use off-the-shelf\n                     Example: Authentication, billing, email delivery\n\nBounded Context:\n  A boundary within which a domain model is defined and applicable\n  The same word can mean different things in different contexts\n  Example: 'Customer' in Sales context vs 'Customer' in Support context\n\nUbiquitous Language:\n  Shared vocabulary between developers and domain experts\n  Used in code, documentation, conversations, tests\n  No translation between business language and code\n  WRONG: user_account, person_record, contact_entry\n  RIGHT: Customer, Policy, Claim, Premium (use domain terms)\n\nRule of thumb:\n  One bounded context = one team ownership\n  One bounded context = one ubiquitous language\n  One bounded context = one deployable unit (microservice or module)\n```\n\n### Context Map Patterns\n```\nPartnership:\n  Two teams coordinate changes together\n  Use when: teams are closely aligned, mutual success\n\nShared Kernel:\n  Two contexts share a subset of the domain model\n  Use when: teams are closely aligned, sharing is worth the coupling\n  Risk: changes require coordination between teams\n\nCustomer-Supplier:\n  Upstream (supplier) provides API for downstream (customer)\n  Customer can influence roadmap but supplier decides\n  Use when: clear dependency direction, some influence\n\nConformist:\n  Downstream conforms to upstream model without influence\n  Use when: upstream does not care about downstream needs\n  Example: Using a third-party API, conforming to its model\n\nAnti-Corruption Layer (ACL):\n  Downstream creates translation layer to protect its model\n  Use when: upstream model is messy or incompatible\n  MOST IMPORTANT pattern for keeping domain clean\n\nOpen Host Service:\n  Provider publishes a protocol for integration\n  Use when: many consumers, published API\n\nPublished Language:\n  Well-documented shared language for integration\n  Example: CloudEvents, OpenAPI specs, AsyncAPI\n```\n\n### Aggregates — The Core Tactical Pattern\n```typescript\n\u002F\u002F Aggregate: cluster of domain objects treated as a unit\n\u002F\u002F Aggregate Root: the single entry point to the aggregate\n\u002F\u002F Rules:\n\u002F\u002F   1. All access to the aggregate goes through the root\n\u002F\u002F   2. External objects hold references only to the root\n\u002F\u002F   3. The aggregate maintains its own invariants\n\u002F\u002F   4. Aggregate boundaries define transaction scope\n\n\u002F\u002F Example: Order aggregate\n\u002F\u002F OrderItem is INSIDE the Order aggregate\n\u002F\u002F Customer is OUTSIDE — referenced by ID only\n\nexport class Order {  \u002F\u002F Aggregate Root\n  private readonly _id: OrderId;\n  private readonly _customerId: CustomerId;  \u002F\u002F reference by ID, not object\n  private _items: OrderItem[] = [];\n  private _status: OrderStatus;\n  private _domainEvents: DomainEvent[] = [];\n\n  private constructor(id: OrderId, customerId: CustomerId) {\n    this._id = id;\n    this._customerId = customerId;\n    this._status = OrderStatus.Draft;\n  }\n\n  static create(customerId: CustomerId): Order {\n    const order = new Order(OrderId.generate(), customerId);\n    order.addDomainEvent(new OrderCreatedEvent(order._id, customerId));\n    return order;\n  }\n\n  \u002F\u002F All business operations go through the aggregate root\n  addItem(productId: ProductId, quantity: Quantity, price: Money): void {\n    this.guardAgainstConfirmedOrder();\n\n    const existingItem = this._items.find(i => i.productId.equals(productId));\n    if (existingItem) {\n      existingItem.increaseQuantity(quantity);\n    } else {\n      this._items.push(OrderItem.create(productId, quantity, price));\n    }\n  }\n\n  removeItem(productId: ProductId): void {\n    this.guardAgainstConfirmedOrder();\n    this._items = this._items.filter(i => !i.productId.equals(productId));\n  }\n\n  confirm(): void {\n    if (this._status !== OrderStatus.Draft) {\n      throw new DomainError('Only draft orders can be confirmed');\n    }\n    if (this._items.length === 0) {\n      throw new DomainError('Cannot confirm empty order');\n    }\n    this._status = OrderStatus.Confirmed;\n    this.addDomainEvent(new OrderConfirmedEvent(this._id, this.total()));\n  }\n\n  cancel(reason: string): void {\n    if (this._status === OrderStatus.Shipped) {\n      throw new DomainError('Cannot cancel shipped order');\n    }\n    this._status = OrderStatus.Cancelled;\n    this.addDomainEvent(new OrderCancelledEvent(this._id, reason));\n  }\n\n  \u002F\u002F Computed value — derived from state\n  total(): Money {\n    return this._items.reduce(\n      (sum, item) => sum.add(item.subtotal()),\n      Money.zero('USD')\n    );\n  }\n\n  \u002F\u002F Invariant guard\n  private guardAgainstConfirmedOrder(): void {\n    if (this._status !== OrderStatus.Draft) {\n      throw new DomainError('Cannot modify a confirmed order');\n    }\n  }\n\n  \u002F\u002F Domain events\n  private addDomainEvent(event: DomainEvent): void {\n    this._domainEvents.push(event);\n  }\n\n  pullDomainEvents(): DomainEvent[] {\n    const events = [...this._domainEvents];\n    this._domainEvents = [];\n    return events;\n  }\n\n  \u002F\u002F Getters\n  get id(): OrderId { return this._id; }\n  get customerId(): CustomerId { return this._customerId; }\n  get status(): OrderStatus { return this._status; }\n  get items(): ReadonlyArray\u003COrderItem> { return this._items; }\n}\n\n\u002F\u002F OrderItem is INSIDE the Order aggregate\n\u002F\u002F It cannot be accessed directly from outside\nexport class OrderItem {\n  private _quantity: Quantity;\n\n  private constructor(\n    readonly productId: ProductId,\n    quantity: Quantity,\n    readonly unitPrice: Money,\n  ) {\n    this._quantity = quantity;\n  }\n\n  static create(productId: ProductId, quantity: Quantity, price: Money): OrderItem {\n    if (quantity.value \u003C= 0) {\n      throw new DomainError('Quantity must be positive');\n    }\n    return new OrderItem(productId, quantity, price);\n  }\n\n  increaseQuantity(by: Quantity): void {\n    this._quantity = this._quantity.add(by);\n  }\n\n  subtotal(): Money {\n    return this.unitPrice.multiply(this._quantity.value);\n  }\n\n  get quantity(): Quantity { return this._quantity; }\n}\n```\n\n### Value Objects\n```typescript\n\u002F\u002F Value Object: defined by its attributes, not identity\n\u002F\u002F Immutable — no setters, create new instance for changes\n\u002F\u002F Equality by value, not reference\n\nexport class Money {\n  private constructor(\n    readonly amount: number,   \u002F\u002F in cents to avoid float issues\n    readonly currency: string,\n  ) {\n    if (amount \u003C 0) throw new DomainError('Amount cannot be negative');\n    if (!currency) throw new DomainError('Currency is required');\n  }\n\n  static of(amount: number, currency: string): Money {\n    return new Money(Math.round(amount * 100), currency);\n  }\n\n  static zero(currency: string): Money {\n    return new Money(0, currency);\n  }\n\n  add(other: Money): Money {\n    this.guardSameCurrency(other);\n    return new Money(this.amount + other.amount, this.currency);\n  }\n\n  subtract(other: Money): Money {\n    this.guardSameCurrency(other);\n    const result = this.amount - other.amount;\n    if (result \u003C 0) throw new DomainError('Insufficient funds');\n    return new Money(result, this.currency);\n  }\n\n  multiply(factor: number): Money {\n    return new Money(Math.round(this.amount * factor), this.currency);\n  }\n\n  equals(other: Money): boolean {\n    return this.amount === other.amount && this.currency === other.currency;\n  }\n\n  isGreaterThan(other: Money): boolean {\n    this.guardSameCurrency(other);\n    return this.amount > other.amount;\n  }\n\n  toString(): string {\n    return (this.amount \u002F 100).toFixed(2) + ' ' + this.currency;\n  }\n\n  private guardSameCurrency(other: Money): void {\n    if (this.currency !== other.currency) {\n      throw new DomainError('Cannot operate on different currencies: ' + this.currency + ' vs ' + other.currency);\n    }\n  }\n}\n\n\u002F\u002F Strong ID types prevent mixing IDs of different aggregates\nexport class OrderId {\n  private constructor(readonly value: string) {\n    if (!value) throw new DomainError('OrderId cannot be empty');\n  }\n\n  static generate(): OrderId {\n    return new OrderId(crypto.randomUUID());\n  }\n\n  static from(value: string): OrderId {\n    return new OrderId(value);\n  }\n\n  equals(other: OrderId): boolean {\n    return this.value === other.value;\n  }\n\n  toString(): string { return this.value; }\n}\n\nexport class Quantity {\n  private constructor(readonly value: number) {\n    if (!Number.isInteger(value) || value \u003C= 0) {\n      throw new DomainError('Quantity must be a positive integer');\n    }\n  }\n\n  static of(value: number): Quantity { return new Quantity(value); }\n\n  add(other: Quantity): Quantity {\n    return new Quantity(this.value + other.value);\n  }\n\n  equals(other: Quantity): boolean { return this.value === other.value; }\n}\n```\n\n### Domain Events\n```typescript\n\u002F\u002F Domain Event: something significant that happened in the domain\n\u002F\u002F Named in past tense, immutable, carry relevant data\n\ninterface DomainEvent {\n  readonly eventId: string;\n  readonly occurredAt: Date;\n  readonly eventType: string;\n}\n\nexport class OrderCreatedEvent implements DomainEvent {\n  readonly eventId = crypto.randomUUID();\n  readonly occurredAt = new Date();\n  readonly eventType = 'order.created';\n\n  constructor(\n    readonly orderId: OrderId,\n    readonly customerId: CustomerId,\n  ) {}\n}\n\nexport class OrderConfirmedEvent implements DomainEvent {\n  readonly eventId = crypto.randomUUID();\n  readonly occurredAt = new Date();\n  readonly eventType = 'order.confirmed';\n\n  constructor(\n    readonly orderId: OrderId,\n    readonly total: Money,\n  ) {}\n}\n\nexport class OrderCancelledEvent implements DomainEvent {\n  readonly eventId = crypto.randomUUID();\n  readonly occurredAt = new Date();\n  readonly eventType = 'order.cancelled';\n\n  constructor(\n    readonly orderId: OrderId,\n    readonly reason: string,\n  ) {}\n}\n\n\u002F\u002F Domain event dispatcher\nexport class DomainEventDispatcher {\n  private handlers = new Map\u003Cstring, Function[]>();\n\n  register(eventType: string, handler: Function): void {\n    const existing = this.handlers.get(eventType) ?? [];\n    this.handlers.set(eventType, [...existing, handler]);\n  }\n\n  async dispatch(events: DomainEvent[]): Promise\u003Cvoid> {\n    for (const event of events) {\n      const eventHandlers = this.handlers.get(event.eventType) ?? [];\n      await Promise.all(eventHandlers.map(h => h(event)));\n    }\n  }\n}\n\n\u002F\u002F Dispatch after saving aggregate\nclass OrderApplicationService {\n  constructor(\n    private orderRepo: OrderRepository,\n    private eventDispatcher: DomainEventDispatcher,\n  ) {}\n\n  async confirmOrder(orderId: string): Promise\u003Cvoid> {\n    const order = await this.orderRepo.findById(OrderId.from(orderId));\n    if (!order) throw new Error('Order not found');\n\n    order.confirm();\n\n    await this.orderRepo.save(order);\n\n    \u002F\u002F Pull and dispatch events AFTER successful persistence\n    const events = order.pullDomainEvents();\n    await this.eventDispatcher.dispatch(events);\n  }\n}\n```\n\n### Anti-Corruption Layer\n```typescript\n\u002F\u002F Protect your domain from external systems\n\u002F\u002F Translate external model to your domain model\n\n\u002F\u002F External payment provider model (messy, not your domain)\ninterface StripePaymentIntent {\n  id: string;\n  amount: number;           \u002F\u002F in cents\n  currency: string;\n  status: string;           \u002F\u002F 'succeeded', 'processing', 'requires_payment_method'\n  payment_method_types: string[];\n  metadata: Record\u003Cstring, string>;\n}\n\n\u002F\u002F Your domain model\nexport class Payment {\n  constructor(\n    readonly id: PaymentId,\n    readonly orderId: OrderId,\n    readonly amount: Money,\n    readonly status: PaymentStatus,\n    readonly completedAt: Date | null,\n  ) {}\n}\n\nexport enum PaymentStatus {\n  Pending = 'pending',\n  Completed = 'completed',\n  Failed = 'failed',\n}\n\n\u002F\u002F ACL: translates Stripe model to domain model\nexport class StripePaymentAdapter {\n  private stripe: Stripe;\n\n  constructor(apiKey: string) {\n    this.stripe = new Stripe(apiKey);\n  }\n\n  async chargeOrder(orderId: OrderId, amount: Money): Promise\u003CPayment> {\n    \u002F\u002F Call external system\n    const intent = await this.stripe.paymentIntents.create({\n      amount: amount.amount,  \u002F\u002F already in cents\n      currency: amount.currency.toLowerCase(),\n      metadata: { order_id: orderId.value },\n      confirm: true,\n    });\n\n    \u002F\u002F Translate to domain model\n    return this.toDomain(intent, orderId);\n  }\n\n  private toDomain(intent: StripePaymentIntent, orderId: OrderId): Payment {\n    return new Payment(\n      PaymentId.from(intent.id),\n      orderId,\n      Money.of(intent.amount \u002F 100, intent.currency.toUpperCase()),\n      this.translateStatus(intent.status),\n      intent.status === 'succeeded' ? new Date() : null,\n    );\n  }\n\n  private translateStatus(stripeStatus: string): PaymentStatus {\n    switch (stripeStatus) {\n      case 'succeeded': return PaymentStatus.Completed;\n      case 'processing': return PaymentStatus.Pending;\n      default: return PaymentStatus.Failed;\n    }\n  }\n}\n```\n\n### Event Storming — Discovery Technique\n```\nEvent Storming is a collaborative workshop to explore a domain\n\nSteps:\n1. Domain Events (orange sticky):\n   - What happened? Past tense\n   - OrderPlaced, PaymentFailed, ItemShipped, UserRegistered\n\n2. Commands (blue sticky):\n   - What triggered the event? Imperative\n   - PlaceOrder, ProcessPayment, ShipItem, RegisterUser\n\n3. Actors (yellow sticky):\n   - Who issues the command?\n   - Customer, Admin, System, Scheduler\n\n4. Aggregates (pale yellow sticky):\n   - What handles the command and produces the event?\n   - Order, Payment, Shipment, User\n\n5. Policies (purple sticky):\n   - When Event X happens, do Command Y\n   - When OrderConfirmed, then SendConfirmationEmail\n   - When PaymentFailed, then CancelOrder\n\n6. External Systems (pink sticky):\n   - Stripe, SendGrid, Warehouse API\n\n7. Read Models (green sticky):\n   - What data does the actor need to make decisions?\n   - Order summary, Inventory levels, Customer history\n\nResult: natural bounded contexts emerge around clusters of aggregates\nThese become your microservices or modules\n```\n\n---\n\n## Best Practices\n\n- Spend time on strategic design before writing code — wrong boundaries are expensive\n- Use ubiquitous language everywhere — code, tests, docs, conversations\n- Keep aggregates small — one to three entities max, load eagerly\n- Reference other aggregates by ID only — never hold object references across boundaries\n- Prefer value objects over primitives — Money not float, Email not string\n- Domain events should be facts, not commands — past tense, immutable\n- Push logic into the domain — avoid anemic models with all logic in services\n- Use anti-corruption layers when integrating with external systems\n\n---\n\n## Common Pitfalls\n\n| Pitfall | Problem | Fix |\n|---|---|---|\n| Anemic domain model | Entities are data bags, logic is in services | Move business rules into entity methods |\n| Giant aggregates | Lock contention, slow loads, complex invariants | Keep aggregates small, 1-3 entities |\n| Object references across aggregates | Tight coupling, loading too much | Reference by ID only |\n| Primitive obsession | string for email, float for money | Create value objects for domain concepts |\n| Ignoring ubiquitous language | Code diverges from business language | Use domain terms directly in code |\n| One bounded context for everything | God context, unmaintainable | Split by subdomain and team ownership |\n| No anti-corruption layer | External model pollutes domain | Always translate at context boundaries |\n| Skipping strategic design | Wrong service boundaries | Event storm first, code second |\n\n---\n\n## Related Skills\n\n- **clean-architecture**: For structuring code within a bounded context\n- **microservices-expert**: For aligning services with bounded contexts\n- **event-driven-expert**: For domain events and integration events\n- **design-patterns**: For tactical DDD implementation patterns\n- **testing-expert**: For testing domain logic in isolation\n- **system-design**: For strategic design at the system level",{"name":5,"author":54,"license":15,"metadata":55,"description":7},"luo-kai",{"author":9,"version":56,"category":43},"1.0","main","4d2fb89cdfb75f3f6fd3c6c5ec9ad90ea129eb3c","ai-agent-skills\u002F08-architecture-and-patterns (by Luo Kai)\u002F04-domain-driven-design\u002Fdomain-driven-design-expert",2,"https:\u002F\u002Fgithub.com\u002Fluokai0\u002Fai-agent-skills-by-luo-kai","https:\u002F\u002Fgithub.com\u002Fluokai0","a solo builder.","2026-05-06 04:29:04",{"source":8,"skillName":5,"commands":66,"prompt":76,"zipAvailable":77},[67,70,73],{"tool":68,"command":69},"npx","npx skills add luokai0\u002Fai-agent-skills-by-luo-kai --skill domain-driven-design",{"tool":71,"command":72},"bunx","bunx skills add luokai0\u002Fai-agent-skills-by-luo-kai --skill domain-driven-design",{"tool":74,"command":75},"pnpm","pnpm dlx skills add luokai0\u002Fai-agent-skills-by-luo-kai --skill domain-driven-design","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fluokai0\u002Fai-agent-skills-by-luo-kai\u002Fblob\u002Fmain\u002Fai-agent-skills\u002F08-architecture-and-patterns (by Luo Kai)\u002F04-domain-driven-design\u002Fdomain-driven-design-expert\u002FSKILL.md",true,[],[80,105,122,150,182,216],{"skillNo":81,"name":82,"nameZh":6,"description":83,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":84,"categories":85,"keywords":86,"publishedAt":49},"01M6F2SKTE208174C7S7JJCZC7","write-contracts","Generates secure Aptos Move V2 smart contracts with Object model, Digital Asset integration, security patterns, and storage type guidance. Includes comprehensive storage decision framework for optimal data structure selection. Triggers on: 'write contract', 'create NFT collection', 'build marketplace', 'implement minting', 'generate Move module', 'create token contract', 'build DAO', 'implement staking'. Ask storage questions when: 'store', 'track', 'registry', 'mapping', 'list', 'collection'.","https:\u002F\u002Fgithub.com\u002Fluokai0\u002Fai-agent-skills-by-luo-kai\u002Fblob\u002Fmain\u002Fai-agent-skills\u002F22-clawhub-skills\u002Fwrite-contracts\u002FSKILL.md",[],[87,88,89,90,91,92,93,94,44,95,96,97,98,39,99,100,101,102,103,104],"write","contracts","write contracts","storage","secure","create","generate","collection","decision","ask","smart-contracts","selection","structure","nft","token","aptos","critical","move",{"skillNo":106,"name":107,"nameZh":6,"description":108,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":109,"categories":110,"keywords":111,"publishedAt":49},"01M6F2RFXJ6T9MKW0A7N7GM45B","web-search","通用网络搜索技能，支持多引擎搜索（百度、必应、DuckDuckGo），无需API密钥即可获取实时信息","https:\u002F\u002Fgithub.com\u002Fluokai0\u002Fai-agent-skills-by-luo-kai\u002Fblob\u002Fmain\u002Fai-agent-skills\u002F22-clawhub-skills\u002Fweb-search-ex-skill\u002FSKILL.md",[],[112,113,114,115,116,117,118,119,120,121],"web","search","web search","搜索","baidu","bing","duckduckgo","百度","多引擎","internet",{"skillNo":123,"name":124,"nameZh":6,"description":125,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":126,"categories":127,"keywords":131,"publishedAt":49},"01M6F2K968G1FZEZFPW2E6B222","solo-build","Execute implementation plan tasks with TDD workflow, auto-commit, and phase gates. Use when user says \"build it\", \"start building\", \"execute plan\", \"implement tasks\", \"ship it\", or references a track ID. Do NOT use for planning (use \u002Fplan) or scaffolding (use \u002Fscaffold).","https:\u002F\u002Fgithub.com\u002Fluokai0\u002Fai-agent-skills-by-luo-kai\u002Fblob\u002Fmain\u002Fai-agent-skills\u002F22-clawhub-skills\u002Fsolo-build\u002FSKILL.md",[128],{"categoryNo":129,"categoryName":130,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[132,133,134,135,136,137,138,139,113,140,141,142,87,143,144,145,146,147,148,149],"build","solo","solo build","MCP","workflow","commit","tasks","code","Bash","plan","read","auto","references","context7","query","scaffolding","ship it","project",{"skillNo":151,"name":152,"nameZh":6,"description":153,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":154,"categories":155,"keywords":162,"publishedAt":49},"01M6F2GKN39SGHSPWYB3662YC8","react-email","Use when building HTML email templates with React components, adding a visual email editor to an application using the React Email visual editor, rendering emails to HTML, or sending emails with Resend. Covers welcome emails, password resets, notifications, order confirmations, newsletters, transactional emails, and the embeddable email editor component.","https:\u002F\u002Fgithub.com\u002Fluokai0\u002Fai-agent-skills-by-luo-kai\u002Fblob\u002Fmain\u002Fai-agent-skills\u002F22-clawhub-skills\u002Freact-email-skills\u002FSKILL.md",[156,159],{"categoryNo":157,"categoryName":158,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000005","邮件与沟通",{"categoryNo":160,"categoryName":161,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发",[163,164,165,166,167,168,169,170,171,172,173,174,175,41,176,177,178,179,180,181],"react","email","react email","github","editor","components","react components","component","welcome","https","notifications","password","node","html","resend","visual","emails","rendering","with",{"skillNo":183,"name":184,"nameZh":6,"description":185,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":186,"categories":187,"keywords":198,"publishedAt":49},"01M6F2FBVGVDFSG8SSFJGXA40C","APITester Agent-Driven API Testing","Test API endpoints and document responses. Define tests in plain English, run them, get formatted results. Agent-driven Postman alternative.","https:\u002F\u002Fgithub.com\u002Fluokai0\u002Fai-agent-skills-by-luo-kai\u002Fblob\u002Fmain\u002Fai-agent-skills\u002F22-clawhub-skills\u002Fopenclaw-api-tester\u002FSKILL.md",[188,191,194,195],{"categoryNo":189,"categoryName":190,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":192,"categoryName":193,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":129,"categoryName":130,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":196,"categoryName":197,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000030","开发工程师",[199,200,201,30,202,203,204,205,206,207,208,209,210,211,212,213,214,215],"testing","api","api testing","APITester Agent Driven API Testing","test","run","postman","results","plain english","tester","endpoints","document","get","define","tests","english","api-tester",{"skillNo":217,"name":218,"nameZh":6,"description":219,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":220,"categories":221,"keywords":222,"publishedAt":49},"01M6F273Y3NGAPVJZ608BC0G9G","hubspot-implementation-plan","Generate a phased implementation plan from a HubSpot audit report. Creates prioritized, sequenced cleanup processes with effort estimates, dependencies, and automation feasibility. Use after running \u002Fhubspot-audit.","https:\u002F\u002Fgithub.com\u002Fluokai0\u002Fai-agent-skills-by-luo-kai\u002Fblob\u002Fmain\u002Fai-agent-skills\u002F22-clawhub-skills\u002Fhubspot-implementation-plan\u002FSKILL.md",[],[141,223,224,225,226,227,228,93,229,230,231,232,233,41,234,235,181,236],"implementation","hubspot","hubspot implementation plan","automation","feasibility","cleanup","planning","report","audit","audit report","phased implementation","dependencies","processes","hubspot audit"]