generated from john/python-template
76 lines
3.9 KiB
Markdown
76 lines
3.9 KiB
Markdown
---
|
|
description: Follow these guidelines when editing the services
|
|
applyTo: 'src/transcription/services/*.py'
|
|
---
|
|
|
|
# Services
|
|
|
|
## Structure
|
|
|
|
- Project core data models defined in [models](../../src/transcription/models.py)
|
|
- 1 service class per data model
|
|
- Only services directly interact with the database, and only through async methods
|
|
- Services are completely independent of one another. Any operation that needs to use more than a single service, which is most of them, needs to have a separate orchestration function.
|
|
|
|
## Error Handling
|
|
|
|
- Service-specific errors defined at the top of the respective module and inherit from `AppError`
|
|
- Use a context manager for large `try/except` blocks like in [transcription](../../src/transcription/services/transcription.py)
|
|
|
|
## Checklist
|
|
|
|
- [ ] Uses `ServiceBase` for common logic
|
|
- [ ] CRUD methods created at the top
|
|
- [ ] Session kwarg for `AsyncSession` to pass in a session object to each method
|
|
- [ ] Services use `self._session_scope` in their methods to pass the session thru.
|
|
- Multiple operations on the same object(s) require sharing a session between all the methods used.
|
|
|
|
## CRUD Methods
|
|
|
|
- Create, read, update, and delete, created in that order
|
|
- Name format `<operation>_<model >`, for example `create_document` or `update_job`
|
|
- All services must define these 4 methods first, and in that order
|
|
|
|
## Transaction Finalization
|
|
|
|
When a service method accepts an optional `session` kwarg, write methods must use `self._finalize` to finalize the transaction properly according to whether or not they are sharing a session.
|
|
|
|
- If `session` is `None`: the method owns the transaction and should `commit()`.
|
|
- If `session` is provided: the method must **not** commit; it should `flush()` so IDs and FK values are available to the caller's transaction.
|
|
- Use `refresh()` on returned ORM objects when the caller needs DB-populated values (defaults, triggers, merged state).
|
|
|
|
Recommended helper behavior:
|
|
|
|
- Inputs: active session object, original `session` arg (or a boolean ownership flag), and an optional list of objects to refresh.
|
|
- Logic: `commit` when service-owned session, `flush` when caller-owned session, then refresh requested objects.
|
|
|
|
This keeps orchestration functions atomic: they can pass one shared session across multiple services and commit exactly once at the workflow boundary.
|
|
|
|
## Workflow Transaction Boundaries
|
|
|
|
For multi-step job lifecycles (for example queued transcription jobs), orchestration functions must use explicit transaction phases.
|
|
|
|
Required boundary model:
|
|
|
|
- **Transaction A (claim):** transition `JobStatus.QUEUED -> JobStatus.PROCESSING` and commit immediately.
|
|
- Perform provider/network work **outside** database transactions.
|
|
- **Transaction B (terminal success):** write transcript content and set `JobStatus.TRANSCRIBED` in the same shared-session commit.
|
|
- **Transaction B (terminal failure):** write transcript error detail and set `JobStatus.FAILED` in the same shared-session commit.
|
|
- **Transaction C (retry path):** write transcript error detail, increment retry count, and set `JobStatus.QUEUED` in one shared-session commit.
|
|
|
|
Atomicity rules:
|
|
|
|
- Never commit transcript updates separately from the paired terminal/retry job status change.
|
|
- Terminal state (`TRANSCRIBED` or `FAILED`) and transcript row changes must succeed or roll back together.
|
|
- Retry persistence (`QUEUED` + retry increment + error detail) must succeed or roll back together.
|
|
|
|
Separation of concerns:
|
|
|
|
- Worker modules should stay lightweight and delegate lifecycle transitions to service/workflow orchestration functions.
|
|
- Services should expose session-aware write helpers (flush on caller-owned session) so orchestration controls commit boundaries.
|
|
- Backoff/sleep behavior must run outside transactional scopes.
|
|
|
|
# Service Composition
|
|
|
|
Some operations, like uploading a picutre, require modifications to multiple tables, which can be done by composing methods from the service object into a separate function.
|