create-event-handlers

Sets up RabbitMQ event publishers and consumers following ModuleImplementationGuide.md Section 9. RabbitMQ only (no Azure Service Bus). Creates publishers with DomainEvent (tenantId preferred), consumers with handlers, naming {domain}.{entity}.{action}, required fields (id, type, version, timestamp, tenantId, source, data). Use when adding event-driven communication, async workflows, or integrating via events.

16 stars

Best use case

create-event-handlers is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Sets up RabbitMQ event publishers and consumers following ModuleImplementationGuide.md Section 9. RabbitMQ only (no Azure Service Bus). Creates publishers with DomainEvent (tenantId preferred), consumers with handlers, naming {domain}.{entity}.{action}, required fields (id, type, version, timestamp, tenantId, source, data). Use when adding event-driven communication, async workflows, or integrating via events.

Teams using create-event-handlers should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/create-event-handlers/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/data-ai/create-event-handlers/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/create-event-handlers/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How create-event-handlers Compares

Feature / Agentcreate-event-handlersStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Sets up RabbitMQ event publishers and consumers following ModuleImplementationGuide.md Section 9. RabbitMQ only (no Azure Service Bus). Creates publishers with DomainEvent (tenantId preferred), consumers with handlers, naming {domain}.{entity}.{action}, required fields (id, type, version, timestamp, tenantId, source, data). Use when adding event-driven communication, async workflows, or integrating via events.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Create Event Handlers

Sets up RabbitMQ event publishers and consumers following ModuleImplementationGuide.md Section 9.

## Event Naming Convention

Reference: ModuleImplementationGuide.md Section 9.1

Format: `{domain}.{entity}.{action}`

✅ **Correct:**
- `user.created`
- `auth.login.success`
- `notification.email.sent`
- `secret.rotated`

❌ **Wrong:**
- `userCreated`
- `loginSuccess`
- `emailSent`

**Standard Actions:**
- `created`, `updated`, `deleted`
- `started`, `completed`, `failed`
- `sent`, `received`, `expired`, `rotated`

## Event Structure

Reference: ModuleImplementationGuide.md Section 9.2

```typescript
interface DomainEvent<T = unknown> {
  // Identity
  id: string;                    // Unique event ID (UUID)
  type: string;                  // Event type (domain.entity.action)
  
  // Metadata
  timestamp: string;             // ISO 8601
  version: string;               // Event schema version
  source: string;                // Module that emitted
  correlationId?: string;        // Request correlation
  
  // Context
  tenantId?: string;             // Tenant context (PREFERRED; use for new modules)
  organizationId?: string;       // DEPRECATED for new modules; prefer tenantId
  userId?: string;               // Actor
  
  // Payload
  data: T;                       // Event-specific data
}
```

## Event Publisher

Reference: containers/auth/src/events/publishers/AuthEventPublisher.ts

### src/events/publishers/[Module]EventPublisher.ts

```typescript
import { randomUUID } from 'crypto';
import { EventPublisher, getChannel, closeConnection } from '@coder/shared';
import { log } from '../../utils/logger';
import { getConfig } from '../../config';

let publisher: EventPublisher | null = null;

export async function initializeEventPublisher(): Promise<void> {
  if (publisher) return;
  
  const config = getConfig();
  if (!config.rabbitmq?.url) {
    log.warn('RabbitMQ URL not configured, events will not be published');
    return;
  }
  
  try {
    await getChannel();
    publisher = new EventPublisher(config.rabbitmq.exchange || 'coder_events');
    log.info('Event publisher initialized', { exchange: config.rabbitmq.exchange });
  } catch (error: any) {
    log.error('Failed to initialize event publisher', error);
  }
}

export async function closeEventPublisher(): Promise<void> {
  try {
    await closeConnection();
    publisher = null;
  } catch (error: any) {
    log.error('Error closing event publisher', error);
  }
}

function getPublisher(): EventPublisher | null {
  if (!publisher) {
    const config = getConfig();
    publisher = new EventPublisher(config.rabbitmq.exchange || 'coder_events');
  }
  return publisher;
}

export function createBaseEvent(
  type: string,
  userId?: string,
  tenantId?: string,
  correlationId?: string,
  data?: any
) {
  return {
    id: randomUUID(),
    type,
    timestamp: new Date().toISOString(),
    version: '1.0',
    source: '[module-name]',
    correlationId,
    tenantId,
    userId,
    data: data || {},
  };
}

export async function publishEvent(event: any, routingKey?: string): Promise<void> {
  const pub = getPublisher();
  if (!pub) {
    log.warn('Event publisher not initialized, skipping event', { type: event.type });
    return;
  }
  
  try {
    await pub.publish(routingKey || event.type, event);
    log.debug('Event published', { type: event.type, id: event.id });
  } catch (error: any) {
    log.error('Failed to publish event', error, { type: event.type });
  }
}
```

### Usage in Services

```typescript
import { publishEvent, createBaseEvent } from '../events/publishers/ModuleEventPublisher';

// Publish event (tenantId for tenant context)
const event = createBaseEvent(
  'resource.created',
  userId,
  tenantId,
  correlationId,
  {
    resourceId: resource.id,
    name: resource.name,
  }
);

await publishEvent(event);
```

## Event Consumer

Reference: ModuleImplementationGuide.md Section 9.4

### src/events/consumers/[Resource]Consumer.ts

```typescript
import { EventConsumer } from '@coder/shared';
import { log } from '../../utils/logger';
import { getConfig } from '../../config';

let consumer: EventConsumer | null = null;

export async function initializeEventConsumer(): Promise<void> {
  if (consumer) return;
  
  const config = getConfig();
  if (!config.rabbitmq?.url) {
    log.warn('RabbitMQ URL not configured, events will not be consumed');
    return;
  }
  
  try {
    consumer = new EventConsumer({
      queue: config.rabbitmq.queue || '[module-name]_service',
      exchange: config.rabbitmq.exchange || 'coder_events',
      bindings: config.rabbitmq.bindings || [],
    });
    
    // Register handlers
    consumer.on('other.resource.created', handleResourceCreated);
    consumer.on('other.resource.updated', handleResourceUpdated);
    
    await consumer.start();
    log.info('Event consumer initialized', { queue: config.rabbitmq.queue });
  } catch (error: any) {
    log.error('Failed to initialize event consumer', error);
  }
}

async function handleResourceCreated(event: any): Promise<void> {
  log.info('Resource created event received', { resourceId: event.data.resourceId });
  // Handle the event
}

async function handleResourceUpdated(event: any): Promise<void> {
  log.info('Resource updated event received', { resourceId: event.data.resourceId });
  // Handle the event
}

export async function closeEventConsumer(): Promise<void> {
  try {
    if (consumer) {
      await consumer.stop();
      consumer = null;
    }
  } catch (error: any) {
    log.error('Error closing event consumer', error);
  }
}
```

## Event Documentation

Reference: ModuleImplementationGuide.md Section 9.5

### logs-events.md (if events are logged)

Create in module root if module publishes events that get logged:

```markdown
# [Module Name] - Logs Events

## Published Events

### {domain}.{entity}.{action}

**Description**: When this event is triggered.

**Triggered When**: 
- Condition 1
- Condition 2

**Event Type**: `{domain}.{entity}.{action}`

**Event Schema**:

\`\`\`json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": ["id", "type", "timestamp", "version", "source", "data"],
  "properties": {
    "id": { "type": "string", "format": "uuid" },
    "type": { "type": "string" },
    "timestamp": { "type": "string", "format": "date-time" },
    "version": { "type": "string" },
    "source": { "type": "string" },
    "tenantId": { "type": "string", "format": "uuid" },
    "userId": { "type": "string", "format": "uuid" },
    "data": {
      "type": "object",
      "properties": {
        "resourceId": { "type": "string" }
      }
    }
  }
}
\`\`\`
```

### notifications-events.md (if events trigger notifications)

Create in module root if module publishes events that trigger notifications.

## Configuration

Add to config/default.yaml:

```yaml
rabbitmq:
  url: ${RABBITMQ_URL}
  exchange: coder_events
  queue: [module-name]_service
  bindings:
    - "other.resource.created"
    - "other.resource.updated"
```

## Checklist

- [ ] Event publisher created following pattern
- [ ] Event consumer created (if consuming events)
- [ ] Events follow naming convention: {domain}.{entity}.{action}
- [ ] Events include all required fields (id, type, version, timestamp, source, data)
- [ ] Events include tenantId when applicable (RabbitMQ only; no Azure Service Bus)
- [ ] logs-events.md created (if events are logged)
- [ ] notifications-events.md created (if events trigger notifications)
- [ ] RabbitMQ config added to default.yaml

Related Skills

kitt-create-slash-commands

16
from diegosouzapw/awesome-omni-skill

Expert guidance for creating slash commands. Use when working with slash commands, creating custom commands, understanding command structure, or learning YAML configuration.

kitt-create-plans

16
from diegosouzapw/awesome-omni-skill

Create hierarchical project plans optimized for solo agentic development. Use when planning projects, phases, or tasks that the AI agent will execute. Produces agent-executable plans with verification criteria, not enterprise documentation. Handles briefs, roadmaps, phase plans, and context handoffs.

event-driven

16
from diegosouzapw/awesome-omni-skill

Event-driven architecture patterns including message queues, pub/sub, event sourcing, CQRS, and sagas. Use when implementing async messaging, distributed transactions, event stores, command query separation, domain events, integration events, data streaming, choreography, orchestration, or integrating with RabbitMQ, Kafka, Apache Pulsar, AWS SQS, AWS SNS, NATS, event buses, or message brokers.

create-workflow

16
from diegosouzapw/awesome-omni-skill

Create Jazz workflow automation files (WORKFLOW.md). Use this for scheduling Jazz agents to run recurring tasks. For OS-level scripts/commands, use create-system-routine.

create-rule

16
from diegosouzapw/awesome-omni-skill

Create persistent AI agent rules and instructions. Use when you want to create a rule, add coding standards, set up project conventions, configure file-specific patterns, or create AGENTS.md/GEMINI.md rule files across Cursor, Gemini CLI, or Codex.

create-prompt

16
from diegosouzapw/awesome-omni-skill

Expert prompt engineering for creating effective prompts for Claude, GPT, and other LLMs. Use when writing system prompts, user prompts, few-shot examples, or optimizing existing prompts for better performance.

create-expert-skill

16
from diegosouzapw/awesome-omni-skill

Create production-ready skills from expert knowledge. Extracts domain expertise and system ontologies, uses scripts for deterministic work, loads knowledge progressively. Use when building skills that must work reliably in production.

create-custom-prompt

16
from diegosouzapw/awesome-omni-skill

Prompt for creating custom prompt files

create-assistant

16
from diegosouzapw/awesome-omni-skill

Create and configure Vapi voice AI assistants with models, voices, transcribers, tools, hooks, and advanced settings. Use when building voice agents, phone bots, customer support assistants, or any conversational AI that handles phone or web calls.

create-agents-md

16
from diegosouzapw/awesome-omni-skill

Create or rewrite AGENTS.md files for Open Mercato packages and modules. Use this skill when adding a new package, creating a new module, or when an existing AGENTS.md needs to be created or refactored. Ensures prescriptive tone, MUST rules, checklists, and consistent structure across all agent guidelines.

create-agent-with-sanity-context

16
from diegosouzapw/awesome-omni-skill

Build AI agents with structured access to Sanity content via Context MCP. Covers Studio setup, agent implementation, and advanced patterns like client-side tools and custom rendering.

awesome-copilot-root-typespec-create-agent

16
from diegosouzapw/awesome-omni-skill

Generate a complete TypeSpec declarative agent with instructions, capabilities, and conversation starters for Microsoft 365 Copilot Use when: the task directly matches typespec create agent responsibilities within plugin awesome-copilot-root. Do not use when: a more specific framework or task-focused skill is clearly a better match.