Project Structure
Go backend directory organization.
Directory Layout
sis_backendgo/
├── cmd/
│ ├── api/
│ │ └── main.go # Application entry point
│ ├── migrate-static/ # Static data migration tool
│ ├── migrate-daily/ # Daily data migration tool
│ ├── migrate-requirements/ # Requirements migration tool
│ ├── inspect-requirements/ # Requirements inspection tool
│ └── seed-data/ # Database seeding tool
├── internal/
│ ├── config/
│ │ └── config.go # Environment configuration
│ ├── domain/ # Business domains (20 modules)
│ │ ├── academic/
│ │ ├── audit/
│ │ ├── authentication/
│ │ ├── authorization/
│ │ ├── billing/
│ │ ├── communications/
│ │ ├── core/
│ │ ├── financialaid/
│ │ ├── formbuilder/
│ │ ├── grading/
│ │ ├── organization/
│ │ ├── people/
│ │ ├── proxy/
│ │ ├── registration/
│ │ ├── reporting/
│ │ ├── roles/
│ │ ├── sso/
│ │ ├── testscores/
│ │ ├── workflow/
│ │ └── workflowengine/
│ ├── lmsintegration/ # LMS connectors, engine, export (NOT under domain/)
│ ├── middleware/ # HTTP middleware
│ │ ├── auth.go # Session-based authentication
│ │ ├── bodylimit.go # Request body size limit
│ │ ├── chain.go # Middleware chaining
│ │ ├── cors.go # CORS handling
│ │ ├── csrf.go # CSRF token validation
│ │ ├── impersonation_audit.go # Impersonation audit logging
│ │ ├── locale.go # Accept-Language parsing
│ │ ├── logging.go # Request logging
│ │ ├── ratelimit.go # Rate limiting
│ │ ├── security_headers.go # Security response headers
│ │ └── stdlib.go # Standard middleware (RequestID, RealIP, Recoverer)
│ ├── router/
│ │ └── router.go # Route registration
│ └── shared/ # Shared utilities
│ ├── audit/ # Audit logging helpers
│ ├── auth/ # Session service, password hashing
│ ├── credits/ # Credit conversion utilities
│ ├── db/ # Database utilities
│ ├── email/ # SMTP email service
│ ├── errors/ # Error types & constructors
│ ├── response/ # HTTP response helpers
│ └── validator/ # Input validation
├── migrations/ # Go-specific migration scripts
├── go.mod # Go module definition
├── go.sum # Dependency checksums
└── .env # Environment variablesDomain Module Structure
Each domain follows a consistent 6-file structure:
internal/domain/{name}/
├── model.go # Database entity structs with db tags
├── dto.go # Request/Response DTOs with validation
├── repository.go # Data access layer (SQL queries)
├── service.go # Business logic layer
├── handler.go # HTTP request handlers
└── routes.go # Route registration functionComplex Domains
Some domains have additional files for subresources or complex logic:
internal/domain/academic/
├── model.go
├── dto.go
├── repository.go
├── service.go
├── service_junction.go # Junction table logic (e.g. course-requisites)
├── handler.go
└── routes.go
internal/domain/billing/
├── model.go
├── dto.go
├── repository.go
├── service.go
├── service_subresources.go # Complex subresource logic
├── handler.go
├── handler_subresources.go # Subresource handlers
└── routes.goKey Directories
cmd/api/
Application entry point:
go
// main.go
func main() {
// Load configuration
cfg, _ := config.Load()
// Initialize logger
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
// Connect to database (lib/pq via database/sql)
database, _ := sql.Open("postgres", cfg.DSN())
// Connect to Valkey/Redis for sessions
sessionService := auth.NewSessionService(redisClient, cfg)
// Create router with all dependencies
deps := &router.Deps{
Config: cfg,
Logger: logger,
DB: database,
SessionService: sessionService,
// ...
}
handler := router.New(deps)
// Start server
http.ListenAndServe(":8000", handler)
}internal/config/
Environment configuration:
go
type Config struct {
// Server
ServerPort string
ServerHost string
Environment string
// Database (PostgreSQL via lib/pq)
DBHost string
DBPort string
DBUser string
DBPassword string
DBName string
DBSSLMode string
DBMaxConns int
// Redis/Valkey (sessions)
RedisURL string
RedisPassword string
RedisDB int
// Sessions
SessionTTL time.Duration
SessionSlidingExpiration bool
// CORS
CORSAllowedOrigins []string
CORSAllowCredentials bool
// Cookies
CookieDomain string
CookiePath string
CookieSecure bool
// Photo Storage
PhotoStoragePath string
PhotoMaxSizeMB int
// Email
SMTPHost string
SMTPPort int
SMTPFrom string
SMTPUseTLS bool
}
func Load() (*Config, error) {
// Load from .env file and environment variables
}internal/middleware/
HTTP middleware chain:
| File | Purpose |
|---|---|
auth.go | Session-based authentication (reads sis_session cookie, validates via Valkey) |
bodylimit.go | Request body size limit (1 MB default) |
chain.go | Middleware composition |
cors.go | CORS headers |
csrf.go | CSRF token validation for mutating requests |
impersonation_audit.go | Audit logging for impersonated actions |
locale.go | Accept-Language header parsing for i18n |
logging.go | Request/response logging |
ratelimit.go | Rate limiting for public endpoints |
security_headers.go | Security response headers |
stdlib.go | Standard middleware (RequestID, RealIP, Recoverer) |
internal/shared/
Shared utilities:
| Directory | Purpose |
|---|---|
audit/ | Audit logging helpers |
auth/ | Session service (Valkey), password hashing, cookie management |
credits/ | Credit conversion utilities |
db/ | Query builder, row scanner, pagination, transactions |
email/ | SMTP email service |
errors/ | Custom error types and constructors |
response/ | HTTP JSON response helpers |
validator/ | Input validation utilities |
internal/router/
Central route registration. All 20 domain packages register their routes here:
go
func New(deps *Deps) http.Handler {
mux := http.NewServeMux()
// Session-based auth middleware
authMw := middleware.Auth(sessionValidator)
csrfMw := middleware.CSRF()
impersonationAuditMw := middleware.ImpersonationAudit(deps.DB)
protect := func(handler http.HandlerFunc) http.HandlerFunc {
return middleware.WithMiddleware(authMw,
middleware.WithMiddleware(csrfMw,
middleware.WithMiddleware(impersonationAuditMw, handler)))
}
// Register domain routes
core.RegisterRoutes(mux, p, deps.DB, deps.Validator, protect)
organization.RegisterRoutes(mux, p, deps.DB, deps.Validator, protect)
people.RegisterRoutes(mux, p, deps.PeopleHandler, protect)
academic.RegisterRoutes(mux, p, deps.AcademicHandler, protect)
// ... all 20 domain packages, plus lmsintegration
// Global middleware stack
handler := middleware.Chain(normalizedMux,
middleware.RequestID,
middleware.RealIP,
middleware.Locale,
middleware.Logger(deps.Logger),
middleware.Recoverer,
corsMiddleware,
middleware.SecurityHeaders,
middleware.BodyLimit(1<<20),
)
return handler
}Frontend Stats
The frontend (sis_frontend/) contains:
| Category | Count |
|---|---|
| Views | 147 across 18 directories |
| API services | 36 service files |
| Pinia stores | 10 stores |
| Reusable components | 67 components |
File Naming Conventions
| Type | Pattern | Example |
|---|---|---|
| Entry point | main.go | cmd/api/main.go |
| Domain model | model.go | domain/people/model.go |
| Domain DTO | dto.go | domain/people/dto.go |
| Repository | repository.go | domain/people/repository.go |
| Service | service.go | domain/people/service.go |
| Handler | handler.go | domain/people/handler.go |
| Routes | routes.go | domain/people/routes.go |
| Tests | *_test.go | service_test.go |
Configuration
Environment Variables
Set via .env file in sis_backendgo/:
bash
# Server
SERVER_PORT=8000
SERVER_HOST=0.0.0.0
ENVIRONMENT=development
# Database (PostgreSQL via lib/pq)
DB_HOST=127.0.0.1
DB_PORT=5433
DB_USER=postgres
DB_PASSWORD=postgres
DB_NAME=sis
DB_SSLMODE=disable
DB_MAX_CONNS=25
# Valkey/Redis (required for sessions)
REDIS_URL=localhost:6379
REDIS_PASSWORD=
REDIS_DB=0
# Sessions
SESSION_TTL=4h
SESSION_SLIDING_EXPIRATION=true
# CORS
CORS_ALLOWED_ORIGINS=http://localhost:5173,http://127.0.0.1:5173
CORS_ALLOW_CREDENTIALS=true
# Cookies
COOKIE_DOMAIN=
COOKIE_PATH=/api
COOKIE_SECURE=false
# Photo Storage
PHOTO_STORAGE_PATH=./photos
PHOTO_MAX_SIZE_MB=2
# Email (Mailpit for development)
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_FROM=no-reply@puc.edu
SMTP_USE_TLS=falseEnvironment Selection
Use the ENVIRONMENT variable:
| Value | Description |
|---|---|
development | Local development (verbose logging, relaxed cookie settings) |
production | Production (minimal logging, secure cookies required) |