# Event

> Domain Events

- Skill: `harshamendu/event` (Agent Skill, multi-file: 11 files)
- Install (CLI): `npx skillmds@latest add harshamendu/event`
- Raw SKILL.md: https://api.skillmd.com/api/skills/harshamendu/event/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Harshamendu (https://skillmd.com/u/harshamendu)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/harshamendu/event

---

# Domain Events

> **📝 Note:** This guide uses generic placeholder names to be reusable across any Spring Boot microservice.
> Replace with your actual implementation:
> - `{YourService}` → Your service name (e.g., `OrderService`, `PaymentService`)
> - `BusinessService` → Your core service (e.g., `OrderService`, `UserService`)
> - `DataService` → Your data processing service (e.g., `PaymentService`, `InventoryService`)
> - `IntegrationService` → Your external integration (e.g., `PaymentGatewayService`)
> - `{RequestType}` → Your request DTO (e.g., `CreateOrderRequest`)
> - `{ResponseType}` → Your response DTO (e.g., `OrderResponse`)


## Purpose
Provides domain event classes for asynchronous communication with external systems via AWS SQS message queues. Events represent significant business occurrences published to downstream consumers.

## Package Structure
```
event/
└── DomainEvent.java    # workflow activation notification event
```

## Key Concepts

### Domain Event Pattern
Events represent business facts that have occurred (e.g., workflow activation).

### Event-Driven Architecture
- Decouples producers from consumers through asynchronous messaging
- Uses AWS SQS FIFO queues for reliable, ordered delivery
- Ensures exactly-once delivery per message group
- Idempotent publishing prevents duplicate notifications

### Event Schema: DomainEvent

| Field | Type | Description |
|-------|------|-------------|
| `account_id` | UUID | AMC account identifier |
| `mvpd` | String | external provider name (lowercase) |
| `adobe_id` | String | Adobe Audience Manager person ID |
| `household_id` | String | Adobe household identifier |
| `activated_at` | String | ISO-8601 UTC timestamp |
| `channel_id` | String | MVPD channel identifier |

Example JSON:
```json
{
  "account_id": "550e8400-e29b-41d4-a716-446655440000",
  "mvpd": "spectrum",
  "adobe_id": "adobe-person-id",
  "household_id": "household-id",
  "activated_at": "2025-06-02T14:30:45.123Z",
  "channel_id": "amcplus"
}
```

## Implementation Patterns

### Builder Pattern
Events use static nested builder for fluent creation:
```java
DomainEvent event = DomainEvent.MvpdActivationEventBuilder.builder()
    .withAccountId(accountId)
    .withMvpd("spectrum")
    .withAdobeId("adobe-id-123")
    .withActivatedAt("2025-06-02T14:30:45.123Z")
    .build();
```

### Idempotent Publishing
Database-backed deduplication prevents duplicate events:
```java
if (mvpdActivationHistoryRepository.existsByAccountIdAndMvpd(accountId, mvpd)) {
    return;  // Already notified
}
sqsPublisher.publishEventToFifoQueue(event, messageGroupId);
```

### FIFO Message Grouping
Message group ID ensures ordering per entity:
```java
var messageGroupId = accountId + "." + mvpd;
sqsPublisher.publishEventToFifoQueue(event, messageGroupId);
```

### UTC Timestamp Normalization
Always use UTC for consistency:
```java
var activatedAt = ZonedDateTime.now(ZoneId.of("UTC"))
    .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX"));
```

## Quick Start

1. **Create Event**
   ```java
   DomainEvent event = MvpdActivationEventBuilder.builder()
       .withAccountId(accountId)
       .withMvpd(mvpd)
       .withActivatedAt(timestamp)
       .build();
   ```

2. **Check Idempotency**
   ```java
   if (repository.existsByAccountIdAndMvpd(accountId, mvpd)) {
       return;
   }
   ```

3. **Publish to SQS**
   ```java
   var messageGroupId = accountId + "." + mvpd;
   sqsPublisher.publishEventToFifoQueue(event, messageGroupId);
   ```

## Testing Strategy
- **Unit tests:** Verify event creation, idempotency, message grouping
- **Integration tests:** Use Testcontainers with LocalStack for SQS
- **Mock:** Repository and SQS publisher in unit tests
- **Verify:** Message group IDs, event fields, idempotency behavior

## Configuration
```properties
mvpd.activate.sqs.queue.url=${MVPD_ACTIVATE_SQS_QUEUE_URL}
```

## Documentation

### Guides
- **[Architecture](guides/architecture.md)** - Event patterns and flow diagrams
- **[Best Practices](guides/best-practices.md)** - Builder pattern, idempotency, FIFO ordering
- **[Anti-Patterns](guides/anti-patterns.md)** - Common mistakes to avoid
- **[Testing](guides/testing.md)** - Unit and integration testing strategies
- **[SQS Configuration](guides/sqs-configuration.md)** - Queue setup and monitoring

### Examples
- **[DomainEvent.java](examples/DomainEvent.java)** - Event class with builder
- **[EventPublishingServiceExample.java](examples/EventPublishingServiceExample.java)** - Publishing service
- **[EventPublishingServiceExampleTest.java](examples/EventPublishingServiceExampleTest.java)** - Unit tests
- **[EventProcessingServiceExample.java](examples/EventProcessingServiceExample.java)** - Service integration

## Code Formatting
All Java code formatted using Spotless with Google Java Format (AOSP style):
```bash
./gradlew spotlessApply
```

