3.9 KiB
3.9 KiB
Service Layer Template
Standards for business logic services, commands, queries, policies, and specifications.
Enterprise default: phases deliver these layers for in-scope capabilities — CRUD-only services are insufficient (definition-of-done.md, mandatory-phase-artifacts.md).
Responsibility
Services:
- Own domain rules and orchestration
- Validate invariants (via validators / specifications)
- Apply policies (authorization, entitlement, domain policy objects)
- Expose Commands (state changes) and Queries (reads without write side-effects)
- Write audit records
- Write outbox/domain events in the same transaction as state changes
- Call other services only via HTTP clients / events — never via foreign repos
- Receive dependencies via explicit injection (constructor / factory / FastAPI deps)
Services must not:
- Depend on FastAPI
Request/ response objects - Emit raw SQL bypassing repositories (except rare documented cases)
- Perform provider I/O without going through provider adapters owned by the service
- Mutate another aggregate’s invariants across boundaries without explicit domain rules
- Duplicate another service’s business logic (boundary-rules.md)
Commands
| Rule | Detail |
|---|---|
| Location | app/commands/ or clearly named command methods on services |
| Naming | Verb phrases (CreateMember, FreezeMembership) |
| Effect | Single use-case state change; emit audit + outbox as required |
| Idempotency | Document for retried operations |
Queries
| Rule | Detail |
|---|---|
| Location | app/queries/ or query services / read methods |
| Effect | Read-only; no accidental writes or event emission |
| Lists | Support pagination, filtering, sorting, searching as API requires |
Specifications
| Rule | Detail |
|---|---|
| Location | app/specifications/ |
| Purpose | Reusable query predicates and business specification objects |
| Use | Repositories/services compose specs instead of duplicating filter logic |
Policies
| Rule | Detail |
|---|---|
| Location | app/policies/ |
| Purpose | Authorization, entitlement, and domain policy decisions reusable across commands |
| Note | Route-level permission deps remain; policies encode richer domain rules |
Conventions
| Topic | Rule |
|---|---|
| Location | app/services/ (+ commands/queries/policies/specifications) |
| Naming | {Capability}Service |
| Dependencies | Repositories, validators, specs, policies, event publisher, provider protocols |
| DTOs | Accept/return schema objects or typed domain results — not ORM leakage to API |
| Errors | Raise shared/domain exceptions with stable codes |
| Idempotency | Document for retried operations (OTP, webhooks, messaging) |
| DI | Explicit wiring; no hidden service locators for domain logic |
Transaction & Events
- State change + outbox row in one transaction (ADR-006).
- Event types follow event-template.md.
- Failures calling remote systems must not corrupt local invariants; use async patterns where required (Communication pattern).
Audit
- Record actor, action, entity, tenant, timestamp for sensitive mutations.
- Prefer dedicated audit tables where the service already has them.
Testing
Service / command / query / policy / specification tests cover business rules with DB or fakes — see testing-template.md.