Files
mygo/docs/architecture.md
T
ld 63ede5c237 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.
2026-07-05 23:30:17 +08:00

4.5 KiB

Architecture

Layered Design

Handler (Gin handlers)          ← translates HTTP ↔ Service calls
    ↓
Service (business logic)        ← orchestrates, authorizes, validates
    ↓                        ↓
Repository (GORM data access)   Storage (file I/O)
    ↓                        ↓
[SQLite / PostgreSQL]        [Local FS / S3]

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.

Package Map

Layer Package Purpose Status
CLI cmd Cobra root command 🛠 WIP
cmd/serve.go mygo serve — wire deps, start HTTP
cmd/config.go mygo config — config subcommand 🛠 WIP
cmd/status.go mygo status — health check 🛠 WIP
Config internal/config Viper load (YAML + env + flags), typed Duration config via built-in decode hook
App internal/app Runtime dependency container and build metadata 🛠 WIP
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, 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 and REST error-kind mapping

API Routes (v0)

GET /api/v1/version

POST /api/v1/auth/register
POST /api/v1/auth/login
POST /api/v1/auth/refresh
POST /api/v1/auth/logout

GET    /api/v1/account
GET    /api/v1/account/passkeys
POST   /api/v1/account/passkeys
DELETE /api/v1/account/passkeys/:id

GET    /api/v1/files
POST   /api/v1/files                   # JSON creates directories; multipart uploads files. File upload parent_id is a query parameter.
GET    /api/v1/files/:id
GET    /api/v1/files/:id/content
PUT    /api/v1/files/:id
DELETE /api/v1/files/:id

GET    /api/v1/admin/users
GET    /api/v1/admin/users/:id
PUT    /api/v1/admin/users/:id
DELETE /api/v1/admin/users/:id

Middleware Chain

Applied globally by gin.Default(): logger → recovery

Planned globally: cors

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

  • cmd/serve.go loads config, calls app.Bootstrap to initialize DB + services, builds the router, and starts the HTTP server.
  • app.WebApp carries runtime dependencies and build metadata needed to assemble handlers.
  • internal/server owns Gin router setup (router.go), route registration split into routes_public.go (public auth) and routes_protected.go (JWT-protected account).
  • Each route group creates its own handler instance: routes_public.go creates AuthHandler, routes_protected.go creates AccountHandler — no shared handler state between public and protected routes.
  • RunWithGracefulShutdown stops accepting new requests on termination and gives in-flight requests time to finish.