99 lines
5.7 KiB
Markdown
99 lines
5.7 KiB
Markdown
# Architecture
|
||
|
||
This document defines the development principles for this Go project. It is inward-facing: developers and LLM coding agents should use it to preserve the project’s shape, boundaries, and invariants as the code evolves.
|
||
|
||
## Project Shape
|
||
|
||
Default to a small, explicit, dependency-light Go application. Keep the design modular enough to test and change safely, but do not add abstraction unless it protects a real boundary or enables a real extension point.
|
||
|
||
Business/domain logic should live outside CLI, transport, and external-adapter packages.
|
||
|
||
## Dependency Policy
|
||
|
||
Prefer the Go standard library where practical.
|
||
|
||
Use external dependencies only when justified by correctness, security, interoperability, or substantial complexity reduction. Good reasons include complex security-sensitive behavior, such as HTML sanitization, or widely used de facto standards, such as YAML parsing.
|
||
|
||
Avoid dependencies for small conveniences. Do not let external dependency types leak across internal package boundaries unless the dependency is itself the explicit public contract of that package.
|
||
|
||
## Package Layout
|
||
|
||
Use this layout unless the project has a documented reason to differ:
|
||
|
||
- `internal/app`: application orchestration and top-level use cases.
|
||
- `internal/cli`: CLI command definitions, flags, argument parsing, and command wiring.
|
||
- `internal/config`: configuration structs, defaults, loading, precedence, and validation.
|
||
- `internal/adapters/<name>`: adapters for external CLIs, APIs, databases, object stores, or libraries.
|
||
- `internal/api`: HTTP API handlers and request/response types, when the application exposes an HTTP API.
|
||
- `internal/transport/http`: HTTP client code, when the application calls HTTP services.
|
||
|
||
Package-private implementation constants may live near the package that owns them, preferably in `constants.go` when useful.
|
||
|
||
## Configuration
|
||
|
||
Centralize configuration loading, processing, precedence, defaults, and validation in `internal/config`.
|
||
|
||
The goal is to make configuration discoverable and avoid implicit or hidden operational values. User-visible defaults and cross-package operational defaults should be defined in `internal/config/defaults.go`.
|
||
|
||
Unless documented otherwise, precedence is:
|
||
|
||
1. CLI flags
|
||
2. environment variables
|
||
3. configuration file
|
||
4. built-in defaults
|
||
|
||
Prefer YAML configuration unless the project has a strong reason to use another format. Config files should be discovered at `/usr/local/etc/<app_name>/config.yml`, with a CLI override via `--config`.
|
||
|
||
Configuration files should not contain raw secrets unless the application is explicitly designed for that. Prefer environment variables or secret files for secrets.
|
||
|
||
## Adapters and External Integrations
|
||
|
||
Use a hexagonal architecture style for external integrations.
|
||
|
||
External adapters belong under `internal/adapters/<name>`. If an adapter uses an external dependency, that dependency’s interface must not leak outside the adapter package. Other packages should interact only with the adapter’s API, so the dependency can be swapped, upgraded, or removed without touching unrelated code.
|
||
|
||
Adapters should be thin. Domain decisions belong in application/domain packages, not inside adapter glue.
|
||
|
||
## Modules, Stages, and Registries
|
||
|
||
When the application has stages or modules, each major stage/module should live in its own package and have an explicit input/output contract.
|
||
|
||
The orchestrator should be able to compose, skip, resume, or run individual stages/modules when their prerequisites are satisfied. Ordering should be explicit: use a default sequence, dependency graph, or documented orchestration rule.
|
||
|
||
If users can select modules, stages, validators, renderers, or adapters, selection should go through a registry or equivalent mechanism rather than scattered conditionals.
|
||
|
||
## Embedded Assets
|
||
|
||
Store embedded JSON schemas, Markdown prompts, templates, and similar assets as separate files, not inline string literals, unless there is a strong reason otherwise.
|
||
|
||
## Errors and Logging
|
||
|
||
Errors should be actionable and preserve context. Wrap errors with operation and path/resource context. CLI code should convert internal errors into concise user-facing messages.
|
||
|
||
Errors and logs must not expose secrets.
|
||
|
||
Use structured logging where practical. Logs should describe operations, paths, external calls, retries, and failure causes, but should not include large user data by default.
|
||
|
||
## Context, Timeouts, and Cancellation
|
||
|
||
Long-running operations should accept `context.Context`. External calls, subprocesses, HTTP requests, storage operations, and multi-stage workflows should respect cancellation and timeouts.
|
||
|
||
## State, Files, and Safety
|
||
|
||
If the application writes durable state, writes should be atomic where practical. Multi-step workflows should preserve enough state to support inspection, retry, or resume after failure.
|
||
|
||
Code that deletes, moves, or overwrites files must use narrow, explicit paths. Avoid broad parent-directory operations. Cleanup that can cause data loss must be opt-in.
|
||
|
||
## Testing
|
||
|
||
Core logic should be testable without real external services. Use fakes, fixtures, or local test doubles for adapters where practical.
|
||
|
||
Config examples should be load-tested. Important CLI workflows should have parser or command tests. Stage/module contracts should have focused tests that do not require running the full application unless end-to-end coverage is intentional.
|
||
|
||
## Documentation
|
||
|
||
Documentation should follow the project documentation policy. Keep user docs focused on implemented behavior. Put future, planned, or aspirational work only under `docs/roadmap/`.
|
||
|
||
When changing architecture, config, CLI behavior, adapters, or stage/module contracts, update the relevant docs and examples in the same change.
|
||
|