CLAUDE.md
Rama main · réplica de lectura del repositorio original.
@AGENTS.md
# CLAUDE.md
This file provides guidance to Claude Code when working with the Flexprice backend repository.
## Project Overview
Flexprice is a monetization infrastructure platform for AI-native and SaaS companies. It provides usage-based metering, credit management, flexible pricing, and automated invoicing.
- **Language**: Go 1.23+
- **Framework**: Gin (HTTP), Uber FX (DI), Ent (ORM)
- **Databases**: PostgreSQL (transactional), ClickHouse (analytics/events)
- **Messaging**: Kafka
- **Workflow Engine**: Temporal
## Quick Start Commands
```bash
# Complete development environment setup (Docker-based)
make dev-setup
# Start only infrastructure services
docker compose up -d postgres kafka clickhouse temporal temporal-ui
# Run the application locally
make run-server
# or
go run cmd/server/main.go
# Start all services via Docker Compose
make up
# Stop all services
make down
# Restart only FlexPrice services (not infrastructure)
make restart-flexprice
```
## Testing
```bash
# Run all tests
make test
# Run tests verbosely
make test-verbose
# Run tests with coverage
make test-coverage
# Run a single test by name
go test -v -race ./internal/ee/service -run TestBillingService_GenerateInvoice
```
## Linting & Vetting
```bash
# Vet code
go vet ./...
# Format code
gofmt -w .
```
## Database Operations
```bash
# Generate Ent code from schema
make generate-ent
# Apply migrations
make migrate-ent
# Dry-run migration (see SQL without executing)
make migrate-ent-dry-run
# Generate migration SQL file (for production)
make generate-migration
# Run PostgreSQL migrations only
make migrate-postgres
# Run ClickHouse migrations only
make migrate-clickhouse
```
## API Documentation
```bash
# Generate both Swagger 2.0 and OpenAPI 3.0 specs in docs/swagger/
make swagger
```
## SDK Generation
```bash
# Generate Go SDK (current production pipeline)
make go-sdk
# Quick regeneration during development (no clean)
make regenerate-go-sdk
```
## Kafka Operations
```bash
# Initialize Kafka topics
make init-kafka
# Access Kafka UI (requires --profile dev)
docker compose --profile dev up -d kafka-ui
```
## Architecture
### Project Structure
```
flexprice/
├── cmd/
│ ├── server/ # Main application entry point
│ └── migrate/ # Database migration tool
├── ent/
│ └── schema/ # Ent entity schemas (data models)
├── internal/
│ ├── api/ # HTTP handlers and routing
│ │ ├── v1/ # API v1 handlers
│ │ └── cron/ # Legacy manual trigger for void-old-pending invoices (no Temporal equivalent); other cron-style jobs are Temporal schedules
│ ├── domain/ # Domain models and repository interfaces
│ ├── repository/ # Data access layer implementations
│ ├── service/ # Business logic layer
│ ├── temporal/ # Temporal workflows and activities
│ │ ├── workflows/ # Workflow definitions
│ │ └── activities/ # Activity implementations
│ ├── integration/ # Third-party integrations (Stripe, Chargebee, etc.)
│ ├── config/ # Configuration management
│ ├── kafka/ # Kafka producer/consumer
│ └── testutil/ # Test utilities and fixtures
├── migrations/
│ ├── postgres/ # PostgreSQL migrations
│ └── clickhouse/ # ClickHouse migrations
└── api/ # Generated SDKs (Go, Python, JavaScript)
```
### Layered Architecture
1. **Domain Layer** (`internal/domain/`) — Core business entities, repository interfaces, no external dependencies
2. **Repository Layer** (`internal/repository/`) — Implements domain interfaces, direct DB access via Ent/ClickHouse
3. **Service Layer** (`internal/ee/service/`) — Business logic orchestration, transaction management
4. **API Layer** (`internal/api/`) — HTTP request/response, DTO conversions, request validation (no business logic)
5. **Integration Layer** (`internal/integration/`) — Third-party service integrations (Stripe, Chargebee, Razorpay, HubSpot, QuickBooks, etc.), factory pattern
### Key Patterns
- **Dependency Injection**: Uber FX throughout; all deps provided in `cmd/server/main.go` via `fx.Provide()`
- **Repository Pattern**: Interfaces in domain layer, implementations in repository layer
- **Service Composition**: Services depend on repository interfaces and other services; complex operations composed from smaller service methods
- **Temporal Workflows**: Long-running processes (billing cycles, invoice processing, subscription changes) are Temporal workflows for reliability and observability
- **Pub/Sub**: Event processing via Kafka topics: `events`, `events_lazy`, `events_post_processing`, `system_events`
## Core Domain Concepts
### Tenancy & Multi-Environment
- **Tenant** — Top-level isolation boundary (company/organization)
- **Environment** — Within each tenant (e.g., production, staging, development); all entities are scoped to tenant + environment
### Billing Entities
- **Customer** — End user/organization being billed
- **Plan** — Pricing model definition (seats, usage tiers, features)
- **Subscription** — Active plan assignment to a customer
- **Invoice** — Generated billing document
- **Payment** — Payment transaction records
### Metering & Usage
- **Meter** — Defines what to measure (API calls, compute time, storage)
- **Event** — Raw usage data ingested into the system
- **Feature** — Capabilities with usage limits or toggles
- **Entitlement** — Customer's access to features based on their plan
### Credits & Discounts
- **Wallet** — Prepaid credit balance for a customer
- **CreditGrant** — Allocation of credits (prepaid or promotional)
- **Coupon** — Discount codes and rules
- **CreditNote** — Refund or credit memo
### Pricing
- **Price** — Atomic pricing unit (per-seat, per-GB, etc.)
- **Addon** — Optional add-ons to plans
- **CostSheet** — Usage-based pricing calculations
## Development Workflows
### Adding a New API Endpoint
1. Define domain model in `internal/domain/<entity>/`
2. Create/update Ent schema in `ent/schema/<entity>.go`
3. Implement repository in `internal/repository/<entity>.go`
4. Implement service in `internal/ee/service/<entity>.go`
5. Create API handler in `internal/api/v1/<entity>.go`
6. Register route in `internal/api/router.go`
7. Add Swagger annotations
### Ent Schema Changes
1. Modify schema in `ent/schema/*.go`
2. Run `make generate-ent`
3. Run `make migrate-ent` (or `make generate-migration` for production SQL)
### Creating a Temporal Workflow
1. Define workflow interface in `internal/temporal/workflows/<name>_workflow.go`
2. Implement activities in `internal/temporal/activities/`
3. Register in `internal/temporal/registration.go`
4. Start workflow from service layer using `TemporalService`
### Integrating a Payment Provider
1. Create provider package in `internal/integration/<provider>/`
2. Implement common interfaces (payment, invoice sync, etc.)
3. Register in `internal/integration/factory.go`
4. Add configuration in `internal/config/config.yaml`
### Event Processing Flow
1. Events ingested via API → published to Kafka (`events` topic)
2. Consumer reads from Kafka
3. Processed by `EventConsumptionService` or `FeatureUsageTrackingService`
4. Stored in ClickHouse for analytics
5. Triggers downstream workflows (metering, alerting, billing)
### Activity Log
An ent hook on the writer client records every write to a registered entity (`internal/activity/entities.go`) and publishes it to ClickHouse after commit.
**Writes to registered entities must return their rows.** Use `Create…Save`, `UpdateOne…Save`, `CreateBulk…Save`, or for bulk writes `Update()…SaveReturning(ctx)` / `Delete()…ExecReturning(ctx)`. Never a count-only `Update()…Save/Exec` or `Delete()…Exec`: the hook cannot see those rows, so the change is missing from the activity log and only `activity.uncaptured` is counted.
**Keep the snapshot safe.** Every new field on a registered domain struct is stored in the snapshot unless tagged:
- `activity:"redact"` for secrets and gateway data (tokens, provider payloads, payment details); the value is stored as `"[redacted]"`.
- `activity:"drop"` for internal bookkeeping (`version`, sequences) and personal data (names, emails, phone numbers, addresses, free-form metadata); the field is left out.
**Actors are ids only.** `types.Actor` holds a type and an id, never a name or email; the read API resolves labels from Postgres. A new system actor whose id is not readable gets its label in `fixedActorLabel` (`internal/ee/service/activity.go`).
#### Onboarding an Entity
1. **Entity type:** add `SystemEntityType<Name>` in `internal/types/entity.go` if it does not exist.
2. **Domain converter:** expose `FromEnt(*ent.<Name>) *<Name>` in `internal/domain/<name>/model.go`.
3. **Tags:** add `activity:"redact"` / `activity:"drop"` on the domain struct fields as above.
4. **Registration:** add a `Registration` in `internal/activity/entities.go`: `EntityType`, `EntName` (the ent type name), `Snapshot: snapshotOf(<name>.FromEnt)`, `Label` (snapshot keys that name the entity, first non-empty wins) and `Actions`.
5. **Declared actions** (optional): add `<entity>.<past-tense verb>` constants in `internal/activity/actions.go`, list them in `Actions` highest priority first, and declare them in the service before the write: `ctx = activity.WithAction(ctx, id, activity.<Name>Voided)`. Without a declaration the entry is `<entity>.created`, `.updated` or `.deleted`.
6. **Customer:** an entry takes `customer_id` from the snapshot. If the entity has no `customer_id`, add `GetLinkedCustomerID(ctx, id)` to its repository (reading rows in any status) and register it in `NewLinkedCustomers` (`internal/activity/linked_customer.go`).
7. **Writes:** convert any count-only bulk update or delete of the entity to `SaveReturning` / `ExecReturning`.
8. **Tests:** add the entity to `entColumns` and `bareRow` in `internal/activity/registry_test.go`; the registry tests skip entities missing there.
## Testing Conventions
- **File location**: Tests live alongside implementation (e.g., `internal/ee/service/billing_test.go`)
- **Test utilities**: Use `internal/testutil/` for DB setup (`testutil.SetupTestDB()`), fixtures, and mocks
- **Table-driven tests**: Preferred for multiple scenarios
- **Integration tests**: Use real DB instances (via testcontainers or docker compose); avoid mocking Ent client
## Configuration
Configuration is managed via Viper with multiple sources (later sources override earlier):
1. `internal/config/config.yaml` (defaults)
2. Environment variables (prefix: `FLEXPRICE_`)
3. `.env` file (loaded by godotenv)
Examples: `FLEXPRICE_POSTGRES_HOST` overrides `postgres.host`, `FLEXPRICE_KAFKA_BROKERS` overrides `kafka.brokers`
**ClickHouse per-query memory limit:** Every ClickHouse query is bounded by a hardcoded limit of 90 GB (`max_memory_usage`).
## Deployment Modes
Set via `FLEXPRICE_DEPLOYMENT_MODE`:
- `local` — Runs all services (API, Consumer, Worker) in single process
- `api` — HTTP API only
- `consumer` — Kafka consumer only
- `temporal_worker` — Temporal workers only
- `admin` — Admin portal API only (`make run-local-admin`). `/v1` requires `FLEXPRICE_ADMIN_SECRET`. Does not serve the public API
Docker Compose runs these as separate services: `flexprice-api`, `flexprice-consumer`, `flexprice-worker`.
## Infrastructure Access (Local Dev)
| Service | URL |
|---------|-----|
| FlexPrice API | http://localhost:8080 |
| Temporal UI | http://localhost:8088 |
| Kafka UI | http://localhost:8084 (requires `--profile dev`) |
| ClickHouse HTTP | http://localhost:8123 |
## Common Operations
```bash
# Access PostgreSQL
docker compose exec postgres psql -U flexprice -d flexprice
# Access ClickHouse
docker compose exec clickhouse clickhouse-client --user=flexprice --password=flexprice123 --database=flexprice
# View service logs
docker compose logs -f flexprice-api
docker compose logs -f flexprice-consumer
docker compose logs -f flexprice-worker
```
Temporal UI (http://localhost:8088): monitor/debug workflow executions, manually trigger workflows, view workflow history.
## License
Core is AGPLv3 licensed. Enterprise features (`internal/ee/`) require a commercial license.
## Graphify
This project can maintain a code knowledge graph under **`graphify-out/`** (when the Graphify CLI is used).
- If **`graphify-out/graph.json`** exists, prefer **`graphify query "<question>"`** for codebase questions, **`graphify path "<A>" "<B>"`** for relationships, and **`graphify explain "<concept>"`** for focused subgraphs.
- If **`graphify-out/wiki/index.md`** exists, use it for broad navigation instead of undirected browsing.
- Read **`graphify-out/GRAPH_REPORT.md`** only for broad architecture review or when query/path/explain do not surface enough context.
- After modifying code in ways that change structure or dependencies, run **`graphify update .`** from the repository root to keep the graph current (AST-only). Use the **same command** day-to-day and for a **first-time** graph (creates `graphify-out/` when the CLI supports it); confirm with `graphify --help` if your build uses a separate init step.
Persistent human/agent-oriented architecture docs live under **`docs/`** (`REPO_MAP.md`, `ARCHITECTURE.md`, `DEPENDENCY_GRAPH.md`, `HOTSPOTS.md`, `FLOWS/`). For the same workflow in **any** repository, use the personal Cursor skill **`repo-architecture-intelligence`** in `~/.cursor/skills/repo-architecture-intelligence/SKILL.md` and copy prompts from **`USAGE.md`** in that folder.