Chandra
SaaS Template

SaaS Template

Goal

[One sentence. What are we building?]

Non-goals

  • [What we’re NOT doing]

Numbers

  • QPS: ______
  • Storage: ______ / year
  • Latency target: ______

Constraints

[Rules that limit complexity. Prevents over-engineering.]

  • [Constraint — e.g., “Only handle statuses: PENDING, COMPLETED, FAILED”]
  • [Constraint — e.g., “Single tenant. No isolation logic.”]
  • [Constraint — e.g., “Max 3 retries. No custom retry policies.”]
  • [Constraint — e.g., “No update or delete of submitted tasks.”]

Core Features

[Features without which the app is useless. Must ship in MVP.]

Feature 1: [Name]

What it does: [One sentence.]

Risks we tolerate:

  • [Risk — e.g., “No authentication on this endpoint”]
  • [Risk — e.g., “Data loss if process crashes mid-write”]

Trusted sources:

  • paper or articles or books

Feature 2: [Name]

What it does: [One sentence.]

Risks we tolerate:

  • [Risk]
  • [Risk]

Trusted sources:

  • paper or articles or books

Feature 3: [Name]

What it does: [One sentence.]

Risks we tolerate:

  • [Risk]

Trusted sources:

  • paper or articles or books

Software Architecture

use modular monolith architecture

Core flow

[Paragraphs. Use bullet lists when needed.]

Storage choice & why

[What + reasoning.]

Directory Structure

cmd/example/main.go   # entrypoint — starts HTTP + gRPC
modules/              # domain modules
  example/            #   example module
    config.go         #     module-specific config struct
    dependencies.go   #     wire deps, load own config
    http.go           #     HTTP handlers + route registration
middleware/           # shared: recovery, request ID, timeout, validation
config/               # YAML loader + config.yaml

Module boundaries

[Package 1] — Responsibility
[Package 2] — Responsibility
[Package 3] — Responsibility

Dependencies

programming language standard library, open source packages, sidecar pattern, etc.)

Abstraction Depth per Modules

  • use interfaces for swappable implementation
  • do not over abstract
  • add why we abstract it (the functions) why the module export this function to be usable to outside world

Core Feature Implementation Phase

Phase 1: [Feature A]

[First working feature.]

  • [Task]
  • [Task]
  • Testing

Checkpoint: [What works when this phase is done]

Phase 2: [Feature B]

  • [Task]
  • [Task]
  • Testing

Checkpoint: [What works]


Testing Strategy

tests should be done after each phase implementation, we should breakdown the system to be independent and testable without waiting the apps fully build

Unit Tests

What: Domain logic. Core functionalities

Example:

  • “Task with status COMPLETED cannot transition to IN_PROGRESS”
  • “Priority comparison correctly orders high-priority before low-priority with same scheduled_at”

Integration Tests

What: Database interactions. Broker interactions. The poll-and-claim loop with real PostgreSQL. End-to-end claim → dispatch → worker acknowledgment.

Example:

  • “Two concurrent schedulers claiming from same PENDING pool: exactly N tasks claimed, no duplicates”
  • “Visibility timeout cleanup resets IN_PROGRESS tasks stuck longer than timeout”
  • “Worker idempotency: submitting the same task_id twice results in single execution”
  • use test-container if applicable