Introduction
@frame/events is the centralized repository of integration events for the FRAME ecosystem. Every event that crosses a domain module boundary lives here — never inside the module that emits it.
Why this package exists
Section titled “Why this package exists”Domain modules in apps/nest must not import one another directly. They still need to communicate sometimes: the users module needs to notify the notifications module when a user registers.
Without @frame/events, the only alternative would be for notifications to import from users — a direct coupling that makes it impossible to extract modules as independent microservices.
graph LR
A[UsersModule] -- "UserRegisteredEvent" --> B[@frame/events]
B -- "typed contract" --> C[NotificationsModule]
B -- "typed contract" --> D[AdminModule]
style B fill:#336699,color:#fff,stroke:#225588
@frame/events acts as the neutral layer between modules: the emitter publishes a contract, the consumer subscribes to that contract — neither knows the other’s implementation.
Domain vs Integration
Section titled “Domain vs Integration”Not every event belongs here. The distinction is clear:
| Type | Where it lives | Who consumes it |
|---|---|---|
| Domain | src/modules/<mod>/v1/domain/events/ | Only within the same module |
| Integration | @frame/events/src/<domain>/ | Any module or microservice |
Role in the microservices migration
Section titled “Role in the microservices migration”This package is the foundation that enables migration without a rewrite. When a module is extracted as a microservice, the event contracts already exist and are framework-agnostic. Only the EventBus transport changes (in-process → RabbitMQ/Kafka).
See Event Architecture for the contract structure that supports that path.
Dependencies
Section titled “Dependencies”The package has a single runtime dependency: zod. It does not depend on NestJS, @nestjs/cqrs, or any other framework — the contracts are portable to any context.