docs: the events are emitted by the API, and what that implies
Recording where the work lands and the two facts about that codebase that shape it: every write passes through one BaseModelViewSet, so one hook covers every domain; and the models are managed = False, so a transactional outbox introduces the estate's first Django-managed table beside the pre-existing schema. Emission belongs in the API rather than a database CDC or a gateway plugin, because only the API knows which change is a business event and only it holds the sub/azp that the event must carry for a downstream agent to act under the same authority. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -96,15 +96,38 @@ policy must not need to know the trigger mode.
|
||||
|
||||
## Backlog
|
||||
|
||||
**Events over Kafka.** The target for the reactive half. The estate emits no
|
||||
domain event today — `classic-models-api` has no signals, no outbound webhook,
|
||||
and there is no bus in the cluster — so a workflow can only poll, which is what
|
||||
made a nightly sweep look like the only option. Until Kafka is in place,
|
||||
detection stays unbuilt rather than being approximated by a periodic scan: a
|
||||
batch that pretends to be reactive is worse than an absent feature, because it
|
||||
looks finished.
|
||||
**Events over Kafka, emitted by `classic-models-api`.** The target for the
|
||||
reactive half. The estate emits no domain event today — the API has no signals
|
||||
and no outbound webhook, and there is no bus in the cluster — which is what made
|
||||
a nightly sweep look like the only option.
|
||||
|
||||
The periodic ordering run does not wait on this.
|
||||
The emission belongs **in the API**, not in a database CDC and not in a gateway
|
||||
plugin. Only the API knows which change is a business event rather than a row
|
||||
update, and only it holds the identity that made it — `sub` and `azp` from the
|
||||
caller's token, which the event has to carry if a downstream agent is to act
|
||||
under the same authority as the person who triggered it.
|
||||
|
||||
Two facts about that codebase shape the work:
|
||||
|
||||
- Every write goes through one `BaseModelViewSet` in
|
||||
`api/v1/classicmodels/views.py`. One hook there covers products, orders,
|
||||
payments and the rest — Django signals scattered across models would be the
|
||||
harder version of the same thing.
|
||||
- **The models are `managed = False`.** The tables are the pre-existing
|
||||
classicmodels schema and Django owns no migrations for them. A transactional
|
||||
outbox means introducing the estate's first managed table beside unmanaged
|
||||
ones — which works, and is worth deciding deliberately rather than
|
||||
discovering.
|
||||
|
||||
An outbox is the part that matters: emitting from a viewset after `save()` drops
|
||||
the event if the process dies between the two, and an agent that never hears
|
||||
about a stock movement is worse than one that hears late.
|
||||
|
||||
Until this exists, detection stays unbuilt rather than being approximated by a
|
||||
periodic scan: a batch that pretends to be reactive is worse than an absent
|
||||
feature, because it looks finished.
|
||||
|
||||
The periodic ordering run does not wait on any of it.
|
||||
|
||||
## Two controls that become mandatory here
|
||||
|
||||
|
||||
Reference in New Issue
Block a user