Skip to content

Commit 080258d

Browse files
committed
feat(scaffold): align monorepo layout and docker build patterns
1 parent 8118b4e commit 080258d

File tree

5 files changed

+213
-171
lines changed

5 files changed

+213
-171
lines changed

docs/ai-assistant-mode.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ Enabled via `WithDebugEndpoints()` or `FRAME_DEBUG_ENDPOINTS=true`.
6767

6868
All responses must be deterministic and safe to parse by agents.
6969

70+
Security note:
71+
- Keep debug endpoints disabled in production unless explicitly protected.
72+
7073
## CLI (v0.1)
7174

7275
```

docs/ai-assistants.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,11 @@ For monorepos, prefer:
3535

3636
```text
3737
/cmd
38-
/monolith
39-
/users
40-
/billing
38+
/users/main.go
39+
/billing/main.go
4140
/apps
42-
/users
43-
/billing
41+
/users/cmd/main.go
42+
/billing/cmd/main.go
4443
/pkg
4544
/plugins
4645
/openapi

docs/blueprints.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ These rules keep blueprints safe for AI agents and humans: extension is the defa
8181
Base:
8282

8383
```yaml
84-
service: users
84+
service:
85+
name: users
8586
http:
8687
- name: list-users
8788
method: GET

docs/monorepo.md

Lines changed: 67 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,169 +1,128 @@
11
# Monorepo by Default: Monolith or Polylith
22

3-
Frame assumes a **monorepo-first** layout that can run as:
3+
Frame uses a **monorepo-first** layout. You can run the same codebase as:
44

5-
- **Monolith**: one binary, one service
6-
- **Polylith**: multiple independent binaries from one repo
5+
- **Monolith**: one Frame service, one mux, many routes.
6+
- **Polylith**: many independent binaries, each service deployed separately.
77

8-
Both modes share the same shared packages and conventions. The only difference is **how many entrypoints you build**.
9-
10-
## Canonical Layout (Monorepo)
8+
## Canonical Layout
119

1210
```text
1311
/README.md
1412
/go.mod
1513
/cmd
16-
/monolith
17-
main.go
18-
/users
19-
main.go
20-
/billing
21-
main.go
14+
/users/main.go
15+
/billing/main.go
2216
/apps
2317
/users
24-
/cmd
25-
/users
26-
main.go
27-
/users/Dockerfile
28-
/service
18+
/cmd/main.go
19+
/service/routes.go
20+
/queues
2921
/config
3022
/migrations
3123
/tests
24+
/Dockerfile
3225
/billing
33-
/cmd
34-
/billing
35-
main.go
36-
/billing/Dockerfile
37-
/service
26+
/cmd/main.go
27+
/service/routes.go
28+
/queues
3829
/config
3930
/migrations
4031
/tests
32+
/Dockerfile
4133
/pkg
42-
/shared
4334
/plugins
4435
/openapi
36+
/shared
4537
/configs
4638
/Dockerfile
4739
```
4840

49-
## Monolith Mode
50-
51-
A single entrypoint composes multiple modules into one binary and runs one Frame service with one mux:
41+
## Monolith Mode (Single Service)
5242

53-
```text
54-
/cmd/monolith/main.go
55-
/apps/users/service
56-
/apps/billing/service
57-
```
43+
Monolith means **one `frame.Service` + one `http.ServeMux`**. Multiple app routes are mounted into that one mux.
5844

59-
All routes are wired into the same mux in one process. This is ideal for:
45+
Example shape:
6046

61-
- fast local development
62-
- smaller deployments
63-
- shared runtime state
64-
65-
## Polylith Mode (Composable)
47+
```go
48+
mux := http.NewServeMux()
49+
users.RegisterRoutes(mux)
50+
billing.RegisterRoutes(mux)
6651

67-
Each service has its **own entrypoint** under `/apps/<service>/cmd/<service>`, and a Dockerfile next to it. Shared libraries live in `/pkg`:
52+
ctx, svc := frame.NewService(
53+
frame.WithName("monolith"),
54+
frame.WithHTTPHandler(mux),
55+
)
6856

69-
```text
70-
/apps/users/cmd/users/main.go
71-
/apps/users/cmd/users/Dockerfile
72-
/apps/billing/cmd/billing/main.go
73-
/apps/billing/cmd/billing/Dockerfile
74-
/pkg/...
57+
if err := svc.Run(ctx, ":8080"); err != nil { ... }
7558
```
7659

77-
Each binary is independent, but uses the same `/apps` and `/pkg` packages. This matches the structure used in `service-profile`.
78-
79-
## How to Switch Modes
80-
81-
You do **not** need to restructure anything. You only:
82-
83-
- add or remove `apps/<service>/cmd/<service>/main.go` entrypoints
84-
- build different binaries or Docker images
60+
## Polylith Mode (Independent Binaries)
8561

86-
This makes the repo **composable** by default.
62+
Each app has its own binary entrypoint and Dockerfile:
8763

88-
## Recommended Conventions
64+
- `apps/<service>/cmd/main.go`
65+
- `apps/<service>/Dockerfile`
8966

90-
- `/apps/<service>` is the source of truth for a service
91-
- `/apps/<service>/cmd/<service>` is the polylith entrypoint
92-
- `/cmd/<service>` is the monorepo-level entrypoint (optional)
93-
- `/pkg` is shared infrastructure and cross-cutting plugins
94-
- `/configs` holds environment or YAML configs for all services
67+
The repo-level `cmd/<service>/main.go` gives a consistent top-level build/run entrypoint.
9568

9669
## One-Command Scaffold
9770

98-
Frame includes a scaffold tool that creates the monorepo layout with per-service entrypoints and Dockerfiles.
99-
10071
```bash
10172
go run github.com/pitabwire/frame/cmd/frame@latest init \
10273
-root . \
10374
-services users,billing \
10475
-module your/module
10576
```
10677

107-
This generates:
78+
`-module` is optional. If omitted, Frame tries `go.mod`, then falls back to `example.com/project`.
10879

109-
- `/apps/<service>/cmd/<service>/main.go`
110-
- `/apps/<service>/cmd/<service>/Dockerfile`
111-
- `/cmd/monolith/main.go`
112-
- `/Dockerfile`
113-
- `/pkg` and `/configs` folders
80+
Generated artifacts:
11481

115-
## Example: Polylith Entry Point
82+
- `apps/<service>/cmd/main.go`
83+
- `apps/<service>/service/routes.go`
84+
- `apps/<service>/Dockerfile`
85+
- `cmd/<service>/main.go`
86+
- `Dockerfile`
87+
- `pkg` and `configs`
11688

117-
```go
118-
package main
89+
## Build Patterns
11990

120-
import (
121-
"context"
122-
"log"
91+
Polylith binary:
12392

124-
"github.com/pitabwire/frame"
125-
"your/module/apps/users/service"
126-
)
93+
```bash
94+
go build ./apps/users/cmd
95+
```
12796

128-
func main() {
129-
ctx, svc := frame.NewService(
130-
frame.WithName("users"),
131-
frame.WithHTTPHandler(service.Router()),
132-
)
97+
Monorepo-level binary for one app:
13398

134-
if err := svc.Run(ctx, ":8080"); err != nil {
135-
log.Fatal(err)
136-
}
137-
}
99+
```bash
100+
go build ./cmd/users
138101
```
139102

140-
## Example: Monolith Entry Point
103+
Single-binary monolith is generated from blueprints in monolith mode (`frame build`), producing `cmd/main.go` that composes all routes into one mux.
141104

142-
```go
143-
package main
105+
## Docker Build Patterns
144106

145-
import (
146-
"context"
147-
"log"
148-
"net/http"
107+
Monorepo-level Dockerfile (`/Dockerfile`) uses a multi-stage builder and copies:
149108

150-
"github.com/pitabwire/frame"
151-
"your/module/apps/users/service"
152-
"your/module/apps/billing/service"
153-
)
109+
- `/apps`
110+
- `/pkg`
111+
- `/cmd`
112+
113+
Build an app via top-level cmd entrypoint:
154114

155-
func main() {
156-
mux := http.NewServeMux()
157-
service.RegisterRoutes(mux)
158-
billing.RegisterRoutes(mux)
115+
```bash
116+
docker build -t users-service --build-arg APP=users .
117+
```
159118

160-
ctx, svc := frame.NewService(
161-
frame.WithName("monolith"),
162-
frame.WithHTTPHandler(mux),
163-
)
119+
Per-service Dockerfile (`/apps/<service>/Dockerfile`) copies:
164120

165-
if err := svc.Run(ctx, ":8080"); err != nil {
166-
log.Fatal(err)
167-
}
168-
}
121+
- `/apps/<service>`
122+
- `/pkg`
123+
124+
Build a single polylith app:
125+
126+
```bash
127+
docker build -t users-service -f apps/users/Dockerfile .
169128
```

0 commit comments

Comments
 (0)