# go-crm (lightweight local dev) This service is intentionally small so junior Go developers can understand it end-to-end without Docker, hot reloads, or other heavy tooling. The goal: `go run main.go` with a single SQLite file in `.dev-data/`. ## Local development (junior-friendly) 1. Ensure Go 1.25+ is installed and `go` is in your `PATH`. 2. Run the helper script (no Docker): ```bash ./scripts/dev.sh ``` 3. The server boots on [http://localhost:8080](http://localhost:8080). 4. The database and WhatsApp store live under `.dev-data/` (auto-created). There are no file watchers or `air` hot reloads—just the plain Go toolchain so newcomers can focus on code, not tooling. ## Project layout (modern, clean layers) ``` go-crm/ ├── cmd/ # optional commands (e.g. migrations) │ └── migrate/ # lightweight migration CLI (future) ├── config/ # templated configs (kept simple) ├── data/ # production data path (gitignored) ├── internal/ # HTTP handlers (usecases wired to HTTP) │ ├── handlers/ │ ├── middleware/ │ └── templates/ ├── pkg/ # reusable application packages │ ├── domain/ # entities + repository interfaces │ ├── usecase/ # application services / business logic │ └── repo/ # persistence adapters (SQLite, etc.) ├── scripts/ # helper scripts (dev runner, migrations) ├── main.go # application entrypoint (wire handlers → usecases) ├── go.mod └── go.sum ``` ## Recommended architecture / interfaces ### `pkg/domain/lead.go` ```go package domain // Lead is the core business entity. type Lead struct { LeadID int64 ClientID int64 Name string PhoneRaw string PhoneNormalized string ServiceInterest string Status string PaymentStatus string } // LeadRepository abstracts persistence. type LeadRepository interface { FindAll(ctx context.Context, clientID int64) ([]Lead, error) FindByID(ctx context.Context, clientID, leadID int64) (*Lead, error) Update(ctx context.Context, lead Lead) error } ``` ### `pkg/usecase/lead_service.go` ```go package usecase import ( "context" "go-crm/pkg/domain" ) // LeadService orchestrates business logic. type LeadService struct { Repo domain.LeadRepository } func (s *LeadService) ListAll(ctx context.Context, clientID int64) ([]domain.Lead, error) { return s.Repo.FindAll(ctx, clientID) } ``` ### `pkg/repo/sqlite_lead_repo.go` ```go package repo import ( "context" "database/sql" "go-crm/pkg/domain" ) // SQLiteLeadRepository implements LeadRepository using sqlite. type SQLiteLeadRepository struct { DB *sql.DB } func (r *SQLiteLeadRepository) FindAll(ctx context.Context, clientID int64) ([]domain.Lead, error) { // query leads table, scan rows, return []domain.Lead } ``` Handlers in `internal/handlers` should accept a clear service layer (`usecase.LeadService`), not raw DB logic. Keep controllers thin: parse request, call service, write HTML/JSON. ## Migration strategy - Do not scan/clean the entire `leads` table on every startup (that was causing RAM blowups). - Run the lightweight CLI with `make migrate` (which builds and runs the migration binary) whenever you need to refresh the schema or re-decode legacy Latin-1 data. To target non-default files, run the migration binary directly: ```bash go build -o migrate cmd/migrate/main.go && ./migrate --db /path/to/go-crm.db ``` ## Running tests ```bash go test ./... ``` ## Summary - No Docker or `air` watchers—just `go run main.go`. - Clean package boundaries (domain/usecase/repo) improve readability and testability. - Scripts under `scripts/` orchestrate database setup or helper tasks. - Use the architecture sketch above to guide future refactors.