Summary
Extend the opt-in emitEvents capability (added for TypeScript in #79 / PR #80) to the Python (FastAPI + SQLAlchemy) generator, so that opted-in entities produce a durable domain-event spine written in the same transaction as the state change.
Today emitEvents is parsed and honored only by the TypeScript generator; the Python generator inherits the base no-op (generateDomainEvents returns []), so emitEvents is silently ignored for Python projects.
Reference
#79 / PR #80 is the reference spec. The TypeScript implementation generates (into autogen/events/): a DomainEvent entity (events table), an EntitySubscriber writing events transactionally, an app-overridable DomainEventMapper (event-type taxonomy + payload), a DomainEventRelay (drain/retry; publish() is the app-supplied delivery hook), and a @Global() module wired into the index barrel. Flag resolution is entity.emitEvents ?? <top-level emitEvents> ?? false (global default with per-entity opt-out). Keep the same naming and the same extension-point boundaries.
Language-specific mechanism
The durability lever is ORM-specific. For SQLAlchemy:
- Use session events (e.g.
before_flush, or SessionEvents) to write the event row into the same session / unit of work as the state change, so it commits atomically. (after_commit is NOT transactional — avoid it for the durable write.)
- Map SQLAlchemy ORM insert/update/delete to
created/updated/removed.
- Recursion guard: never emit for the
DomainEvent model itself.
- Scope emission to the opted-in entities only.
- Provide the mapper + relay as overridable extension points (delivery left to the app); a
DomainEvent model + events table; relay drains pending rows.
Scope / notes
Environment
Summary
Extend the opt-in
emitEventscapability (added for TypeScript in #79 / PR #80) to the Python (FastAPI + SQLAlchemy) generator, so that opted-in entities produce a durable domain-event spine written in the same transaction as the state change.Today
emitEventsis parsed and honored only by the TypeScript generator; the Python generator inherits the base no-op (generateDomainEventsreturns[]), soemitEventsis silently ignored for Python projects.Reference
#79 / PR #80 is the reference spec. The TypeScript implementation generates (into
autogen/events/): aDomainEvententity (eventstable), an EntitySubscriber writing events transactionally, an app-overridableDomainEventMapper(event-type taxonomy + payload), aDomainEventRelay(drain/retry;publish()is the app-supplied delivery hook), and a@Global()module wired into the index barrel. Flag resolution isentity.emitEvents ?? <top-level emitEvents> ?? false(global default with per-entity opt-out). Keep the same naming and the same extension-point boundaries.Language-specific mechanism
The durability lever is ORM-specific. For SQLAlchemy:
before_flush, orSessionEvents) to write the event row into the same session / unit of work as the state change, so it commits atomically. (after_commitis NOT transactional — avoid it for the durable write.)created/updated/removed.DomainEventmodel itself.DomainEventmodel +eventstable; relay drains pending rows.Scope / notes
Environment
@apso/cli— language: python (FastAPI + SQLAlchemy)