@dewtech/dare-cli 2.7.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,312 @@
1
+ ---
2
+ description: Decisão e migração de Cargo workspace multi-crate para projetos Rust/Axum. Use durante design/blueprint para decidir o layout, ou quando um projeto single-crate cresceu além do que comporta confortavelmente.
3
+ globs: src/**/*.rs,Cargo.toml,DARE/DESIGN.md,DARE/BLUEPRINT.md
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Skill: Rust workspace — single-crate vs multi-crate
8
+
9
+ Esta skill cobre dois cenários relacionados:
10
+
11
+ - **Cenário A** — você está gerando `DESIGN.md`/`BLUEPRINT.md` de um projeto
12
+ Rust/Axum e precisa decidir desde já se ele nasce single-crate ou em
13
+ workspace.
14
+ - **Cenário B** — o projeto Rust **já existe** em single-crate e cresceu ao
15
+ ponto de doer (compilação lenta, fronteiras arquiteturais erodindo,
16
+ workers acoplados ao API server). Você precisa propor um plano de
17
+ migração para workspace.
18
+
19
+ A regra geral: **comece simples, migre quando os critérios objetivos
20
+ abaixo aparecerem.** Workspace é a estrutura idiomática Rust para projetos
21
+ maduros, mas é overengineering para o "hello world".
22
+
23
+ ---
24
+
25
+ ## Cenário A — Decisão na fase Design/Blueprint
26
+
27
+ Antes de escrever o BLUEPRINT da stack `rust-axum`, decida: single-crate
28
+ ou workspace? Os critérios são objetivos:
29
+
30
+ ### Comece **single-crate** quando TODOS forem verdadeiros
31
+
32
+ - Apenas **1 binário** (HTTP server).
33
+ - Estimativa de **< 30 arquivos `.rs`** no `src/`.
34
+ - **1–2 sistemas externos** (apenas DB; ou DB + cache).
35
+ - **Equipe ≤ 2 devs** trabalhando ativamente.
36
+ - Nenhum requisito de deploy independente para subcomponentes.
37
+
38
+ ### Comece **workspace multi-crate** quando QUALQUER um for verdadeiro
39
+
40
+ - **≥ 2 binários** previstos (API + worker; API + admin; API + CLI).
41
+ - **Múltiplos sistemas externos** (3+: PG + Redis + Rabbit + Qdrant +
42
+ Neo4j + …).
43
+ - **Deploy independente** desejado (workers em pods separados no k8s,
44
+ scaling independente).
45
+ - **Fronteiras arquiteturais** críticas (domain puro sem HTTP/DB; SDK que
46
+ vai virar crate público; biblioteca de cliente compartilhada com outras
47
+ apps).
48
+ - **Equipe ≥ 3 devs** trabalhando em módulos diferentes em paralelo.
49
+
50
+ ### Layout convencional para workspace
51
+
52
+ Use o prefixo do projeto (`<p>`) como namespace de todos os crates — por
53
+ exemplo `wa-` para uma `wa-business-api`, `agent-` para um `agent-ai`,
54
+ `chat-` para um `chat-service`.
55
+
56
+ ```
57
+ projeto/
58
+ ├── Cargo.toml # workspace root, deps centralizadas
59
+ ├── docker-compose.yml
60
+ ├── Dockerfile
61
+ ├── crates/
62
+ │ ├── <p>-domain/ (lib) # entities puras: structs, errors, types
63
+ │ ├── <p>-config/ (lib) # AppConfig::from_env()
64
+ │ ├── <p>-db/ (lib) # sqlx pool, migrations
65
+ │ ├── <p>-cache/ (lib) # redis/moka (se houver)
66
+ │ ├── <p>-queue/ (lib) # lapin/kafka (se houver)
67
+ │ ├── <p>-crypto/ (lib) # bcrypt, jwt, aes (se aplicável)
68
+ │ ├── <p>-meta-client/ (lib) # cliente externo (Meta API, Stripe…)
69
+ │ ├── <p>-services/ (lib) # business logic, sem HTTP
70
+ │ ├── <p>-api/ (bin + lib) # HTTP server (handlers, router, mw)
71
+ │ ├── <p>-worker-<X>/ (bin) # 1 binário por tipo de worker
72
+ │ ├── <p>-admin/ (bin) # CLI admin (se houver)
73
+ │ └── <p>-e2e/ (tests) # integration tests cross-crate
74
+ └── xtask/ # scripts de build/dev em Rust
75
+ ```
76
+
77
+ Granularidade: **um crate por bounded context**, não por arquivo. Resista
78
+ ao "crate util" / "crate common" — vira lixeira.
79
+
80
+ ### Template de `Cargo.toml` raiz (workspace)
81
+
82
+ ```toml
83
+ [workspace]
84
+ resolver = "2"
85
+ members = [
86
+ "crates/<p>-domain",
87
+ "crates/<p>-config",
88
+ "crates/<p>-db",
89
+ "crates/<p>-services",
90
+ "crates/<p>-api",
91
+ "crates/<p>-worker-x",
92
+ "crates/<p>-e2e",
93
+ "xtask",
94
+ ]
95
+
96
+ [workspace.package]
97
+ version = "0.1.0"
98
+ edition = "2021"
99
+ rust-version = "1.80"
100
+ license = "Proprietary"
101
+
102
+ [workspace.dependencies]
103
+ # Runtime
104
+ tokio = { version = "1.40", features = ["full"] }
105
+ async-trait = "0.1"
106
+
107
+ # Web
108
+ axum = { version = "0.7", features = ["macros"] }
109
+ tower = "0.5"
110
+ tower-http = { version = "0.6", features = ["cors", "trace", "timeout"] }
111
+
112
+ # Serialization
113
+ serde = { version = "1.0", features = ["derive"] }
114
+ serde_json = "1.0"
115
+
116
+ # Database
117
+ sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "uuid", "chrono", "migrate"] }
118
+
119
+ # Errors / observability
120
+ thiserror = "1.0"
121
+ anyhow = "1.0"
122
+ tracing = "0.1"
123
+ tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
124
+
125
+ # Types
126
+ uuid = { version = "1.10", features = ["v4", "serde"] }
127
+ chrono = { version = "0.4", features = ["serde"] }
128
+
129
+ [profile.release]
130
+ opt-level = 3
131
+ lto = "thin"
132
+ codegen-units = 1
133
+ strip = true
134
+ ```
135
+
136
+ Cada crate filho herda via `version.workspace = true` e
137
+ `<dep> = { workspace = true }` — assim você atualiza axum/tokio/etc. em
138
+ **uma linha** no root.
139
+
140
+ ### Diagrama no BLUEPRINT.md
141
+
142
+ Sempre desenhe o grafo de dependências em Mermaid. Exemplo:
143
+
144
+ ```mermaid
145
+ graph TD
146
+ api[<p>-api]
147
+ admin[<p>-admin]
148
+ worker[<p>-worker-flow]
149
+ services[<p>-services]
150
+ db[<p>-db]
151
+ cache[<p>-cache]
152
+ queue[<p>-queue]
153
+ domain[<p>-domain]
154
+
155
+ api --> services
156
+ admin --> services
157
+ worker --> services
158
+ services --> db
159
+ services --> cache
160
+ services --> queue
161
+ services --> domain
162
+ db --> domain
163
+ ```
164
+
165
+ Regras de seta:
166
+ - **`<p>-domain` no fundo:** ninguém depende para baixo dele.
167
+ - **`<p>-services` no meio:** depende de domain/db/cache/queue, NUNCA de api.
168
+ - **Binários (api, admin, worker) no topo:** dependem de services, nunca
169
+ um do outro.
170
+
171
+ ---
172
+
173
+ ## Cenário B — Migração de single-crate para workspace
174
+
175
+ Use quando você herda um projeto single-crate maduro e percebe que está
176
+ apertando.
177
+
178
+ ### Sintomas objetivos de "hora de migrar"
179
+
180
+ - `src/` tem **> 30 arquivos `.rs`** ou **> 6 subpastas top-level**.
181
+ - Existem múltiplos "domínios verticais" misturados (`handlers`, `services`,
182
+ `repositories`, `workers`, `integrators`, `mcp`, `acp`, `skills`, `tools`…).
183
+ - Workers usam `tokio::spawn` no mesmo processo do API server (não
184
+ conseguem escalar independente).
185
+ - `cargo build` incremental > 10s.
186
+ - Múltiplas pessoas mexem no mesmo crate, gerando conflitos de merge
187
+ frequentes.
188
+ - Quer expor parte do código (cliente, SDK) como crate publicável.
189
+
190
+ ### Plano de migração em 4 PRs
191
+
192
+ Migração **incremental**, NUNCA big-bang. Cada PR deve passar build/test
193
+ no fim e ser deployável.
194
+
195
+ #### PR 1 — Workers (mais isolados, menor risco)
196
+
197
+ ```
198
+ src/workers/ → crates/<p>-worker-<nome>/
199
+ ├── Cargo.toml
200
+ └── src/main.rs (era workers/<nome>.rs)
201
+ ```
202
+
203
+ - Cada `tokio::spawn(worker_x)` que estava no `main.rs` da API vira um
204
+ **binário próprio**.
205
+ - O API server **para de fazer `start_workers()`** — workers sobem como
206
+ processo independente.
207
+ - `docker-compose.yml` ganha um `service` novo por worker, com `command:
208
+ ["./<p>-worker-<nome>"]`.
209
+ - Em produção (k8s), cada worker vira um Deployment com seu próprio
210
+ scaling.
211
+
212
+ #### PR 2 — Integrators (clientes externos)
213
+
214
+ ```
215
+ src/integrators/{llm, neo4j, qdrant}.rs → crates/<p>-integrators/src/lib.rs
216
+ ```
217
+
218
+ - Tudo que fala com o mundo externo (LLM, Neo4j, Qdrant, Stripe, Meta,
219
+ …) vira lib.
220
+ - API e workers passam a importar via `<p>-integrators = { path = "../<p>-integrators" }`.
221
+ - `<p>-integrators` NÃO depende de domain/services — é folha do grafo, só
222
+ conhece os tipos de request/response da API externa.
223
+
224
+ #### PR 3 — Domain (entidades puras)
225
+
226
+ ```
227
+ src/models/ \
228
+ src/dto/ → crates/<p>-domain/src/lib.rs
229
+ src/error.rs (parte que é DomainError) /
230
+ ```
231
+
232
+ - `<p>-domain` tem **dependências MÍNIMAS** no Cargo.toml: apenas `serde`,
233
+ `uuid`, `chrono`, `thiserror`. Nada de axum, sqlx, redis.
234
+ - Force a regra fazendo `cargo build -p <p>-domain` falhar se alguém
235
+ importar HTTP/DB.
236
+ - API, services, workers, integrators todos passam a usar
237
+ `<p>-domain = { path = "../<p>-domain" }` em vez de `crate::models::`.
238
+
239
+ #### PR 4 — API e raiz do workspace
240
+
241
+ ```
242
+ Cargo.toml (raiz) → vira [workspace] puro, sem [package]
243
+ src/ (resto) → crates/<p>-api/src/
244
+ ├── main.rs
245
+ ├── lib.rs
246
+ ├── handlers/
247
+ ├── routes.rs
248
+ └── middleware/
249
+ ```
250
+
251
+ - `Cargo.toml` raiz só tem `[workspace]`, `[workspace.package]`,
252
+ `[workspace.dependencies]`, e `[profile.*]`.
253
+ - API (binário principal) vai pra `crates/<p>-api/`.
254
+ - Centralize todas as deps em `workspace.dependencies` — cada crate herda
255
+ com `<dep> = { workspace = true }`.
256
+
257
+ ### Validação após CADA PR
258
+
259
+ ```bash
260
+ cargo build --workspace --all-targets
261
+ cargo test --workspace
262
+ cargo clippy --workspace --all-targets -- -D warnings
263
+ ```
264
+
265
+ Mais o smoke E2E do projeto (subir compose, hitar `/healthz`, login,
266
+ operação CRUD básica). Se algum quebrar, o PR está incompleto.
267
+
268
+ ### Antipatterns ao migrar
269
+
270
+ | Antipattern | Por que evitar |
271
+ |-------------|----------------|
272
+ | Big-bang (1 PR mexe em tudo) | Impossível revisar; impossível reverter; quebra histórico do git blame |
273
+ | Crate `common` / `shared` / `utils` | Vira lixeira; ninguém sabe onde colocar; viola SRP |
274
+ | Crate por arquivo (granularidade demais) | 50 crates de 100 linhas cada vai parar a build |
275
+ | Refactor de lógica + migração no mesmo PR | Não dá pra revisar; bug fica escondido |
276
+ | Mover testes em PR separado | Crate sem test em produção é bug em standby |
277
+ | Não atualizar `xtask`/scripts CI | Pipeline quebra; deploy vira pesadelo |
278
+
279
+ ### Tradução de imports
280
+
281
+ | Antes (single-crate) | Depois (workspace) |
282
+ |----------------------|--------------------|
283
+ | `use crate::models::User;` | `use <p>_domain::User;` |
284
+ | `use crate::services::auth::login;` | `use <p>_services::auth::login;` |
285
+ | `use crate::handlers::health::router;` | `use crate::handlers::health::router;` *(stay — está dentro do mesmo crate)* |
286
+ | `use crate::integrators::llm::Gemini;` | `use <p>_integrators::llm::Gemini;` |
287
+
288
+ Note os hifens viram underlines (Cargo names → Rust idents).
289
+
290
+ ---
291
+
292
+ ## Quando NÃO migrar
293
+
294
+ - Projeto < 30 arquivos com 1 binário e 1 dev → single-crate é mais
295
+ simples, manutenção é trivial.
296
+ - Você está no meio de um sprint crítico → migração é refactor estrutural,
297
+ não combina com prazo.
298
+ - Não há sinais reais de dor (build < 5s, sem conflitos, sem segundo
299
+ binário planejado).
300
+
301
+ A migração tem custo. Faça quando o ganho compensar.
302
+
303
+ ## Checklist final (qualquer cenário)
304
+
305
+ - [ ] Critérios objetivos de decisão documentados no BLUEPRINT
306
+ - [ ] Lista de crates com responsabilidade de cada um
307
+ - [ ] Diagrama Mermaid do grafo de dependências
308
+ - [ ] `Cargo.toml` workspace com `workspace.dependencies` centralizadas
309
+ - [ ] `<p>-domain` sem dependência de framework HTTP/DB
310
+ - [ ] Cada binário com `[[bin]]` e `path = "src/main.rs"` no seu Cargo.toml
311
+ - [ ] Migração planejada em PRs incrementais, não big-bang
312
+ - [ ] Ralph Loop verde após cada PR (`cargo build/test/clippy --workspace`)