69 lines
3.3 KiB
Markdown
69 lines
3.3 KiB
Markdown
---
|
|
name: add-rpc
|
|
description: Scaffold a new gRPC RPC end-to-end (proto, buf generate, hexagonal ports/app/adapter layers, tests, README) following this repo's established pattern across ha-gateway, ai-gateway, and discord-bot.
|
|
---
|
|
|
|
# Add a gRPC RPC
|
|
|
|
Use this whenever a new RPC or field is being added to `ha-gateway`, `ai-gateway`, or a new
|
|
gRPC client call is being added to `discord-bot`. All three services share the same
|
|
hexagonal layering, so follow this order — do not skip layers or reach across them.
|
|
|
|
## Steps
|
|
|
|
1. **Identify the target service and proto package.**
|
|
- `ha-gateway` → `proto/ha/v1/*.proto` (packages: `entity`, `light`, `switch`, `event`, `common`)
|
|
- `ai-gateway` → `proto/ai/v1/ai.proto`
|
|
- `discord-bot` has no proto of its own; it only consumes `ha.v1` / `ai.v1` clients.
|
|
|
|
2. **Edit the `.proto` file** under `proto/<pkg>/v1/`. Add the message/RPC there first —
|
|
it is the source of truth; `gen/` is derived from it.
|
|
|
|
3. **Regenerate generated code:**
|
|
```bash
|
|
buf generate
|
|
```
|
|
This writes into `gen/<pkg>/v1/`. Never hand-edit files under `gen/`.
|
|
|
|
4. **Extend the port interface** in `internal/core/ports/`:
|
|
- `ports/driving/*.go` — the interface the gRPC adapter calls *into* (app-facing, inbound).
|
|
- `ports/driven/*.go` — the interface the app calls *out* to (HA REST, Ollama, ha-gateway
|
|
client, etc.), if the new RPC needs a new external call.
|
|
- `core/domain/*.go` — add/extend domain types here, not in adapter or proto types.
|
|
|
|
5. **Implement orchestration in `internal/app/`.** This is where business logic and
|
|
validation live — it depends only on `core/domain` and `core/ports`, never on
|
|
`adapters/*` types directly. Look at an existing file (e.g. `ha-gateway/internal/app/light.go`)
|
|
for the shape: a struct holding driven-port dependencies, constructed via `NewXApp(...)`.
|
|
|
|
6. **Wire the primary adapter** in `internal/adapters/primary/grpc/<feature>.go`. Implement
|
|
the generated server interface method, translate between proto types and domain types
|
|
(see `mapping.go` in ha-gateway for the existing translation pattern), and call the app
|
|
layer. Keep proto <-> domain conversion here, not in `app`.
|
|
|
|
7. **If the RPC needs a new outbound call**, implement it in the relevant
|
|
`internal/adapters/secondary/<name>/client.go`, implementing the `driven` port interface
|
|
from step 4.
|
|
|
|
8. **Add a test** using the repo's convention: plain `testing` package (no testify), with a
|
|
hand-written mock struct that implements the driven port interface using function fields
|
|
(see `ha-gateway/internal/app/entity_test.go` or `ai-gateway/internal/app/query_test.go`
|
|
for the pattern). Table-driven `t.Run` subtests are the norm.
|
|
|
|
9. **Update the service's `README.md`:**
|
|
- Move the RPC between "Implemented" and "Stubbed" in the gRPC API section if its status
|
|
changed.
|
|
- Add any new environment variables to the Configuration table (and to `.env.example`).
|
|
- Add any new package to the Package Map section if you created one.
|
|
|
|
10. **Verify:**
|
|
```bash
|
|
cd <service> && go vet ./... && go test ./...
|
|
```
|
|
|
|
## Layering rule to enforce
|
|
|
|
Dependencies point inward only: `adapters` → `app` → `core`. Never import
|
|
`internal/adapters/*` from `internal/app` or `internal/core`. Only `cmd/<entrypoint>/main.go`
|
|
is allowed to import across all layers to wire dependencies together.
|