better
This commit is contained in:
+128
-61
@@ -43,6 +43,134 @@ Create a clean, maintainable app structure where:
|
|||||||
- Consider production-minded pool settings (`pool_pre_ping=True` and sensible recycle/timeout values).
|
- Consider production-minded pool settings (`pool_pre_ping=True` and sensible recycle/timeout values).
|
||||||
- Prefer Alembic for schema migrations.
|
- Prefer Alembic for schema migrations.
|
||||||
|
|
||||||
|
## Suggested Project Structure
|
||||||
|
|
||||||
|
Base structure (no database required):
|
||||||
|
|
||||||
|
```text
|
||||||
|
.
|
||||||
|
├─ pyproject.toml
|
||||||
|
├─ .env.example
|
||||||
|
├─ README.md
|
||||||
|
├─ src/
|
||||||
|
│ └─ app/
|
||||||
|
│ ├─ __init__.py
|
||||||
|
│ ├─ main.py
|
||||||
|
│ ├─ config.py
|
||||||
|
│ ├─ logging.py
|
||||||
|
│ ├─ api/
|
||||||
|
│ │ ├─ __init__.py
|
||||||
|
│ │ └─ health.py
|
||||||
|
│ ├─ services/
|
||||||
|
│ │ ├─ __init__.py
|
||||||
|
│ │ └─ example_service.py
|
||||||
|
│ ├─ ui/
|
||||||
|
│ │ ├─ __init__.py
|
||||||
|
│ │ ├─ components/
|
||||||
|
│ │ │ ├─ __init__.py
|
||||||
|
│ │ │ └─ nav.py
|
||||||
|
│ │ └─ pages/
|
||||||
|
│ │ ├─ __init__.py
|
||||||
|
│ │ ├─ home.py
|
||||||
|
│ │ ├─ dashboard.py
|
||||||
|
│ │ └─ about.py
|
||||||
|
│ └─ bootstrap.py
|
||||||
|
└─ tests/
|
||||||
|
├─ test_health.py
|
||||||
|
└─ test_pages_registration.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Conceptual boundaries for the base structure
|
||||||
|
|
||||||
|
- `main.py`: application entrypoint only; wires `create_app()` and process startup concerns.
|
||||||
|
- `bootstrap.py`: app composition layer; owns app factory, router registration, page registration, and lifespan wiring.
|
||||||
|
- `config.py`: configuration boundary; centralizes settings parsing and typed config objects.
|
||||||
|
- `logging.py`: observability boundary; centralizes logging format/levels/handlers so modules do not configure logging ad hoc.
|
||||||
|
- `api/`: transport boundary for HTTP endpoints; validate/shape request-response objects and delegate business work to `services/`.
|
||||||
|
- `services/`: use-case/business boundary; orchestrates domain behavior and dependencies, independent of UI rendering.
|
||||||
|
- `ui/pages/`: route-level UI boundary; one module per page, focused on layout/event handling for that page.
|
||||||
|
- `ui/components/`: reusable presentation boundary; shared UI widgets/composition utilities with no business side effects.
|
||||||
|
- `tests/`: behavior boundary; verify public behavior (health endpoint, page registration, service outcomes), not private implementation details.
|
||||||
|
|
||||||
|
### Dependency direction to keep clear
|
||||||
|
|
||||||
|
- Prefer this dependency flow:
|
||||||
|
- `main/bootstrap` -> `config/logging` + `api` + `ui/pages` + `services`
|
||||||
|
- `api` -> `services`
|
||||||
|
- `ui/pages` -> `ui/components` + `services`
|
||||||
|
- `services` -> pure helpers/clients (and later `db/` if enabled)
|
||||||
|
- Avoid reverse imports (for example, `services` importing from `ui/pages` or `api`).
|
||||||
|
- Keep page modules and API handlers as thin adapters; keep business decisions in `services/`.
|
||||||
|
|
||||||
|
## Extending This Pattern with a Database (Optional)
|
||||||
|
|
||||||
|
If database access is needed, extend with:
|
||||||
|
|
||||||
|
```text
|
||||||
|
.
|
||||||
|
├─ alembic.ini
|
||||||
|
├─ alembic/
|
||||||
|
│ ├─ env.py
|
||||||
|
│ └─ versions/
|
||||||
|
├─ src/
|
||||||
|
│ └─ app/
|
||||||
|
│ └─ db/
|
||||||
|
│ ├─ __init__.py
|
||||||
|
│ ├─ base.py
|
||||||
|
│ ├─ session.py
|
||||||
|
│ ├─ models/
|
||||||
|
│ │ ├─ __init__.py
|
||||||
|
│ │ └─ example.py
|
||||||
|
│ └─ repositories/
|
||||||
|
│ ├─ __init__.py
|
||||||
|
│ └─ example_repo.py
|
||||||
|
└─ tests/
|
||||||
|
└─ test_db_session_dependency.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Recommended database layering
|
||||||
|
|
||||||
|
- Keep `db/` focused on persistence primitives:
|
||||||
|
- `session.py`: engine + session factory + dependency helpers
|
||||||
|
- `models/`: ORM table mappings only
|
||||||
|
- `repositories/`: query/persistence operations only
|
||||||
|
- Keep transaction orchestration in `services/`, not in UI pages.
|
||||||
|
- Keep `ui/pages/` free of SQLAlchemy details; pages call services.
|
||||||
|
- Keep API handlers thin and delegate data work to services/repositories.
|
||||||
|
|
||||||
|
### Session and transaction pattern to request
|
||||||
|
|
||||||
|
- Use one engine and one sessionmaker per process, initialized at app startup.
|
||||||
|
- Use a `get_db_session()` dependency with `yield` for request-scoped sessions.
|
||||||
|
- In write flows, always use context managers for commit/rollback safety.
|
||||||
|
- In read-only flows, avoid unnecessary transactions and keep queries narrowly scoped.
|
||||||
|
- Do not share a session across concurrent tasks; open a new session per task/unit of work.
|
||||||
|
|
||||||
|
### Migration and schema workflow
|
||||||
|
|
||||||
|
- Treat Alembic migrations as the source of truth for schema evolution.
|
||||||
|
- Prefer:
|
||||||
|
- `alembic revision --autogenerate -m "..."`
|
||||||
|
- review migration script
|
||||||
|
- `alembic upgrade head`
|
||||||
|
- Avoid relying on `metadata.create_all()` for production schema management.
|
||||||
|
- Include a lightweight startup check that logs current migration state (without mutating schema).
|
||||||
|
|
||||||
|
### Configuration and runtime concerns
|
||||||
|
|
||||||
|
- Read `DATABASE_URL` from settings (`pydantic-settings`) and keep secrets out of source control.
|
||||||
|
- Configure pool options appropriate for environment (for example: `pool_pre_ping`, timeout/recycle tuning).
|
||||||
|
- Keep SQL echo/debug logging disabled in production by default.
|
||||||
|
- Consider a readiness probe (`/readyz`) that verifies DB connectivity when your deployment needs it.
|
||||||
|
|
||||||
|
### Testing guidance for DB-enabled projects
|
||||||
|
|
||||||
|
- Unit test repositories against a disposable test database.
|
||||||
|
- Integration test `get_db_session()` lifecycle and rollback behavior on failures.
|
||||||
|
- Add migration tests that validate model changes are represented in Alembic revisions.
|
||||||
|
- Add API/service tests for critical read/write paths (including uniqueness and constraint errors).
|
||||||
|
- Keep DB tests isolated and deterministic (fresh schema + transactional cleanup per test module/session).
|
||||||
|
|
||||||
## Extending This Pattern with LangGraph (Optional)
|
## Extending This Pattern with LangGraph (Optional)
|
||||||
|
|
||||||
If your app needs multi-step AI workflows, tool-calling loops, or human-in-the-loop approvals, a clean extension is to add a dedicated LangGraph domain layer.
|
If your app needs multi-step AI workflows, tool-calling loops, or human-in-the-loop approvals, a clean extension is to add a dedicated LangGraph domain layer.
|
||||||
@@ -108,67 +236,6 @@ If your app needs multi-step AI workflows, tool-calling loops, or human-in-the-l
|
|||||||
- Add tests ensuring `thread_id` reuse resumes correctly and new IDs start fresh sessions.
|
- Add tests ensuring `thread_id` reuse resumes correctly and new IDs start fresh sessions.
|
||||||
- Add regression tests for state-schema evolution and serialization compatibility.
|
- Add regression tests for state-schema evolution and serialization compatibility.
|
||||||
|
|
||||||
## Suggested Project Structure
|
|
||||||
|
|
||||||
Base structure (no database required):
|
|
||||||
|
|
||||||
```text
|
|
||||||
.
|
|
||||||
├─ pyproject.toml
|
|
||||||
├─ .env.example
|
|
||||||
├─ README.md
|
|
||||||
├─ src/
|
|
||||||
│ └─ app/
|
|
||||||
│ ├─ __init__.py
|
|
||||||
│ ├─ main.py
|
|
||||||
│ ├─ config.py
|
|
||||||
│ ├─ logging.py
|
|
||||||
│ ├─ api/
|
|
||||||
│ │ ├─ __init__.py
|
|
||||||
│ │ └─ health.py
|
|
||||||
│ ├─ services/
|
|
||||||
│ │ ├─ __init__.py
|
|
||||||
│ │ └─ example_service.py
|
|
||||||
│ ├─ ui/
|
|
||||||
│ │ ├─ __init__.py
|
|
||||||
│ │ ├─ components/
|
|
||||||
│ │ │ ├─ __init__.py
|
|
||||||
│ │ │ └─ nav.py
|
|
||||||
│ │ └─ pages/
|
|
||||||
│ │ ├─ __init__.py
|
|
||||||
│ │ ├─ home.py
|
|
||||||
│ │ ├─ dashboard.py
|
|
||||||
│ │ └─ about.py
|
|
||||||
│ └─ bootstrap.py
|
|
||||||
└─ tests/
|
|
||||||
├─ test_health.py
|
|
||||||
└─ test_pages_registration.py
|
|
||||||
```
|
|
||||||
|
|
||||||
If database access is needed, extend with:
|
|
||||||
|
|
||||||
```text
|
|
||||||
.
|
|
||||||
├─ alembic.ini
|
|
||||||
├─ alembic/
|
|
||||||
│ ├─ env.py
|
|
||||||
│ └─ versions/
|
|
||||||
├─ src/
|
|
||||||
│ └─ app/
|
|
||||||
│ └─ db/
|
|
||||||
│ ├─ __init__.py
|
|
||||||
│ ├─ base.py
|
|
||||||
│ ├─ session.py
|
|
||||||
│ ├─ models/
|
|
||||||
│ │ ├─ __init__.py
|
|
||||||
│ │ └─ example.py
|
|
||||||
│ └─ repositories/
|
|
||||||
│ ├─ __init__.py
|
|
||||||
│ └─ example_repo.py
|
|
||||||
└─ tests/
|
|
||||||
└─ test_db_session_dependency.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## Implementation Notes
|
## Implementation Notes
|
||||||
|
|
||||||
- Prefer an explicit page registration function pattern, for example:
|
- Prefer an explicit page registration function pattern, for example:
|
||||||
|
|||||||
Reference in New Issue
Block a user