mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-08-01 19:06:01 +00:00
refactor(schedule): move the secondary adapters to app/adapters
Adopts the layout feedback landed in cb49dd67: secondary adapters live under `app/adapters/<context>/`, one file per port, the file named after the port in snake_case with the technology carried by the class name. `app/infra/` is now gone entirely. The rename also separates two meanings of "run" that shared one filename space: `run_sql.py` held `ScheduledRun` (an execution record), while the `run_launcher.py` still to come deals in Gateway runs. task_sql.py -> scheduled_task_repository.py run_sql.py -> scheduled_run_repository.py spec_mapping.py -> spec_mapping.py (implements no port, so no rename) Both SQL adapters now carry the `Secondary adapter (owned persistence)` docstring marker -- this context owns both tables and writes its own queries. `spec_mapping` says instead that it is a boundary mapping and names its two callers. The package `__init__.py` is empty, so imports go through the full path and a class's home file stays unambiguous. Pure move: every top-level symbol was compared against its pre-move original by AST dump (docstrings excluded), plus a separate per-class method-name comparison, since a whole-class dump reports a docstring edit and a renamed method the same way. Also repoints three docstrings and one diagram that still named the deleted `app/infra/` path.
This commit is contained in:
parent
35be955177
commit
0bae77ffc0
0
backend/app/adapters/schedule/__init__.py
Normal file
0
backend/app/adapters/schedule/__init__.py
Normal file
@ -1,7 +1,8 @@
|
||||
"""SQL adapter for the schedule context's execution-record repository.
|
||||
"""Secondary adapter (owned persistence) -- the scheduled_task_runs table in SQL.
|
||||
|
||||
Secondary adapter implementing `ScheduledRunRepository`. Queries are migrated
|
||||
unchanged from the legacy repository.
|
||||
Implements `ScheduledRunRepository` from `deerflow.domain.schedule.ports`. This
|
||||
context owns the `scheduled_task_runs` table and writes its own queries.
|
||||
Queries are migrated unchanged from the legacy repository.
|
||||
|
||||
The load-bearing piece is the `IntegrityError` translation in `add`: the
|
||||
partial unique index `uq_scheduled_task_run_active` is the atomic arbiter of
|
||||
@ -29,7 +30,7 @@ from deerflow.domain.schedule.model import (
|
||||
from deerflow.domain.schedule.ports import ScheduledRunRepository
|
||||
|
||||
# Transitional: the ORM row stays in the harness until engine, models, and
|
||||
# migrations move into app/infra together.
|
||||
# migrations move into app/adapters together.
|
||||
from deerflow.persistence.scheduled_task_runs.model import ScheduledTaskRunRow
|
||||
|
||||
_ACTIVE_STATUS_VALUES = tuple(str(status) for status in ACTIVE_RUN_STATUSES)
|
||||
@ -1,10 +1,12 @@
|
||||
"""SQL adapter for the schedule context's task repository.
|
||||
"""Secondary adapter (owned persistence) -- the scheduled_tasks table in SQL.
|
||||
|
||||
Sibling of `run_sql.py`; both consume `spec_mapping` for the stored JSON spec.
|
||||
Implements `ScheduledTaskRepository` from `deerflow.domain.schedule.ports`.
|
||||
This context owns the `scheduled_tasks` table and writes its own queries, so
|
||||
SQL/ORM vocabulary stops at this file: methods exchange domain objects and
|
||||
normalize SQLite's tz-naive reads.
|
||||
|
||||
Secondary adapter implementing `ScheduledTaskRepository`. SQL/ORM vocabulary
|
||||
stops at this file: methods exchange domain objects and normalize SQLite's
|
||||
tz-naive reads.
|
||||
Sibling of `scheduled_run_repository.py`; both consume `spec_mapping` for the
|
||||
stored JSON spec.
|
||||
|
||||
**The queries are migrated unchanged from the legacy repository.** The claim
|
||||
statement's `FOR UPDATE SKIP LOCKED` and the `protect_terminal` conditional
|
||||
@ -22,7 +24,7 @@ from datetime import UTC, datetime, timedelta
|
||||
from sqlalchemy import and_, or_, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
|
||||
|
||||
from app.infra.schedule.spec_mapping import spec_to_domain, spec_to_wire
|
||||
from app.adapters.schedule.spec_mapping import spec_to_domain, spec_to_wire
|
||||
from deerflow.domain.schedule.model import (
|
||||
TERMINAL_TASK_STATUSES,
|
||||
ContextMode,
|
||||
@ -33,7 +35,7 @@ from deerflow.domain.schedule.model import (
|
||||
from deerflow.domain.schedule.ports import ScheduledTaskRepository
|
||||
|
||||
# Transitional: the ORM row stays in the harness until engine, models, and
|
||||
# migrations move into app/infra together.
|
||||
# migrations move into app/adapters together.
|
||||
from deerflow.persistence.scheduled_tasks.model import ScheduledTaskRow
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -1,9 +1,11 @@
|
||||
"""Wire/storage <-> ScheduleSpec mapping.
|
||||
"""Boundary mapping (not a port implementation) -- wire/storage <-> ScheduleSpec.
|
||||
|
||||
`schedule_spec` is both an HTTP request field and a JSON column -- one shape,
|
||||
two boundaries -- so the mapping lives here once and both the router and the
|
||||
SQL adapter import it, rather than the domain growing a `Mapping[str, Any]` in
|
||||
its signatures.
|
||||
Unlike its siblings in this package, this module implements no port: it is the
|
||||
shared translation both of them need. `schedule_spec` is both an HTTP request
|
||||
field and a JSON column -- one shape, two boundaries -- so the mapping lives
|
||||
here once and both callers import it (`scheduled_task_repository` for the
|
||||
stored column, the scheduled-task router for the request body), rather than the
|
||||
domain growing a `Mapping[str, Any]` in its signatures.
|
||||
|
||||
The split is deliberate: **structural** checks (is the key present? is it a
|
||||
str?) belong to this boundary, **value** rules (5-field cron, resolvable
|
||||
@ -1,10 +0,0 @@
|
||||
"""Secondary adapters for the schedule bounded context.
|
||||
|
||||
Grouped by context rather than by technology, mirroring `domain/schedule/`:
|
||||
everything the outer ring provides to this one context lives here, whether it
|
||||
talks to SQL (`task_sql`, `run_sql`), converts a wire/storage shape
|
||||
(`spec_mapping`), or wraps a runtime.
|
||||
|
||||
`app/infra/persistence/feedback.py` still follows the older technology-first
|
||||
layout because it belongs to a separate migration; the two will converge.
|
||||
"""
|
||||
@ -61,7 +61,7 @@ flowchart LR
|
||||
C["deerflow/scheduler/schedules.py<br/>时区 / cron 计算"]
|
||||
end
|
||||
subgraph TODO["🚧 待建 · 外圈"]
|
||||
AD["app/infra/persistence/<br/>app/infra/schedule/<br/>app/scheduler/poller.py"]
|
||||
AD["app/adapters/schedule/<br/>app/scheduler/poller.py"]
|
||||
end
|
||||
|
||||
R -.->|尚未调用| SV
|
||||
@ -231,12 +231,12 @@ America/New_York 的 "0 9 * * *"
|
||||
{"cron": "0 9 * * *"} ←──→ ScheduleSpec(CRON, "Asia/Shanghai", cron="0 9 * * *")
|
||||
(JSON 列 / HTTP 字段) (值对象)
|
||||
↑
|
||||
app/infra/schedule/spec_mapping.py
|
||||
app/adapters/schedule/spec_mapping.py
|
||||
```
|
||||
|
||||
这不是洁癖,是一条可执行的判据:**一旦领域方法的签名里出现 `Mapping[str, Any]`,就说明领域在处理持久化/传输格式了**。解析这件事天然可以切成两半——结构校验(键在不在?值是不是字符串?)属于边界,值校验(cron 是不是 5 段、时区认不认识、`run_at` 有没有)属于 `__post_init__`。切开之后领域完全不需要看见 dict,签名全部强类型。
|
||||
|
||||
同样的形状在 Feedback 上下文里也成立:`Feedback` 聚合对 ORM 行一无所知,转换全在 `app/infra/persistence/feedback.py`。
|
||||
同样的形状在 Feedback 上下文里也成立:`Feedback` 聚合对 ORM 行一无所知,转换全在 `app/adapters/feedback/feedback_repository.py`。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -5,5 +5,5 @@ Every package under this namespace is one bounded context laid out as
|
||||
dependencies -- no sqlalchemy/fastapi/pydantic, no `app.*`, and no
|
||||
harness infrastructure modules (enforced in CI by
|
||||
`tests/test_harness_domain_purity.py`). Adapters implementing the ports
|
||||
live in `app/infra/`; wiring happens only in `app/gateway/deps.py`.
|
||||
live in `app/adapters/`; wiring happens only in `app/gateway/deps.py`.
|
||||
"""
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
The legacy SQL repository was replaced by the hexagonal slice:
|
||||
port `deerflow.domain.feedback.ports.FeedbackRepository`, adapter
|
||||
`app.infra.persistence.feedback.SqlFeedbackRepository`. The ORM row stays
|
||||
here until PR-N moves engine/models/migrations into app/infra.
|
||||
`app.adapters.feedback.feedback_repository.SqlFeedbackRepository`. The ORM
|
||||
row stays here until PR-N moves engine/models/migrations into app/adapters.
|
||||
"""
|
||||
|
||||
from deerflow.persistence.feedback.model import FeedbackRow
|
||||
|
||||
@ -28,8 +28,8 @@ from schedule_fakes import (
|
||||
InMemoryScheduledTaskRepository,
|
||||
)
|
||||
|
||||
from app.infra.schedule.run_sql import SqlScheduledRunRepository
|
||||
from app.infra.schedule.task_sql import SqlScheduledTaskRepository
|
||||
from app.adapters.schedule.scheduled_run_repository import SqlScheduledRunRepository
|
||||
from app.adapters.schedule.scheduled_task_repository import SqlScheduledTaskRepository
|
||||
from deerflow.config.database_config import DatabaseConfig
|
||||
from deerflow.domain.schedule.model import (
|
||||
ACTIVE_RUN_STATUSES,
|
||||
|
||||
@ -13,7 +13,7 @@ from datetime import UTC, datetime
|
||||
|
||||
import pytest
|
||||
|
||||
from app.infra.schedule.spec_mapping import spec_to_domain, spec_to_wire
|
||||
from app.adapters.schedule.spec_mapping import spec_to_domain, spec_to_wire
|
||||
from deerflow.domain.schedule.model import InvalidScheduleError, ScheduleSpec, ScheduleType
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user