Summary
Several EventListeners contain business logic directly instead of delegating to Services. This violates our documented architecture pattern (CLAUDE.md Section 6) where entry points should be thin adapters that delegate to Services.
Current State
The following listeners contain business logic directly:
IngestFromUpstream (ingest_listener.py:26-78)
- Iterates over ingestor results
- Creates DepositionSRN
- Emits events to outbox
- Should delegate to an
IngestService
ConvertDepositionToRecord (record_creation_listener.py:26-61)
- Creates Record aggregate
- Persists via repository
- Emits RecordPublished event
- Should delegate to a
RecordService
ProjectNewRecordToIndexes (index_projector.py:17-27)
- Iterates over index backends
- Calls
backend.ingest() directly
- Should delegate to an
IndexService
Target Pattern
# Before (logic in listener)
class IngestFromUpstream(EventListener[IngestRequested]):
ingestors: IngestorRegistry
outbox: Outbox
config: Config
async def handle(self, event: IngestRequested) -> None:
# 50+ lines of business logic...
# After (thin listener delegating to service)
class IngestFromUpstream(EventListener[IngestRequested]):
service: IngestService
async def handle(self, event: IngestRequested) -> None:
await self.service.run_ingest(
ingestor_name=event.ingestor_name,
since=event.since,
limit=event.limit,
)
Benefits
- Testability - Services can be unit tested without event infrastructure
- Reusability - Services can be called from multiple entry points (listeners, commands, CLI)
- Consistency - Matches the documented architecture pattern
- Separation of concerns - Entry points handle event wiring, services handle business logic
Tasks
Summary
Several EventListeners contain business logic directly instead of delegating to Services. This violates our documented architecture pattern (CLAUDE.md Section 6) where entry points should be thin adapters that delegate to Services.
Current State
The following listeners contain business logic directly:
IngestFromUpstream(ingest_listener.py:26-78)IngestServiceConvertDepositionToRecord(record_creation_listener.py:26-61)RecordServiceProjectNewRecordToIndexes(index_projector.py:17-27)backend.ingest()directlyIndexServiceTarget Pattern
Benefits
Tasks
IngestServiceindomain/ingest/service/IngestFromUpstreamto delegate toIngestServiceRecordServiceindomain/record/service/ConvertDepositionToRecordto delegate toRecordServiceIndexServiceindomain/index/service/ProjectNewRecordToIndexesto delegate toIndexService