Files
RenewCMS/docs/architecture.md
T

2.1 KiB

Architecture

RenewCMS is a Go HTTP API and a Vue 3 SPA, shipped as a single binary.

Backend layering

Dependencies point inward:

api/  ──▶  internal/infrastructure/useCases  ──▶  internal/domain  ◀──  internal/infrastructure/{persistence,mailer}
Layer Location Rules
Domain internal/domain/<name>/ Entities, Err… sentinel errors, Repository interfaces. Imports only the stdlib and other domain packages, with no GORM tags.
Use cases internal/infrastructure/useCases/ Application logic. Depends only on domain interfaces, never on persistence, mailer, GORM or net/http.
Adapters internal/infrastructure/persistence/{models,mappers,repositories}, internal/infrastructure/mailer Implement a domain Repository (assert with var _ domain.Repository = &X{}). GORM structs stay in models and are converted by mappers.
Delivery api/handlers/<name>/, api/router.go HTTP handlers receive use cases through NewHandler(...). They never import persistence, mailer or GORM.
Composition root api/dependecyInjection.go The only place that opens the DB, builds repositories and use cases, and lists models for AutoMigrate.
Entry point cmd/server/main.go Env/config, HTTP server, graceful shutdown.

Nothing under internal/ imports api/.

Frontend ↔ backend

web/ (Vue, Vite)
  │  task build:web  (vue-tsc + vite build)
  ▼
internal/infrastructure/assets/dist/        gitignored build output
  │  //go:embed all:dist   (assets_embed.go, default build)
  ▼
bin/renewcms  ──  /v1/*  API
               ──  /healthz
               ──  /*    SPA (api/handlers/frontend), index.html fallback
  • Production: a single origin. The SPA calls the API with relative URLs, so the JWT cookie works without CORS. Files under assets/ are content-hashed by Vite and cached for a year. index.html is never cached.
  • Development: built with -tags nofrontend (assets_stub.go, assets.Dist == nil). Vite serves the SPA on :5173 and proxies /v1 to the API on :8080. See development.md.