{"id":50762661,"url":"https://github.com/mcking-07/eventing-framework","last_synced_at":"2026-06-11T11:30:34.700Z","repository":{"id":360069283,"uuid":"1247911796","full_name":"mcking-07/eventing-framework","owner":"mcking-07","description":"an opinionated event-driven framework for building asynchronous systems on aws.","archived":false,"fork":false,"pushed_at":"2026-05-24T20:33:44.000Z","size":66,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-24T22:22:09.144Z","etag":null,"topics":["ecs-framework","event-driven-architecture","microservice-framework"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mcking-07.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-24T00:15:30.000Z","updated_at":"2026-05-24T20:33:48.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mcking-07/eventing-framework","commit_stats":null,"previous_names":["mcking-07/eventing-framework"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/mcking-07/eventing-framework","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcking-07%2Feventing-framework","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcking-07%2Feventing-framework/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcking-07%2Feventing-framework/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcking-07%2Feventing-framework/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mcking-07","download_url":"https://codeload.github.com/mcking-07/eventing-framework/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcking-07%2Feventing-framework/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34197393,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ecs-framework","event-driven-architecture","microservice-framework"],"created_at":"2026-06-11T11:30:34.010Z","updated_at":"2026-06-11T11:30:34.693Z","avatar_url":"https://github.com/mcking-07.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# eventing-framework\n\n[![ci](https://github.com/mcking-07/eventing-framework/workflows/publish/badge.svg)](https://github.com/mcking-07/eventing-framework/actions)\n[![npm version](https://img.shields.io/npm/v/eventing-framework.svg)](https://www.npmjs.com/package/eventing-framework)\n[![license](https://img.shields.io/npm/l/eventing-framework.svg)](https://github.com/mcking-07/eventing-framework/blob/main/LICENSE)\n[![aws](https://img.shields.io/badge/aws-SNS%20%7C%20SQS%20%7C%20S3-orange?logo=amazonwebservices\u0026logoColor=white)](https://aws.amazon.com)\n\nan opinionated event-driven framework for building asynchronous systems on amazon web services.\n\neventing-framework connects services through a typed event bus built on sns topics, sqs queues, and s3 storage. define domain events, run handlers when they arrive, and emit new events from anywhere in your code. the framework handles publishing, polling, oversized payloads, and s3 reference resolution behind the scenes.\n\n- typed domain events with per-service base classes\n- transparent oversized payload routing (inline or s3 reference)\n- error isolation - a failing handler doesn't block the rest of the batch\n- zero dependencies beyond the aws sdk\n\neventing-framework lets your services communicate without coupling. publish what happened. subscribe to what matters.\n\n## installation\n\n```sh\nnpm install eventing-framework\n```\n\nrequires node.js 20 or later.\n\n## usage\n\n### defining events\n\nextend `DomainEvent` to define your events. use an intermediate base class to inject per-service metadata once.\n\n```typescript\nimport { DomainEvent } from 'eventing-framework';\n\nclass OrderEvent\u003cPayloadType = Record\u003cstring, unknown\u003e\u003e extends DomainEvent {\n  constructor(name: string, payload: PayloadType) {\n    super(name, { ...payload, app: 'order-service', category: 'order' });\n  }\n}\n\nclass OrderPlaced extends OrderEvent\u003c{ id: string; total: number }\u003e {\n  constructor(payload: { id: string; total: number }) {\n    super('OrderPlaced', payload);\n  }\n}\n\nclass OrderProcessed extends OrderEvent\u003c{ id: string }\u003e {\n  constructor(payload: { id: string }) {\n    super('OrderProcessed', payload);\n  }\n}\n```\n\n### configuring\n\nconfigure only what your service needs, topic and queue for the publishing side, queue and storage for the consuming side.\n\n```typescript\nimport { Application } from 'eventing-framework';\n\nconst app = new Application({\n  name: 'order-service',\n  environment: 'development',\n  topic: {\n    arn: 'arn:aws:sns:us-east-1:123456789012:order-events',\n    config: { region: 'us-east-1' },\n  },\n  queue: {\n    url: 'https://sqs.us-east-1.amazonaws.com/123456789012/notification-queue',\n    config: { region: 'us-east-1' },\n    params: { MessageAttributeNames: ['All'], WaitTimeSeconds: 5 },\n  },\n  storage: {\n    bucket: 'eventing-reference-bucket',\n    config: { region: 'us-east-1' },\n  },\n  scheduler: { interval: 5_000 },\n});\n```\n\n### consuming events\n\nservices consume events published by other services. the framework polls sqs, resolves the payload, and routes it to your handler.\n\nuse a typed event map for auto-typed payloads.\n\n```typescript\ntype AppEvents = {\n  OrderPlaced: { id: string; total: number };\n  OrderProcessed: { id: string };\n};\n\nconst app = new Application\u003cAppEvents\u003e({ ... });\napp.on('OrderPlaced', async (payload) =\u003e {\n  console.log(`order ${payload.id} for $${payload.total}`);\n});\n```\n\nor use an explicit generic on a bare `Application()`.\n\n```typescript\nconst app = new Application({ ... });\napp.on\u003c{ id: string; total: number }\u003e('OrderPlaced', async (payload) =\u003e {\n  console.log(`order ${payload.id} for $${payload.total}`);\n});\n```\n\n### publishing events\n\nregister the event, then emit from anywhere in your process.\n\n```typescript\nimport { EventPublisher } from 'eventing-framework';\n\napp.register('OrderProcessed');\nEventPublisher.emit(new OrderProcessed({ id: '123' }));\n```\n\n### chaining events\n\na handler can emit the next event in the flow, the chain continues to downstream services without coordination.\n\n```typescript\napp.on('OrderPlaced', async (payload) =\u003e {\n  await processOrder(payload);\n  EventPublisher.emit(new OrderProcessed({ id: payload.id }));\n});\n```\n\n### starting and stopping\n\n```typescript\nawait app.start();  // begins polling on the configured interval\nawait app.stop();   // stops the scheduler, clears handlers and registrations\n```\n\n## examples\n\ntwo working examples against localstack, no aws account needed. see [examples](examples/) for more details.\n\n- **[single event](examples/simple/)** - a publisher and a consumer communicating through a single event type\n- **[multi-step workflow](examples/advanced/)** - four services chained across three event types, with oversized payloads and error handling\n\n## design decisions\n\n### why sns + sqs + s3\n\nsns handles fan-out to multiple queues. sqs provides at-least-once delivery with visibility timeouts for retry. s3 stores payloads that exceed sns's 256kb message size limit, the framework routes oversized payloads transparently, consumers resolve references without knowing the difference.\n\n### why EventPublisher is static\n\nevents come from handlers, webhooks, scheduled jobs - anywhere. a static emitter avoids threading an `Application` reference through every layer of your code. `EventPublisher.emit()` fires locally, `register()` catches it and publishes to sns.\n\n### why register() and on() are separate\n\n`register()` controls what leaves the service. `on()` controls what enters. the boundary is explicit; consume without publishing, publish without consuming, or both.\n\nerrors in handlers are isolated, one failing handler doesn't block the rest of the batch. the failed message stays in sqs for retry.\n\n## api reference\n\n### Application\n\n```typescript\nclass Application\u003cEvents extends Record\u003cstring, unknown\u003e\u003e {\n  constructor(config: ApplicationConfig)\n  register(event: string): void\n  on\u003cPayloadType\u003e(event: string, handler: (payload: PayloadType) =\u003e void | Promise\u003cvoid\u003e): void\n  on\u003cEvent extends keyof Events\u003e(event: Event, handler: (payload: Events[Event]) =\u003e void | Promise\u003cvoid\u003e): void\n  start(): Promise\u003cvoid\u003e\n  stop(): Promise\u003cvoid\u003e\n}\n```\n\n### ApplicationConfig\n\n```typescript\ntype ApplicationConfig = {\n  name: string\n  environment: 'development' | 'staging' | 'production'\n\n  topic?: {\n    arn: string\n    config: SNSClientConfig\n    params?: Omit\u003cPublishCommandInput, 'TopicArn' | 'Message'\u003e\n  }\n\n  queue?: {\n    url: string\n    config: SQSClientConfig\n    params?: Omit\u003cReceiveMessageCommandInput, 'QueueUrl'\u003e\n  }\n\n  storage?: {\n    bucket: string\n    config: S3ClientConfig\n  }\n\n  scheduler?: {\n    interval: number\n  }\n}\n```\n\nall service configs are optional, configure only what your service needs.\n\n### DomainEvent\n\n```typescript\nclass DomainEvent\u003cPayloadType\u003e {\n  readonly id: string        // uuid v7\n  readonly name: string      // event name\n  readonly payload: PayloadType\n  readonly timestamp: number // epoch ms\n}\n```\n\nextend `DomainEvent` to define your events. use an intermediate base class to inject per-service metadata (`app`, `category`) once.\n\n### EventPublisher\n\n```typescript\nclass EventPublisher {\n  static emit\u003cEventType extends DomainEvent\u003e(event: EventType): void\n}\n```\n\nemits a domain event to local listeners registered via `Application.register()`. can be called from anywhere in your process.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcking-07%2Feventing-framework","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmcking-07%2Feventing-framework","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcking-07%2Feventing-framework/lists"}