refactor(api): enforce protocol-neutral service boundaries
- refactor: move HTTP status and DTO concerns out of model and service layers into API and handler code. - feat: add admin service boundary and route auth through services instead of direct repository access. - test: add architecture and error tests covering package boundaries, redaction, and service behavior. - docs: record the protocol-neutral boundary decision and update architecture and roadmap notes.
This commit is contained in:
+11
-4
@@ -14,7 +14,12 @@ Repository (GORM data access) Storage (file I/O)
|
||||
|
||||
Rules:
|
||||
- Handler has no business logic — parse request, call service, write response.
|
||||
- Handler and middleware never access repositories directly; they depend on services.
|
||||
- Service has no HTTP awareness — operates on domain models and interfaces.
|
||||
- `internal/model` and `internal/service` do not import `net/http`, Gin, or `internal/api`.
|
||||
- JSON response DTOs live in HTTP packages. Domain and service result structs are not the public REST schema.
|
||||
- Domain models may use `json:"-"` for defensive redaction of sensitive fields; this is not a REST response contract.
|
||||
- Domain errors carry a protocol-neutral kind, a safe message, and an optional cause. HTTP status mapping happens only at the API boundary.
|
||||
- Repository abstracts the database; Storage abstracts where bytes live, including staged upload promotion.
|
||||
- `internal/server` is the composition root — wires all dependencies together.
|
||||
|
||||
@@ -31,12 +36,12 @@ Rules:
|
||||
| **HTTP** | `internal/server` | Gin router init, route registration (public/protected split), graceful shutdown | ✅ |
|
||||
| | `internal/handler` | HTTP handlers (auth, file, admin, webdav...) | 🛠 WIP |
|
||||
| | `internal/middleware` | Gin middleware (logger, jwt, cors, auth) | 🛠 WIP |
|
||||
| **Business** | `internal/service` | Business logic: `AuthService` (register, login, refresh, logout, passkey CRUD) | ✅ |
|
||||
| | `internal/model` | Domain types (User, File, Credential, Session), error codes | ✅ |
|
||||
| **Business** | `internal/service` | Business logic: `AuthService`, `FileService`, `AdminService`; protocol-neutral results and errors | ✅ |
|
||||
| | `internal/model` | Domain types (User, File, Credential, Session), protocol-neutral error kinds | ✅ |
|
||||
| **Data** | `internal/repository` | Repository interfaces + GORM implementations (User, Session, File, Credential) | ✅ |
|
||||
| | `internal/storage` | Storage backend interface + local disk impl, with staging and promotion | 🛠 WIP |
|
||||
| **Util** | `internal/auth` | JWT sign/verify (HS256), token type discrimination (access/refresh), password hashing (bcrypt), app passkey tokens | ✅ |
|
||||
| | `internal/api` | Unified JSON error response helpers | ✅ |
|
||||
| | `internal/api` | Unified JSON error response helpers and REST error-kind mapping | ✅ |
|
||||
|
||||
## API Routes (v0)
|
||||
|
||||
@@ -72,7 +77,9 @@ Applied globally by `gin.Default()`: logger → recovery
|
||||
|
||||
Planned globally: cors
|
||||
|
||||
Applied to protected groups: auth (JWT validation, inject user into gin.Context)
|
||||
Applied to protected groups: auth (extract bearer token, delegate access-token authentication to `AuthService`, inject principal into gin.Context)
|
||||
|
||||
Applied to admin groups: admin (check authenticated principal from context)
|
||||
|
||||
## Server Responsibilities
|
||||
|
||||
|
||||
Reference in New Issue
Block a user