From bf85db11d678da200a8be621aca5f5b9228f732c Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 30 Apr 2026 08:25:48 -0400 Subject: [PATCH 1/8] fix(trogonstack-eda): resolve merge conflicts in scaffold-plugin skill Signed-off-by: Yordis Prieto --- .claude-plugin/marketplace.json | 6 ++++++ .github/release-please-config.json | 3 +++ .github/release-please-manifest.json | 3 ++- plugins/trogonstack-eda/.claude-plugin/plugin.json | 9 +++++++++ plugins/trogonstack-eda/README.md | 7 +++++++ 5 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 plugins/trogonstack-eda/.claude-plugin/plugin.json create mode 100644 plugins/trogonstack-eda/README.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 48e8b5f..79c0b93 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -48,6 +48,12 @@ "description": "Event Modeling skills for brainstorming, designing, slicing, and validating software systems following the Event Modeling methodology", "source": "./plugins/trogonstack-eventmodeling", "category": "development" + }, + { + "name": "trogonstack-eda", + "description": "Event-driven architecture skills for designing event names, schemas, contracts, and catalogs following good practices for domain and integration events", + "source": "./plugins/trogonstack-eda", + "category": "development" } ] } diff --git a/.github/release-please-config.json b/.github/release-please-config.json index 382b179..68d912e 100644 --- a/.github/release-please-config.json +++ b/.github/release-please-config.json @@ -44,6 +44,9 @@ }, "plugins/trogonstack-eventmodeling": { "component": "trogonstack-eventmodeling" + }, + "plugins/trogonstack-eda": { + "component": "trogonstack-eda" } }, "plugins": [ diff --git a/.github/release-please-manifest.json b/.github/release-please-manifest.json index 2610e84..a7ed942 100644 --- a/.github/release-please-manifest.json +++ b/.github/release-please-manifest.json @@ -5,5 +5,6 @@ "plugins/trogonstack-datadog": "0.3.0", "plugins/trogonstack-ask": "0.1.1", "plugins/trogonstack-otel": "0.1.0", - "plugins/trogonstack-eventmodeling": "0.1.0" + "plugins/trogonstack-eventmodeling": "0.1.0", + "plugins/trogonstack-eda": "0.0.1" } diff --git a/plugins/trogonstack-eda/.claude-plugin/plugin.json b/plugins/trogonstack-eda/.claude-plugin/plugin.json new file mode 100644 index 0000000..b885491 --- /dev/null +++ b/plugins/trogonstack-eda/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "trogonstack-eda", + "description": "Event-driven architecture skills for designing event names, schemas, contracts, and catalogs following good practices for domain and integration events", + "version": "0.0.1", + "author": { + "name": "TrogonStack", + "url": "https://github.com/TrogonStack" + } +} diff --git a/plugins/trogonstack-eda/README.md b/plugins/trogonstack-eda/README.md new file mode 100644 index 0000000..782b849 --- /dev/null +++ b/plugins/trogonstack-eda/README.md @@ -0,0 +1,7 @@ +# trogonstack-eda + +Event-driven architecture skills for designing event names, schemas, contracts, and catalogs following good practices for domain and integration events. + +```bash +claude plugin install trogonstack-eda@trogonstack +``` From 6108a72279529ead92e22432f394410473dee075 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 30 Apr 2026 08:33:41 -0400 Subject: [PATCH 2/8] feat(trogonstack-eda): add event-name skill for event and field naming Signed-off-by: Yordis Prieto --- .../skills/event-name/SKILL.md | 235 ++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 plugins/trogonstack-eda/skills/event-name/SKILL.md diff --git a/plugins/trogonstack-eda/skills/event-name/SKILL.md b/plugins/trogonstack-eda/skills/event-name/SKILL.md new file mode 100644 index 0000000..2a25cfb --- /dev/null +++ b/plugins/trogonstack-eda/skills/event-name/SKILL.md @@ -0,0 +1,235 @@ +--- +name: event-name +description: >- + Review or create event names and payload field names following + event-driven architecture conventions. Covers both domain events + (event-sourcing) and integration events (cross-service). Validates + tense, specificity, field naming, and metadata placement. Use when + designing new events, reviewing existing event definitions, or + auditing naming consistency across an event catalog. Do not use for: + (1) event schema evolution or versioning strategy (use event-design-contract), + (2) full event payload design (use event-design-domain-schema or + event-design-integration-schema), (3) event modeling workflows + (use trogonstack-eventmodeling skills). +allowed-tools: + - AskUserQuestion + - Write + - Read + - Bash +--- + +# Review or Create Event Names + +Review or create event names and payload field names that follow event-driven architecture conventions, ensuring correct tense, domain language, specificity, and field naming for both domain and integration events. + +## Core Principle + +An event name declares **what happened** as a fact. A field name declares **what was captured** about that fact. Everything else — who emitted it, which service, which environment — belongs in metadata or envelope, not in the event name or field names. + +## Interview + +Before reviewing or creating names, establish: + +1. **Domain or integration?** Domain events live inside a bounded context and are the source of truth for state. Integration events cross service boundaries and form a public contract. +2. **What business process or workflow does this event belong to?** +3. **What are the existing naming conventions in the codebase?** If there is an existing event catalog, read it first and follow its conventions unless they violate the rules below. + +If the user provides event names to review, skip to the review. If the user asks to design new events, gather the business context first. + +## Event Naming Rules + +### 1. Past Tense — Events Are Facts + +Events record something that already happened. Always use past tense. + +| Good | Bad | Why bad | +|------|-----|---------| +| `OrderPlaced` | `PlaceOrder` | Imperative — that's a command | +| `PaymentFailed` | `PaymentFailing` | Progressive — not a fact yet | +| `ItemAddedToCart` | `AddItemToCart` | Imperative | +| `SubscriptionRenewed` | `RenewSubscription` | Imperative | + +### 2. Domain Language — Not Technical Jargon + +Use the language of the business, not the implementation. + +| Good | Bad | Why bad | +|------|-----|---------| +| `OrderPlaced` | `OrderCreated` | "Created" is CRUD, not domain language (unless creation IS the domain concept) | +| `ShipmentDispatched` | `ShipmentUpdated` | "Updated" says nothing about what happened | +| `ClaimApproved` | `ClaimStatusChanged` | Hides the actual business event | +| `EmployeePromoted` | `EmployeeRecordModified` | Technical, not business | + +### 3. Specific — Not Generic + +An event name should tell you exactly what happened without reading the payload. + +| Good | Bad | Why bad | +|------|-----|---------| +| `OrderItemAdded` | `OrderChanged` | What changed? | +| `InvoiceSentToCustomer` | `InvoiceProcessed` | Processed how? | +| `PasswordResetRequested` | `UserActionOccurred` | Meaningless | +| `InventoryReplenished` | `InventoryEvent` | Not an event name, it's a category | + +### 4. No CRUD Unless CRUD IS the Domain + +Reserve `Created`, `Updated`, `Deleted` for domains where those operations ARE the business concept (e.g., CMS content management, configuration management). In most domains, a more specific verb exists. + +| Domain | CRUD OK? | Better name | +|--------|----------|-------------| +| Order management | No | `OrderPlaced` not `OrderCreated` | +| CMS page editing | Yes | `PageCreated` is the actual domain concept | +| User signup | No | `AccountRegistered` not `UserCreated` | +| Config management | Yes | `SettingUpdated` is the actual domain concept | + +### 5. One Event — One Thing That Happened + +Do not combine multiple facts into one event name. + +| Good | Bad | +|------|-----| +| `OrderPlaced` + `PaymentAuthorized` | `OrderPlacedAndPaymentAuthorized` | +| `ItemShipped` + `TrackingNumberAssigned` | `ItemShippedWithTracking` | + +If two things always happen together, they are still two events that happen to share the same cause. + +### 6. Naming Format + +Choose one format and apply it consistently across the entire system: + +- **PascalCase** (most common): `OrderPlaced`, `PaymentFailed` +- **dot.delimited** (common in messaging systems): `order.placed`, `payment.failed` +- **kebab-case** (less common): `order-placed`, `payment-failed` + +Do not mix formats within a system. + +## Integration Event Naming — Additional Rules + +Integration events cross service boundaries. They carry additional constraints: + +### 7. Prefix With Bounded Context or Service + +Integration events must be unambiguous across the entire system. Prefix with the originating bounded context. + +| Good | Bad | Why bad | +|------|-----|---------| +| `billing.InvoiceIssued` | `InvoiceIssued` | Which service? Billing? Accounting? | +| `warehouse.ShipmentDispatched` | `ShipmentDispatched` | Could be logistics or warehouse | +| `identity.AccountLocked` | `AccountLocked` | Could be identity or fraud | + +### 8. Use Shared Vocabulary + +Integration events form a public contract. Use terms that consumers across teams understand, not internal jargon. + +| Good (shared) | Bad (internal jargon) | +|----------------|----------------------| +| `order.PaymentCompleted` | `order.PGTxnSettled` | +| `shipping.DeliveryAttempted` | `shipping.LMDAttemptV2` | + +## Field Naming Rules + +### 9. Domain Language for Fields Too + +Fields describe facts captured about the event. Use business language. + +| Good | Bad | Why bad | +|------|-----|---------| +| `order_id` | `id` | Which id? | +| `total_amount` | `val` | Abbreviation hides meaning | +| `shipping_address` | `addr` | Abbreviation | +| `customer_email` | `data` | Meaningless | + +### 10. Explicit Identifiers + +Always suffix identifiers with what they reference. Never use bare `id`. + +| Good | Bad | +|------|-----| +| `order_id` | `id` | +| `customer_id` | `cid` | +| `product_sku` | `sku` (acceptable if unambiguous in context) | +| `correlation_id` | `corr_id` | + +### 11. Temporal Fields Use Past Tense or `_at` / `_on` Suffix + +| Good | Bad | Why bad | +|------|-----|---------| +| `placed_at` | `place_time` | Not past tense | +| `shipped_on` | `ship_date` | Not past tense | +| `confirmed_at` | `confirmation_timestamp` | Verbose, inconsistent | +| `occurred_at` | `timestamp` | Generic, unclear what time it refers to | + +Pick `_at` (for datetime with time) or `_on` (for date only) and be consistent. + +### 12. Boolean Fields Use Predicate Form + +| Good | Bad | +|------|-----| +| `is_expedited` | `expedited` (ambiguous — could be a noun or adjective) | +| `has_discount` | `discount` (ambiguous — could be the amount) | +| `was_refunded` | `refunded` (ambiguous in some contexts) | + +### 13. No Computed or Derived Fields in Domain Events + +Domain events capture raw facts. Computed values belong in read models. + +| Domain event field (good) | Read model field (where computed values go) | +|--------------------------|---------------------------------------------| +| `unit_price`, `quantity` | `line_total` (= unit_price * quantity) | +| `items[]` | `item_count` (= items.length) | +| `subtotal`, `tax`, `discount` | `total` (= subtotal + tax - discount) | + +This rule does NOT apply to integration events — integration events may include pre-computed values to avoid forcing consumers to replicate business logic. + +### 14. Metadata vs Domain Fields + +Separate envelope/metadata from the business payload. Metadata fields describe the event itself, not the business fact. + +**Metadata (envelope):** +- `event_id` — unique identifier for this event instance +- `event_type` — the event name (redundant with deserialization but useful for routing) +- `occurred_at` — when the business fact happened +- `recorded_at` — when the event was persisted (may differ from occurred_at) +- `correlation_id` — ties related events across a workflow +- `causation_id` — the command or event that caused this event +- `source` — originating service/context (integration events) +- `schema_version` — payload schema version + +**Domain fields (payload):** +Everything specific to the business fact: `order_id`, `customer_id`, `total_amount`, `items[]`, etc. + +### 15. Consistent Casing for Fields + +Pick one and apply it consistently: + +- **snake_case** (most common in event stores, Kafka, NATS): `order_id`, `total_amount` +- **camelCase** (common in JavaScript/TypeScript ecosystems): `orderId`, `totalAmount` + +Do not mix within a system. + +## Review Checklist + +When reviewing event definitions, verify: + +1. Event name is past tense — records a fact, not a command or intention +2. Event name uses domain language, not CRUD or technical jargon +3. Event name is specific enough to understand without reading the payload +4. One event captures one thing that happened +5. Naming format (PascalCase, dot.delimited, etc.) is consistent across the system +6. Integration events are prefixed with bounded context or service name +7. Integration events use shared vocabulary, not internal jargon +8. Field names use domain language, no abbreviations +9. Identifiers are explicit (`order_id` not `id`) +10. Temporal fields use `_at` or `_on` suffix with past tense +11. Boolean fields use predicate form (`is_`, `has_`, `was_`) +12. Domain event payloads contain no computed or derived fields +13. Metadata fields are separated from domain fields +14. Field casing is consistent across the system + +## Output + +Provide: +- List of events reviewed with pass/fail per checklist item +- Suggested corrections for any violations +- Domain vs integration classification if not already clear +- Field naming corrections with rationale From 8c05d19f2648a295b6946186fd8e705cae19ae4e Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 30 Apr 2026 09:18:13 -0400 Subject: [PATCH 3/8] feat(trogonstack-eda): suggest avoiding Was in event names Signed-off-by: Yordis Prieto --- plugins/trogonstack-eda/skills/event-name/SKILL.md | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/trogonstack-eda/skills/event-name/SKILL.md b/plugins/trogonstack-eda/skills/event-name/SKILL.md index 2a25cfb..590075f 100644 --- a/plugins/trogonstack-eda/skills/event-name/SKILL.md +++ b/plugins/trogonstack-eda/skills/event-name/SKILL.md @@ -48,6 +48,7 @@ Events record something that already happened. Always use past tense. | `PaymentFailed` | `PaymentFailing` | Progressive — not a fact yet | | `ItemAddedToCart` | `AddItemToCart` | Imperative | | `SubscriptionRenewed` | `RenewSubscription` | Imperative | +| `OrderPlaced` | `OrderWasPlaced` | Redundant — events are already facts, `Was` adds nothing | ### 2. Domain Language — Not Technical Jargon From 1f1f1b8eaf2ada9781797c5040630dca097ad8f8 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 30 Apr 2026 09:19:29 -0400 Subject: [PATCH 4/8] feat(trogonstack-eda): prefer enums over booleans for future extensibility Signed-off-by: Yordis Prieto --- .../skills/event-name/SKILL.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/plugins/trogonstack-eda/skills/event-name/SKILL.md b/plugins/trogonstack-eda/skills/event-name/SKILL.md index 590075f..9b2d70c 100644 --- a/plugins/trogonstack-eda/skills/event-name/SKILL.md +++ b/plugins/trogonstack-eda/skills/event-name/SKILL.md @@ -162,13 +162,24 @@ Always suffix identifiers with what they reference. Never use bare `id`. Pick `_at` (for datetime with time) or `_on` (for date only) and be consistent. -### 12. Boolean Fields Use Predicate Form +### 12. Prefer Enums Over Booleans + +Booleans lock you into two states. Enums allow future additions without breaking changes. + +| Good (enum) | Bad (boolean) | Why bad | +|-------------|---------------|---------| +| `shipping_priority: "standard" \| "expedited"` | `is_expedited: true` | Can't add `"overnight"` later without a new field | +| `payment_status: "pending" \| "authorized" \| "declined"` | `is_authorized: true` | Can't represent `"pending"` or future states | +| `discount_type: "none" \| "percentage" \| "fixed"` | `has_discount: true` | Can't distinguish discount types | + +Use booleans only when the field is genuinely and permanently binary (e.g., `is_test_order`). + +When a boolean is unavoidable, use predicate form: | Good | Bad | |------|-----| -| `is_expedited` | `expedited` (ambiguous — could be a noun or adjective) | -| `has_discount` | `discount` (ambiguous — could be the amount) | -| `was_refunded` | `refunded` (ambiguous in some contexts) | +| `is_test_order` | `test_order` (ambiguous — could be a noun) | +| `is_gift` | `gift` (ambiguous — could be the gift itself) | ### 13. No Computed or Derived Fields in Domain Events From b9d150b095e4460eda50d6a8eb0860044a9750fe Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 30 Apr 2026 09:52:06 -0400 Subject: [PATCH 5/8] feat(trogonstack-eda): add What/Who/When principle for event completeness Signed-off-by: Yordis Prieto --- .../trogonstack-eda/skills/event-name/SKILL.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/plugins/trogonstack-eda/skills/event-name/SKILL.md b/plugins/trogonstack-eda/skills/event-name/SKILL.md index 9b2d70c..e0d3f24 100644 --- a/plugins/trogonstack-eda/skills/event-name/SKILL.md +++ b/plugins/trogonstack-eda/skills/event-name/SKILL.md @@ -129,6 +129,24 @@ Integration events form a public contract. Use terms that consumers across teams ## Field Naming Rules +### The What, Who, When Principle + +Every event should capture three things: + +- **What** happened — the event name itself (`OrderPlaced`, `ClaimApproved`) +- **Who** caused it — `_by` suffix fields (`placed_by`, `approved_by`) +- **When** it happened — `_at` suffix fields (`placed_at`, `approved_at`) + +``` +OrderPlaced ← What + placed_by: "customer-123" ← Who + placed_at: "2026-04-30T..." ← When + order_id: "order-456" + items: [...] +``` + +If an event is missing Who or When, ask whether it was intentional. System-initiated events may omit Who but should document that the actor is the system itself (e.g., `initiated_by: "scheduler"`). + ### 9. Domain Language for Fields Too Fields describe facts captured about the event. Use business language. From dbc56260e008124336cc71c76b43bda4fb36475c Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 30 Apr 2026 09:56:33 -0400 Subject: [PATCH 6/8] feat(trogonstack-eda): strongly recommend Who/When fields in events Signed-off-by: Yordis Prieto --- plugins/trogonstack-eda/skills/event-name/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/trogonstack-eda/skills/event-name/SKILL.md b/plugins/trogonstack-eda/skills/event-name/SKILL.md index e0d3f24..73618ac 100644 --- a/plugins/trogonstack-eda/skills/event-name/SKILL.md +++ b/plugins/trogonstack-eda/skills/event-name/SKILL.md @@ -145,7 +145,7 @@ OrderPlaced ← What items: [...] ``` -If an event is missing Who or When, ask whether it was intentional. System-initiated events may omit Who but should document that the actor is the system itself (e.g., `initiated_by: "scheduler"`). +**Strongly recommend including all three.** If an event is missing Who or When, flag it and suggest adding them. Events without attribution or timestamps lose forensic and audit value that is nearly impossible to recover later. System-initiated events should still capture the actor explicitly (e.g., `initiated_by: "scheduler"`). ### 9. Domain Language for Fields Too From f6d3a2afba62a2d2b5cfbe76f1081d24fb2d7ed0 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 30 Apr 2026 10:01:35 -0400 Subject: [PATCH 7/8] feat(trogonstack-eda): add rules for negatives, versioning, currency, and collections Signed-off-by: Yordis Prieto --- .../skills/event-name/SKILL.md | 70 ++++++++++++++++--- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/plugins/trogonstack-eda/skills/event-name/SKILL.md b/plugins/trogonstack-eda/skills/event-name/SKILL.md index 73618ac..7af23a2 100644 --- a/plugins/trogonstack-eda/skills/event-name/SKILL.md +++ b/plugins/trogonstack-eda/skills/event-name/SKILL.md @@ -104,6 +104,29 @@ Choose one format and apply it consistently across the entire system: Do not mix formats within a system. +### Avoid Negatives in Event Names + +Negative event names usually hide a positive event with a reason. Prefer the positive form with a field explaining the outcome. + +| Good | Bad | Why bad | +|------|-----|---------| +| `ShipmentFailed { reason: "address_invalid" }` | `OrderNotShipped` | Negative — what DID happen? | +| `PaymentDeclined { reason: "insufficient_funds" }` | `PaymentNotProcessed` | Negative — why not? | +| `ApplicationRejected { reason: "..." }` | `ApplicationNotApproved` | Double negative with `Approved` | + +If a negative sounds natural in the domain (e.g., `ClaimDenied`), that's fine — it's domain language, not a negation of another event. + +### Event Versioning — Keep It Out of the Name + +Do not embed version numbers in event names. Use `schema_version` in metadata instead. + +| Good | Bad | Why bad | +|------|-----|---------| +| `OrderPlaced` + `schema_version: 2` | `OrderPlacedV2` | Pollutes the name, couples consumers to version | +| `InvoiceIssued` + `schema_version: 3` | `InvoiceIssuedV3` | Every version change requires new consumer routing | + +Versioning belongs in the event envelope. The event name describes what happened — that doesn't change when you add a field to the payload. + ## Integration Event Naming — Additional Rules Integration events cross service boundaries. They carry additional constraints: @@ -228,7 +251,27 @@ Separate envelope/metadata from the business payload. Metadata fields describe t **Domain fields (payload):** Everything specific to the business fact: `order_id`, `customer_id`, `total_amount`, `items[]`, etc. -### 15. Consistent Casing for Fields +### 15. Monetary Amounts Must Include Currency + +A bare amount field is incomplete. Always pair with currency or use a composite money object. + +| Good | Bad | Why bad | +|------|-----|---------| +| `total_amount`, `total_currency` | `total` | Which currency? | +| `price: { amount: 1999, currency: "USD" }` | `price: 1999` | Ambiguous — cents? dollars? which currency? | +| `refund_amount`, `refund_currency` | `refund_amount` alone | Loses currency context across services | + +For domain events, capture the currency at the time of the fact — currencies can change between events. + +### 16. Collection Fields Use Plural, Scalars Use Singular + +| Good | Bad | Why bad | +|------|-----|---------| +| `items` (array) | `item` (for an array) | Misleading — suggests a single value | +| `line_items` (array) | `line_item_list` | Redundant suffix — plural already signals a collection | +| `shipping_address` (object) | `shipping_addresses` (for one) | Misleading — suggests multiple | + +### 17. Consistent Casing for Fields Pick one and apply it consistently: @@ -245,16 +288,21 @@ When reviewing event definitions, verify: 2. Event name uses domain language, not CRUD or technical jargon 3. Event name is specific enough to understand without reading the payload 4. One event captures one thing that happened -5. Naming format (PascalCase, dot.delimited, etc.) is consistent across the system -6. Integration events are prefixed with bounded context or service name -7. Integration events use shared vocabulary, not internal jargon -8. Field names use domain language, no abbreviations -9. Identifiers are explicit (`order_id` not `id`) -10. Temporal fields use `_at` or `_on` suffix with past tense -11. Boolean fields use predicate form (`is_`, `has_`, `was_`) -12. Domain event payloads contain no computed or derived fields -13. Metadata fields are separated from domain fields -14. Field casing is consistent across the system +5. No negatives in event names — use positive form with a reason field +6. No version numbers in event names — use `schema_version` in metadata +7. Naming format (PascalCase, dot.delimited, etc.) is consistent across the system +8. Integration events are prefixed with bounded context or service name +9. Integration events use shared vocabulary, not internal jargon +10. Event includes Who (`_by`) and When (`_at`) fields +11. Field names use domain language, no abbreviations +12. Identifiers are explicit (`order_id` not `id`) +13. Temporal fields use `_at` or `_on` suffix with past tense +14. Enums preferred over booleans; booleans use predicate form when unavoidable +15. Monetary amounts include currency +16. Collection fields are plural, scalar fields are singular +17. Domain event payloads contain no computed or derived fields +18. Metadata fields are separated from domain fields +19. Field casing is consistent across the system ## Output From c2ea27e16dac2e76f4da245929f8366fdf7a5061 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Thu, 30 Apr 2026 10:15:09 -0400 Subject: [PATCH 8/8] feat(trogonstack-eda): add rules for redundant suffixes, infrastructure names, polymorphic payloads, and PII Signed-off-by: Yordis Prieto --- .../skills/event-name/SKILL.md | 81 +++++++++++++++---- 1 file changed, 65 insertions(+), 16 deletions(-) diff --git a/plugins/trogonstack-eda/skills/event-name/SKILL.md b/plugins/trogonstack-eda/skills/event-name/SKILL.md index 7af23a2..85d145a 100644 --- a/plugins/trogonstack-eda/skills/event-name/SKILL.md +++ b/plugins/trogonstack-eda/skills/event-name/SKILL.md @@ -104,6 +104,26 @@ Choose one format and apply it consistently across the entire system: Do not mix formats within a system. +### No Redundant Suffixes — "Event", "Message", "Notification" + +The event name describes what happened. It is already an event by context — appending `Event`, `Message`, or `Notification` adds nothing. + +| Good | Bad | Why bad | +|------|-----|---------| +| `OrderPlaced` | `OrderPlacedEvent` | Redundant — it's already an event | +| `PaymentFailed` | `PaymentFailedMessage` | "Message" is a delivery mechanism, not a domain concept | +| `ShipmentDispatched` | `ShipmentDispatchedNotification` | "Notification" is a side effect, not the fact itself | + +### No Infrastructure or Technology in Names + +Event names must survive technology migrations. Do not embed broker names, protocols, or infrastructure details. + +| Good | Bad | Why bad | +|------|-----|---------| +| `OrderPlaced` | `KafkaOrderPlaced` | Coupled to Kafka — what happens when you migrate? | +| `PaymentCompleted` | `SQSPaymentCompleted` | Coupled to SQS | +| `UserRegistered` | `RabbitMQUserRegisteredMessage` | Infrastructure + redundant suffix | + ### Avoid Negatives in Event Names Negative event names usually hide a positive event with a reason. Prefer the positive form with a field explaining the outcome. @@ -271,7 +291,32 @@ For domain events, capture the currency at the time of the fact — currencies c | `line_items` (array) | `line_item_list` | Redundant suffix — plural already signals a collection | | `shipping_address` (object) | `shipping_addresses` (for one) | Misleading — suggests multiple | -### 17. Consistent Casing for Fields +### 17. No Polymorphic Payloads + +An event whose payload shape changes based on a `type` or `kind` field is really multiple events. Split them. + +| Good | Bad | Why bad | +|------|-----|---------| +| `PaymentAuthorized`, `PaymentDeclined` | `PaymentProcessed { result_type: "authorized" \| "declined" }` with different fields per type | Consumers must branch on `result_type` to know the shape — fragile, hard to evolve independently | +| `ItemBackordered`, `ItemReserved` | `InventoryChecked { outcome: "backordered" \| "reserved" }` with different fields | Two different facts forced into one event | + +If every instance of the event has the same fields regardless of a status value, that's fine — it's not polymorphic, it's a field with valid values. + +### 18. PII and Sensitive Data — Use Indirection + +Events are immutable. PII stored directly in events is nearly impossible to delete (GDPR right to erasure, CCPA). Reference sensitive data by ID instead of inlining it. + +| Good | Bad | Why bad | +|------|-----|---------| +| `customer_id: "cust-123"` | `customer_email: "alice@example.com"` | Can't erase the email from an immutable event | +| `shipping_address_id: "addr-456"` | `shipping_address: { street: "...", city: "..." }` | Address baked into immutable history | +| `payment_method_id: "pm-789"` | `card_number: "4111..."` | Sensitive financial data in an immutable log | + +Store PII in a mutable store keyed by ID. Events reference the ID. When deletion is requested, delete from the mutable store — events remain intact without leaking personal data. + +Flag any PII found directly in event payloads and suggest replacing with an identifier reference. + +### 19. Consistent Casing for Fields Pick one and apply it consistently: @@ -288,21 +333,25 @@ When reviewing event definitions, verify: 2. Event name uses domain language, not CRUD or technical jargon 3. Event name is specific enough to understand without reading the payload 4. One event captures one thing that happened -5. No negatives in event names — use positive form with a reason field -6. No version numbers in event names — use `schema_version` in metadata -7. Naming format (PascalCase, dot.delimited, etc.) is consistent across the system -8. Integration events are prefixed with bounded context or service name -9. Integration events use shared vocabulary, not internal jargon -10. Event includes Who (`_by`) and When (`_at`) fields -11. Field names use domain language, no abbreviations -12. Identifiers are explicit (`order_id` not `id`) -13. Temporal fields use `_at` or `_on` suffix with past tense -14. Enums preferred over booleans; booleans use predicate form when unavoidable -15. Monetary amounts include currency -16. Collection fields are plural, scalar fields are singular -17. Domain event payloads contain no computed or derived fields -18. Metadata fields are separated from domain fields -19. Field casing is consistent across the system +5. No redundant suffixes (`Event`, `Message`, `Notification`) +6. No infrastructure or technology in event names +7. No negatives in event names — use positive form with a reason field +8. No version numbers in event names — use `schema_version` in metadata +9. Naming format (PascalCase, dot.delimited, etc.) is consistent across the system +10. Integration events are prefixed with bounded context or service name +11. Integration events use shared vocabulary, not internal jargon +12. Event includes Who (`_by`) and When (`_at`) fields +13. Field names use domain language, no abbreviations +14. Identifiers are explicit (`order_id` not `id`) +15. Temporal fields use `_at` or `_on` suffix with past tense +16. Enums preferred over booleans; booleans use predicate form when unavoidable +17. No polymorphic payloads — split into separate events +18. No PII directly in payloads — use identifier references +19. Monetary amounts include currency +20. Collection fields are plural, scalar fields are singular +21. Domain event payloads contain no computed or derived fields +22. Metadata fields are separated from domain fields +23. Field casing is consistent across the system ## Output