mirror of
https://github.com/juherr/kill-the-news.git
synced 2026-06-20 22:03:48 +00:00
refactor: move KV repositories to infrastructure (Track P — points 2, 6c)
Make the domain stop depending on infrastructure ("imports point inward").
- Point 2: relocate the four KV adapters (FeedRepository, IconRepository,
WebSubSubscriptionRepository, CountersRepository) from domain/ to
infrastructure/, where the logger import is legitimate. The domain now keeps
only the pure key schema (feed-keys.ts), the Feed aggregate and value objects;
it imports nothing outward. Deliberately no hand-rolled 24-method port
interface (YAGNI without DI) — relocation alone fixes the direction.
- Point 6c: EmailParser.extractFeedId now returns a validated FeedId value
object instead of a raw string, so the most untrusted input (an inbound
recipient address) is guarded at the parse boundary and no longer round-trips
through FeedId.fromTrusted in the ingest path.
All import paths updated; CLAUDE.md source layout/KV-schema notes updated.
351 tests pass; tsc --noEmit clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -56,17 +56,14 @@ src/
|
||||
index.ts # App entrypoint: CORS, IP middleware, route mounting, email handler export
|
||||
config/constants.ts # Shared constants (TTLs, limits)
|
||||
types/index.ts # Env, FeedConfig, EmailData, WebSubSubscription, etc.
|
||||
domain/ # Framework-agnostic core (no Hono imports leak out)
|
||||
domain/ # Framework-agnostic core (no Hono/infra imports leak out)
|
||||
feed.aggregate.ts # Feed aggregate: consistency boundary; all config/metadata mutations go through it
|
||||
feed.ts # Pure invariant functions (expiry, sender policy, byte budget) the aggregate delegates to
|
||||
feed-keys.ts # The KV key schema (pure string builders), shared by every repository
|
||||
feed-repository.ts # KV access for the Feed aggregate + global feed list + email bodies (load/save)
|
||||
icon-repository.ts # KV access for cached favicons (icon:*)
|
||||
websub-subscription-repository.ts # KV access for WebSub subscriber lists (websub:subs:*)
|
||||
counters-repository.ts # KV access for the monitoring counters singleton (stats:counters)
|
||||
clock.ts # Clock port (systemClock) — injected into the aggregate; no ambient Date.now()
|
||||
email-parser.ts # Email parsing (addresses, headers, encoded words)
|
||||
format.ts # Pure formatting helpers (formatBytes)
|
||||
value-objects/ # FeedId, EmailAddress, Domain (immutable, self-validating)
|
||||
value-objects/ # FeedId, EmailAddress, Domain, SenderPolicy (immutable, self-validating)
|
||||
application/ # Use-cases / orchestration (wires domain + infrastructure)
|
||||
feed-service.ts # createFeedRecord / renameFeed / editFeed / deleteFeedRecord (admin UI + REST API)
|
||||
email-processor.ts # Core ingestion: load aggregate → accepts? → feed.ingest → persist
|
||||
@@ -74,6 +71,10 @@ src/
|
||||
stats.ts # Monitoring counters increment policy + storage scans
|
||||
infrastructure/ # Adapters: KV/R2, outbound HTTP, logging, framework glue
|
||||
logger.ts # JSON structured logger
|
||||
feed-repository.ts # KV adapter for the Feed aggregate + global feed list + email bodies (load/save)
|
||||
icon-repository.ts # KV adapter for cached favicons (icon:*)
|
||||
websub-subscription-repository.ts # KV adapter for WebSub subscriber lists (websub:subs:*)
|
||||
counters-repository.ts # KV adapter for the monitoring counters singleton (stats:counters)
|
||||
auth.ts # timingSafeEqual, proxy-auth check, API bearer middleware
|
||||
cloudflare-email.ts # Cloudflare Email routing handler
|
||||
forwardemail.ts # ForwardEmail webhook types/parsing
|
||||
@@ -130,7 +131,7 @@ All data lives in the `EMAIL_STORAGE` KV namespace:
|
||||
| `icon:<domain>` | Cached favicon record (base64 + content type; negative entries allowed) |
|
||||
| `stats:counters` | `Counters` (cumulative monitoring counters singleton) |
|
||||
|
||||
The KV key schema lives in `src/domain/feed-keys.ts` — never inline a `feed:`/`feeds:list`/`websub:`/`icon:`/`stats:counters` key string anywhere else. KV access is owned by four repositories, each for one concern: `FeedRepository` (the Feed aggregate + global list + email bodies), `IconRepository` (`icon:*`), `WebSubSubscriptionRepository` (`websub:subs:*`), and `CountersRepository` (`stats:counters`). Go through a repository, never `env.EMAIL_STORAGE.get/put` directly.
|
||||
The KV key schema lives in `src/domain/feed-keys.ts` (pure, framework-agnostic) — never inline a `feed:`/`feeds:list`/`websub:`/`icon:`/`stats:counters` key string anywhere else. KV access is owned by four repository **adapters** in `src/infrastructure/`, each for one concern: `FeedRepository` (the Feed aggregate + global list + email bodies), `IconRepository` (`icon:*`), `WebSubSubscriptionRepository` (`websub:subs:*`), and `CountersRepository` (`stats:counters`). Go through a repository, never `env.EMAIL_STORAGE.get/put` directly. The domain depends only on the key schema, not on these adapters.
|
||||
|
||||
### Domain & layering rules
|
||||
|
||||
|
||||
Reference in New Issue
Block a user