Skip to main content

Repository structure

civicos/
├── apps/ # pnpm workspaces (React + docs)
│ ├── web/ # Citizen app (Vite + React, :5173)
│ ├── admin/ # Admin console (Vite + React, :5174)
│ └── docs/ # This Docusaurus site (:5175)

├── services/ # One folder per Go service — separate go.mod each
│ ├── api-gateway/ # :3000 — reverse proxy + JWT + rate limit
│ ├── identity-service/ # :3001 — auth, users, applications, moderation
│ ├── community-service/ # :3002 — communities, issues, petitions, reps
│ └── organization-service/ # :3003 — orgs, projects, announcements

├── packages/ # Shared TS packages (pnpm workspaces)
│ ├── types/ # @civicos/types — DTOs + enums shared by both apps
│ ├── config/ # @civicos/config — zod-validated env access for the frontends
│ └── ui/ # @civicos/ui — shared React components

├── infrastructure/
│ └── docker-compose.yml # Postgres 16 + Redis 7 + NATS + Mailpit

├── docs/
│ ├── product/ # 5 source PDFs (Blueprint, Roadmap, Architecture, UX, Playbook)
│ ├── api/openapi-*.yaml # Canonical OpenAPI specs — mirrored into api-gateway
│ ├── deploy.md # Render deployment playbook
│ └── setup.md # Older, partially stale — being superseded by this Docusaurus

├── render.yaml # Render Blueprint (declarative infra)
├── turbo.json # Turborepo pipeline
├── pnpm-workspace.yaml # Workspace globs — apps/*, services/*, packages/*
├── package.json # Root scripts + shared dev deps
├── .env # Local secrets (gitignored)
├── CLAUDE.md # AI collaborator context (also useful for humans)
└── README.md

Each Go service is a mini-project

services/<name>/
├── cmd/server/main.go # Entry point — wires DI, starts Gin
├── internal/
│ ├── domain/models.go # GORM models + enums
│ ├── middleware/ # JWT + role + ban/deletion enforcement
│ └── <feature>/ # repository.go + service.go + handler.go per feature
├── migrations/ # Optional — GORM AutoMigrate does most of the work
├── pkg/
│ ├── config/config.go # Env loading + validation
│ ├── database/postgres.go # GORM connection
│ ├── mailer/ # SMTP + console mailers (identity only)
│ └── response/response.go # Success/Error envelope helpers
├── .air.toml # Air hot-reload config
├── Dockerfile # Multi-stage: golang build → distroless final
├── go.mod
└── go.sum

Each service has its own go.mod — there's no shared Go module. This means each service can upgrade dependencies independently, and a breaking change in one service's model can't accidentally cross into another. The cost is more duplication in go.sum files.

Inside internal/<feature>/ the standard trio is:

  • repository.go — data access. Only touches GORM. Never talks HTTP.
  • service.go — business logic. Consumes the repository interface. Never touches Gin.
  • handler.go — HTTP layer. Consumes the service. Never touches the database.

Wired together in cmd/server/main.go via constructor injection.

Each frontend app is standard Vite

apps/<name>/
├── src/
│ ├── App.tsx
│ ├── main.tsx
│ ├── routes/ # React Router v6 route trees
│ ├── features/ # Feature-scoped components + hooks
│ ├── lib/ # API client, auth helpers, utils
│ └── i18n/ # Translation resources (web only)
├── public/
├── index.html
├── vite.config.ts
├── tsconfig.json
├── playwright.config.ts # E2E tests
└── package.json

Where things you might look for live

Looking forPath
JWT middlewareservices/<svc>/internal/middleware/auth.go
Response envelopeservices/<svc>/pkg/response/response.go
Rate-limit tiersservices/api-gateway/internal/middleware/ratelimit.go
GORM modelsservices/<svc>/internal/domain/models.go
Shared TypeScript enumspackages/types/src/index.ts
Product source of truthdocs/product/*.pdf
OpenAPI specsdocs/api/openapi-*.yaml
Swagger UI handlerservices/api-gateway/internal/docs/docs.go
Render deploy configrender.yaml

Next: Monorepo.