maestro-bundle 1.0.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.
- package/README.md +91 -0
- package/package.json +25 -0
- package/src/cli.mjs +212 -0
- package/templates/bundle-ai-agents/.spec/constitution.md +33 -0
- package/templates/bundle-ai-agents/AGENTS.md +140 -0
- package/templates/bundle-ai-agents/skills/agent-orchestration/SKILL.md +132 -0
- package/templates/bundle-ai-agents/skills/api-design/SKILL.md +100 -0
- package/templates/bundle-ai-agents/skills/clean-architecture/SKILL.md +99 -0
- package/templates/bundle-ai-agents/skills/context-engineering/SKILL.md +98 -0
- package/templates/bundle-ai-agents/skills/database-modeling/SKILL.md +59 -0
- package/templates/bundle-ai-agents/skills/docker-containerization/SKILL.md +114 -0
- package/templates/bundle-ai-agents/skills/eval-testing/SKILL.md +115 -0
- package/templates/bundle-ai-agents/skills/memory-management/SKILL.md +106 -0
- package/templates/bundle-ai-agents/skills/prompt-engineering/SKILL.md +66 -0
- package/templates/bundle-ai-agents/skills/rag-pipeline/SKILL.md +128 -0
- package/templates/bundle-ai-agents/skills/testing-strategy/SKILL.md +95 -0
- package/templates/bundle-base/AGENTS.md +118 -0
- package/templates/bundle-base/skills/branch-strategy/SKILL.md +42 -0
- package/templates/bundle-base/skills/code-review/SKILL.md +54 -0
- package/templates/bundle-base/skills/commit-pattern/SKILL.md +58 -0
- package/templates/bundle-data-pipeline/.spec/constitution.md +32 -0
- package/templates/bundle-data-pipeline/AGENTS.md +115 -0
- package/templates/bundle-data-pipeline/skills/data-preprocessing/SKILL.md +75 -0
- package/templates/bundle-data-pipeline/skills/docker-containerization/SKILL.md +114 -0
- package/templates/bundle-data-pipeline/skills/feature-engineering/SKILL.md +76 -0
- package/templates/bundle-data-pipeline/skills/mlops-pipeline/SKILL.md +77 -0
- package/templates/bundle-data-pipeline/skills/model-training/SKILL.md +68 -0
- package/templates/bundle-data-pipeline/skills/rag-pipeline/SKILL.md +128 -0
- package/templates/bundle-frontend-spa/.spec/constitution.md +32 -0
- package/templates/bundle-frontend-spa/AGENTS.md +107 -0
- package/templates/bundle-frontend-spa/skills/authentication/SKILL.md +90 -0
- package/templates/bundle-frontend-spa/skills/component-design/SKILL.md +115 -0
- package/templates/bundle-frontend-spa/skills/e2e-testing/SKILL.md +101 -0
- package/templates/bundle-frontend-spa/skills/integration-api/SKILL.md +95 -0
- package/templates/bundle-frontend-spa/skills/react-patterns/SKILL.md +130 -0
- package/templates/bundle-frontend-spa/skills/responsive-layout/SKILL.md +65 -0
- package/templates/bundle-frontend-spa/skills/state-management/SKILL.md +86 -0
- package/templates/bundle-jhipster-microservices/.spec/constitution.md +37 -0
- package/templates/bundle-jhipster-microservices/AGENTS.md +307 -0
- package/templates/bundle-jhipster-microservices/skills/ci-cd-pipeline/SKILL.md +112 -0
- package/templates/bundle-jhipster-microservices/skills/clean-architecture/SKILL.md +99 -0
- package/templates/bundle-jhipster-microservices/skills/ddd-tactical/SKILL.md +138 -0
- package/templates/bundle-jhipster-microservices/skills/jhipster-angular/SKILL.md +97 -0
- package/templates/bundle-jhipster-microservices/skills/jhipster-docker-k8s/SKILL.md +183 -0
- package/templates/bundle-jhipster-microservices/skills/jhipster-entities/SKILL.md +87 -0
- package/templates/bundle-jhipster-microservices/skills/jhipster-gateway/SKILL.md +96 -0
- package/templates/bundle-jhipster-microservices/skills/jhipster-kafka/SKILL.md +145 -0
- package/templates/bundle-jhipster-microservices/skills/jhipster-registry/SKILL.md +83 -0
- package/templates/bundle-jhipster-microservices/skills/jhipster-service/SKILL.md +131 -0
- package/templates/bundle-jhipster-microservices/skills/testing-strategy/SKILL.md +95 -0
- package/templates/bundle-jhipster-monorepo/.spec/constitution.md +32 -0
- package/templates/bundle-jhipster-monorepo/AGENTS.md +227 -0
- package/templates/bundle-jhipster-monorepo/skills/clean-architecture/SKILL.md +99 -0
- package/templates/bundle-jhipster-monorepo/skills/ddd-tactical/SKILL.md +138 -0
- package/templates/bundle-jhipster-monorepo/skills/jhipster-angular/SKILL.md +166 -0
- package/templates/bundle-jhipster-monorepo/skills/jhipster-entities/SKILL.md +141 -0
- package/templates/bundle-jhipster-monorepo/skills/jhipster-liquibase/SKILL.md +95 -0
- package/templates/bundle-jhipster-monorepo/skills/jhipster-security/SKILL.md +89 -0
- package/templates/bundle-jhipster-monorepo/skills/jhipster-spring/SKILL.md +155 -0
- package/templates/bundle-jhipster-monorepo/skills/testing-strategy/SKILL.md +95 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: state-management
|
|
3
|
+
description: Gerenciar estado com Zustand para estado global e React Query para server state. Use quando precisar gerenciar estado da aplicação, cache de API, ou estado compartilhado entre componentes.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# State Management
|
|
7
|
+
|
|
8
|
+
## Regra de ouro
|
|
9
|
+
|
|
10
|
+
| Tipo de estado | Solução | Exemplo |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| Server state (API data) | React Query | Lista de demands, dados de agentes |
|
|
13
|
+
| UI state global | Zustand | Sidebar aberta, tema, user logado |
|
|
14
|
+
| UI state local | useState | Modal aberto, input controlado |
|
|
15
|
+
| Form state | React Hook Form | Dados do formulário, validação |
|
|
16
|
+
| Real-time state | Zustand + WebSocket | Status dos agentes, eventos ao vivo |
|
|
17
|
+
|
|
18
|
+
## React Query — Server State
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
// services/demandApi.ts
|
|
22
|
+
export const demandApi = {
|
|
23
|
+
list: (filters?: DemandFilters) =>
|
|
24
|
+
api.get<PaginatedResponse<Demand>>('/api/v1/demands', { params: filters }),
|
|
25
|
+
get: (id: string) =>
|
|
26
|
+
api.get<Demand>(`/api/v1/demands/${id}`),
|
|
27
|
+
create: (data: CreateDemandDto) =>
|
|
28
|
+
api.post<Demand>('/api/v1/demands', data),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// hooks/useDemands.ts — nunca duplicar dados da API no Zustand
|
|
32
|
+
export function useDemands(filters?: DemandFilters) {
|
|
33
|
+
return useQuery({
|
|
34
|
+
queryKey: ['demands', filters],
|
|
35
|
+
queryFn: () => demandApi.list(filters),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Zustand — UI State Global
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
interface AppStore {
|
|
44
|
+
sidebarOpen: boolean;
|
|
45
|
+
toggleSidebar: () => void;
|
|
46
|
+
selectedDemandId: string | null;
|
|
47
|
+
selectDemand: (id: string | null) => void;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const useAppStore = create<AppStore>((set) => ({
|
|
51
|
+
sidebarOpen: true,
|
|
52
|
+
toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
|
|
53
|
+
selectedDemandId: null,
|
|
54
|
+
selectDemand: (id) => set({ selectedDemandId: id }),
|
|
55
|
+
}));
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## WebSocket — Real-time
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
interface RealtimeStore {
|
|
62
|
+
agentStatuses: Record<string, AgentStatus>;
|
|
63
|
+
events: TrackingEvent[];
|
|
64
|
+
connect: () => void;
|
|
65
|
+
disconnect: () => void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const useRealtimeStore = create<RealtimeStore>((set) => {
|
|
69
|
+
let socket: Socket | null = null;
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
agentStatuses: {},
|
|
73
|
+
events: [],
|
|
74
|
+
connect: () => {
|
|
75
|
+
socket = io(WS_URL);
|
|
76
|
+
socket.on('agent:status', (data) =>
|
|
77
|
+
set((s) => ({ agentStatuses: { ...s.agentStatuses, [data.agentId]: data.status } }))
|
|
78
|
+
);
|
|
79
|
+
socket.on('tracking:event', (event) =>
|
|
80
|
+
set((s) => ({ events: [event, ...s.events].slice(0, 100) }))
|
|
81
|
+
);
|
|
82
|
+
},
|
|
83
|
+
disconnect: () => socket?.disconnect(),
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Constitution — Projeto JHipster Microservices
|
|
2
|
+
|
|
3
|
+
## Princípios
|
|
4
|
+
|
|
5
|
+
1. **Spec primeiro, código depois** — Toda demanda passa pelo fluxo SDD antes de implementação
|
|
6
|
+
2. **Database per service** — Cada microsserviço tem seu próprio banco, nunca acessar banco de outro
|
|
7
|
+
3. **Eventos sobre chamadas síncronas** — Preferir Kafka para comunicação, Feign apenas para queries
|
|
8
|
+
4. **Resiliência obrigatória** — Circuit breaker em toda chamada inter-serviço
|
|
9
|
+
5. **Deploy independente** — Cada serviço pode ser deployado sozinho
|
|
10
|
+
6. **Saga over 2PC** — Transações distribuídas via Saga pattern, nunca Two-Phase Commit
|
|
11
|
+
|
|
12
|
+
## Padrões de desenvolvimento
|
|
13
|
+
|
|
14
|
+
### Java / Spring Boot
|
|
15
|
+
- Java 21+, Records para DTOs e Events
|
|
16
|
+
- Constructor injection
|
|
17
|
+
- Idempotência em consumers Kafka
|
|
18
|
+
- Fallback methods para circuit breakers
|
|
19
|
+
- Entidades pertencem a UM serviço
|
|
20
|
+
|
|
21
|
+
### Comunicação
|
|
22
|
+
- Kafka para eventos de domínio (assíncrono)
|
|
23
|
+
- Feign Client para queries (síncrono)
|
|
24
|
+
- DTOs compartilhados por contrato, nunca por JAR
|
|
25
|
+
|
|
26
|
+
### Infraestrutura
|
|
27
|
+
- Docker Compose para dev
|
|
28
|
+
- Kubernetes/K3s para prod
|
|
29
|
+
- Consul para service discovery
|
|
30
|
+
- Keycloak para OAuth2
|
|
31
|
+
|
|
32
|
+
## Padrões de qualidade
|
|
33
|
+
|
|
34
|
+
- Spring Cloud Contract entre serviços
|
|
35
|
+
- Testcontainers para integração
|
|
36
|
+
- Gatling para load testing
|
|
37
|
+
- Deploy via CI/CD, nunca manual
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# Projeto: JHipster Microservices
|
|
2
|
+
|
|
3
|
+
Você está construindo uma arquitetura de microsserviços com JHipster. Múltiplos serviços Spring Boot, gateway centralizado, service discovery, messaging assíncrono e frontend Angular.
|
|
4
|
+
|
|
5
|
+
## Specification-Driven Development (SDD)
|
|
6
|
+
|
|
7
|
+
Este projeto usa **GitHub Spec Kit** para governança. Antes de implementar qualquer demanda:
|
|
8
|
+
|
|
9
|
+
1. Rodar `/speckit.constitution` — se `.spec/constitution.md` não existir
|
|
10
|
+
2. Rodar `/speckit.specify` — descrever O QUE e POR QUÊ (não como)
|
|
11
|
+
3. Rodar `/speckit.plan` — arquitetura e decisões técnicas
|
|
12
|
+
4. Rodar `/speckit.tasks` — quebrar em tasks atômicas
|
|
13
|
+
5. Rodar `/speckit.implement` — executar as tasks
|
|
14
|
+
|
|
15
|
+
Nunca pular direto para código. Spec primeiro, código depois.
|
|
16
|
+
|
|
17
|
+
## References
|
|
18
|
+
|
|
19
|
+
Documentos de referência que o agente deve consultar quando necessário:
|
|
20
|
+
|
|
21
|
+
- `references/jhipster-microservices-guide.md` — Guia de microsserviços JHipster
|
|
22
|
+
- `references/kafka-patterns.md` — Padrões de eventos Kafka
|
|
23
|
+
- `references/saga-patterns.md` — Saga pattern para transações distribuídas
|
|
24
|
+
- `references/k8s-deployment.md` — Deploy em Kubernetes
|
|
25
|
+
|
|
26
|
+
## Stack do projeto
|
|
27
|
+
|
|
28
|
+
- **Backend:** Java 21 + Spring Boot 3.x (múltiplos serviços JHipster)
|
|
29
|
+
- **Gateway:** JHipster Gateway (Spring Cloud Gateway)
|
|
30
|
+
- **Service Discovery:** JHipster Registry (Eureka) ou Consul
|
|
31
|
+
- **Frontend:** Angular 17+ (no Gateway ou app separado)
|
|
32
|
+
- **Banco:** PostgreSQL (um banco por serviço)
|
|
33
|
+
- **Messaging:** Apache Kafka (padrão JHipster para microservices)
|
|
34
|
+
- **Cache:** Redis (compartilhado)
|
|
35
|
+
- **Auth:** OAuth2 + Keycloak (recomendado para microservices)
|
|
36
|
+
- **Containers:** Docker Compose (dev) + Kubernetes/K3s (prod)
|
|
37
|
+
- **Migrations:** Liquibase (por serviço)
|
|
38
|
+
- **CI/CD:** GitLab CI
|
|
39
|
+
- **Monitoring:** Prometheus + Grafana + ELK/Loki
|
|
40
|
+
|
|
41
|
+
## Estrutura Multi-Repo ou Monorepo
|
|
42
|
+
|
|
43
|
+
### Monorepo (recomendado para times pequenos)
|
|
44
|
+
```
|
|
45
|
+
maestro/
|
|
46
|
+
├── gateway/ # JHipster Gateway + Angular
|
|
47
|
+
│ ├── src/main/java/
|
|
48
|
+
│ ├── src/main/webapp/ # Angular app
|
|
49
|
+
│ └── pom.xml
|
|
50
|
+
├── demand-service/ # Microserviço de demandas
|
|
51
|
+
│ ├── src/main/java/com/empresa/demand/
|
|
52
|
+
│ │ ├── domain/
|
|
53
|
+
│ │ ├── repository/
|
|
54
|
+
│ │ ├── service/
|
|
55
|
+
│ │ ├── web/rest/
|
|
56
|
+
│ │ └── config/
|
|
57
|
+
│ ├── src/main/resources/
|
|
58
|
+
│ │ └── config/liquibase/
|
|
59
|
+
│ └── pom.xml
|
|
60
|
+
├── agent-service/ # Microserviço de agentes
|
|
61
|
+
│ └── ...
|
|
62
|
+
├── tracking-service/ # Microserviço de rastreamento
|
|
63
|
+
│ └── ...
|
|
64
|
+
├── bundle-service/ # Microserviço de bundles/skills
|
|
65
|
+
│ └── ...
|
|
66
|
+
├── docker-compose/ # Docker Compose para dev
|
|
67
|
+
│ ├── docker-compose.yml
|
|
68
|
+
│ ├── keycloak.yml
|
|
69
|
+
│ ├── kafka.yml
|
|
70
|
+
│ ├── postgresql.yml
|
|
71
|
+
│ └── monitoring.yml
|
|
72
|
+
├── k8s/ # Kubernetes manifests
|
|
73
|
+
│ ├── demand-service/
|
|
74
|
+
│ ├── agent-service/
|
|
75
|
+
│ ├── gateway/
|
|
76
|
+
│ └── registry/
|
|
77
|
+
└── jhipster-jdl.jdl # JDL completo
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## JDL para Microservices
|
|
81
|
+
|
|
82
|
+
```jdl
|
|
83
|
+
application {
|
|
84
|
+
config {
|
|
85
|
+
baseName gateway
|
|
86
|
+
applicationType gateway
|
|
87
|
+
packageName com.empresa.gateway
|
|
88
|
+
serviceDiscoveryType consul
|
|
89
|
+
authenticationType oauth2
|
|
90
|
+
prodDatabaseType postgresql
|
|
91
|
+
buildTool maven
|
|
92
|
+
clientFramework angular
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
application {
|
|
97
|
+
config {
|
|
98
|
+
baseName demandService
|
|
99
|
+
applicationType microservice
|
|
100
|
+
packageName com.empresa.demand
|
|
101
|
+
serviceDiscoveryType consul
|
|
102
|
+
authenticationType oauth2
|
|
103
|
+
prodDatabaseType postgresql
|
|
104
|
+
buildTool maven
|
|
105
|
+
serverPort 8081
|
|
106
|
+
}
|
|
107
|
+
entities Demand, Task
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
application {
|
|
111
|
+
config {
|
|
112
|
+
baseName agentService
|
|
113
|
+
applicationType microservice
|
|
114
|
+
packageName com.empresa.agent
|
|
115
|
+
serviceDiscoveryType consul
|
|
116
|
+
authenticationType oauth2
|
|
117
|
+
prodDatabaseType postgresql
|
|
118
|
+
buildTool maven
|
|
119
|
+
serverPort 8082
|
|
120
|
+
}
|
|
121
|
+
entities Agent, AgentTeam, Worktree
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
application {
|
|
125
|
+
config {
|
|
126
|
+
baseName trackingService
|
|
127
|
+
applicationType microservice
|
|
128
|
+
packageName com.empresa.tracking
|
|
129
|
+
serviceDiscoveryType consul
|
|
130
|
+
authenticationType oauth2
|
|
131
|
+
prodDatabaseType postgresql
|
|
132
|
+
buildTool maven
|
|
133
|
+
serverPort 8083
|
|
134
|
+
}
|
|
135
|
+
entities TrackingEvent, TrackingSession
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* Entities */
|
|
139
|
+
entity Demand {
|
|
140
|
+
description TextBlob required
|
|
141
|
+
status DemandStatus required
|
|
142
|
+
priority Priority required
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
entity Task {
|
|
146
|
+
description String required
|
|
147
|
+
status TaskStatus required
|
|
148
|
+
branchName String
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
entity Agent {
|
|
152
|
+
name String required
|
|
153
|
+
type AgentType required
|
|
154
|
+
status AgentStatus required
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* Enums */
|
|
158
|
+
enum DemandStatus { CREATED, PLANNED, IN_PROGRESS, COMPLETED }
|
|
159
|
+
enum TaskStatus { PENDING, IN_PROGRESS, COMPLETED, FAILED }
|
|
160
|
+
enum AgentType { FRONTEND, BACKEND, DEVOPS, ML, QA }
|
|
161
|
+
enum AgentStatus { IDLE, BUSY, OFFLINE }
|
|
162
|
+
|
|
163
|
+
/* Relationships */
|
|
164
|
+
relationship OneToMany {
|
|
165
|
+
Demand{tasks} to Task{demand required}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* Options */
|
|
169
|
+
paginate * with pagination
|
|
170
|
+
dto * with mapstruct
|
|
171
|
+
service * with serviceImpl
|
|
172
|
+
|
|
173
|
+
/* Deployments */
|
|
174
|
+
deployment {
|
|
175
|
+
deploymentType docker-compose
|
|
176
|
+
dockerRepositoryName "registry.local/maestro"
|
|
177
|
+
appsFolders [gateway, demandService, agentService, trackingService]
|
|
178
|
+
monitoring yes
|
|
179
|
+
serviceDiscoveryType consul
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
deployment {
|
|
183
|
+
deploymentType kubernetes
|
|
184
|
+
kubernetesNamespace maestro
|
|
185
|
+
kubernetesServiceType Ingress
|
|
186
|
+
appsFolders [gateway, demandService, agentService, trackingService]
|
|
187
|
+
dockerRepositoryName "registry.local/maestro"
|
|
188
|
+
monitoring yes
|
|
189
|
+
serviceDiscoveryType consul
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Comunicação entre serviços
|
|
194
|
+
|
|
195
|
+
### Síncrona — Feign Client (entre serviços)
|
|
196
|
+
```java
|
|
197
|
+
@FeignClient(name = "agentService")
|
|
198
|
+
public interface AgentServiceClient {
|
|
199
|
+
@GetMapping("/api/agents/available")
|
|
200
|
+
List<AgentDTO> getAvailableAgents(@RequestParam("type") AgentType type);
|
|
201
|
+
|
|
202
|
+
@PostMapping("/api/agents/{id}/allocate")
|
|
203
|
+
AgentDTO allocateAgent(@PathVariable Long id, @RequestBody AllocateRequest request);
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Assíncrona — Kafka (eventos de domínio)
|
|
208
|
+
```java
|
|
209
|
+
// Producer — demand-service
|
|
210
|
+
@Service
|
|
211
|
+
public class DemandEventProducer {
|
|
212
|
+
private final KafkaTemplate<String, DemandEvent> kafka;
|
|
213
|
+
|
|
214
|
+
public void publishDemandDecomposed(Demand demand) {
|
|
215
|
+
DemandDecomposed event = new DemandDecomposed(
|
|
216
|
+
demand.getId(), demand.getTasks().stream().map(Task::getId).toList()
|
|
217
|
+
);
|
|
218
|
+
kafka.send("demand-events", demand.getId().toString(), event);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Consumer — agent-service
|
|
223
|
+
@Service
|
|
224
|
+
public class DemandEventConsumer {
|
|
225
|
+
@KafkaListener(topics = "demand-events", groupId = "agent-service")
|
|
226
|
+
public void onDemandDecomposed(DemandDecomposed event) {
|
|
227
|
+
// Alocar agentes para as tasks
|
|
228
|
+
taskAllocationService.allocateForDemand(event.demandId(), event.taskIds());
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Padrões obrigatórios
|
|
234
|
+
|
|
235
|
+
### Database per Service
|
|
236
|
+
Cada microsserviço tem seu próprio banco PostgreSQL. Nunca acessar banco de outro serviço diretamente.
|
|
237
|
+
|
|
238
|
+
### Saga Pattern para transações distribuídas
|
|
239
|
+
```
|
|
240
|
+
demand-service: CreateDemand
|
|
241
|
+
→ kafka: DemandCreated
|
|
242
|
+
→ agent-service: AllocateAgents
|
|
243
|
+
→ kafka: AgentsAllocated
|
|
244
|
+
→ demand-service: UpdateDemandStatus(PLANNED)
|
|
245
|
+
|
|
246
|
+
Se AgentAllocation falhar:
|
|
247
|
+
→ kafka: AllocationFailed
|
|
248
|
+
→ demand-service: CompensateDemand(CANCELLED)
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Circuit Breaker
|
|
252
|
+
```java
|
|
253
|
+
@CircuitBreaker(name = "agentService", fallbackMethod = "fallbackGetAgents")
|
|
254
|
+
public List<AgentDTO> getAvailableAgents(AgentType type) {
|
|
255
|
+
return agentServiceClient.getAvailableAgents(type);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
public List<AgentDTO> fallbackGetAgents(AgentType type, Throwable t) {
|
|
259
|
+
log.warn("Agent service unavailable, returning cached agents", t);
|
|
260
|
+
return cachedAgentService.getCachedAgents(type);
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Docker Compose para dev
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
# Subir tudo
|
|
268
|
+
docker-compose -f docker-compose/docker-compose.yml up -d
|
|
269
|
+
|
|
270
|
+
# Subir serviço específico
|
|
271
|
+
docker-compose -f docker-compose/docker-compose.yml up -d demand-service
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Kubernetes para prod
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
# Deploy via JHipster
|
|
278
|
+
jhipster kubernetes
|
|
279
|
+
|
|
280
|
+
# Aplicar
|
|
281
|
+
kubectl apply -f k8s/
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Testes
|
|
285
|
+
|
|
286
|
+
- **Unit:** JUnit 5 — entidades e services (>= 90% domínio)
|
|
287
|
+
- **Integration:** Testcontainers + PostgreSQL real (>= 70%)
|
|
288
|
+
- **Contract:** Spring Cloud Contract — contratos entre serviços
|
|
289
|
+
- **E2E:** Cypress no gateway
|
|
290
|
+
- **Load:** Gatling (incluso no JHipster)
|
|
291
|
+
|
|
292
|
+
## Git
|
|
293
|
+
|
|
294
|
+
- Commits: `feat(demand-service): adicionar saga de alocação`
|
|
295
|
+
- Branches: `feature/<service>-<descricao>`
|
|
296
|
+
- Um PR por serviço quando possível
|
|
297
|
+
- Nunca fazer breaking change em API sem versionar
|
|
298
|
+
|
|
299
|
+
## O que NÃO fazer
|
|
300
|
+
|
|
301
|
+
- Não acessar banco de outro serviço diretamente
|
|
302
|
+
- Não fazer chamadas síncronas em cadeia (A→B→C→D) — usar eventos
|
|
303
|
+
- Não compartilhar entities entre serviços — cada um tem seus DTOs
|
|
304
|
+
- Não deployar todos os serviços juntos — deploy independente
|
|
305
|
+
- Não ignorar circuit breakers em chamadas inter-serviço
|
|
306
|
+
- Não usar transações distribuídas (2PC) — usar Saga
|
|
307
|
+
- Não criar um "serviço de tudo" — manter bounded contexts
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ci-cd-pipeline
|
|
3
|
+
description: Criar pipelines CI/CD com GitLab CI incluindo lint, test, build, compliance check e deploy. Use quando for configurar CI/CD, criar pipelines, ou automatizar deploy.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CI/CD Pipeline — GitLab CI
|
|
7
|
+
|
|
8
|
+
## Pipeline completo
|
|
9
|
+
|
|
10
|
+
```yaml
|
|
11
|
+
# .gitlab-ci.yml
|
|
12
|
+
stages:
|
|
13
|
+
- lint
|
|
14
|
+
- test
|
|
15
|
+
- security
|
|
16
|
+
- compliance
|
|
17
|
+
- build
|
|
18
|
+
- deploy-staging
|
|
19
|
+
- deploy-prod
|
|
20
|
+
|
|
21
|
+
variables:
|
|
22
|
+
DOCKER_IMAGE: $CI_REGISTRY_IMAGE
|
|
23
|
+
K3S_NAMESPACE: maestro
|
|
24
|
+
|
|
25
|
+
# ===== LINT =====
|
|
26
|
+
lint:python:
|
|
27
|
+
stage: lint
|
|
28
|
+
image: python:3.11-slim
|
|
29
|
+
script:
|
|
30
|
+
- pip install ruff mypy
|
|
31
|
+
- ruff check src/
|
|
32
|
+
- mypy src/ --ignore-missing-imports
|
|
33
|
+
|
|
34
|
+
lint:frontend:
|
|
35
|
+
stage: lint
|
|
36
|
+
image: node:20-slim
|
|
37
|
+
script:
|
|
38
|
+
- npm ci
|
|
39
|
+
- npm run lint
|
|
40
|
+
- npm run type-check
|
|
41
|
+
|
|
42
|
+
# ===== TEST =====
|
|
43
|
+
test:backend:
|
|
44
|
+
stage: test
|
|
45
|
+
image: python:3.11-slim
|
|
46
|
+
services:
|
|
47
|
+
- postgres:16
|
|
48
|
+
- redis:7
|
|
49
|
+
variables:
|
|
50
|
+
DATABASE_URL: postgresql://test:test@postgres/testdb
|
|
51
|
+
REDIS_URL: redis://redis:6379
|
|
52
|
+
script:
|
|
53
|
+
- pip install -r requirements.txt
|
|
54
|
+
- pytest --cov=src --cov-report=xml --cov-fail-under=80
|
|
55
|
+
artifacts:
|
|
56
|
+
reports:
|
|
57
|
+
coverage_report:
|
|
58
|
+
coverage_format: cobertura
|
|
59
|
+
path: coverage.xml
|
|
60
|
+
|
|
61
|
+
test:frontend:
|
|
62
|
+
stage: test
|
|
63
|
+
image: node:20-slim
|
|
64
|
+
script:
|
|
65
|
+
- npm ci
|
|
66
|
+
- npm run test -- --coverage
|
|
67
|
+
|
|
68
|
+
# ===== SECURITY =====
|
|
69
|
+
security:scan:
|
|
70
|
+
stage: security
|
|
71
|
+
image: python:3.11-slim
|
|
72
|
+
script:
|
|
73
|
+
- pip install bandit safety
|
|
74
|
+
- bandit -r src/ -ll
|
|
75
|
+
- safety check
|
|
76
|
+
|
|
77
|
+
# ===== COMPLIANCE =====
|
|
78
|
+
compliance:bundle:
|
|
79
|
+
stage: compliance
|
|
80
|
+
script:
|
|
81
|
+
- python scripts/check_bundle_compliance.py --bundle $BUNDLE_NAME
|
|
82
|
+
- python scripts/check_structure.py
|
|
83
|
+
allow_failure: false
|
|
84
|
+
|
|
85
|
+
# ===== BUILD =====
|
|
86
|
+
build:api:
|
|
87
|
+
stage: build
|
|
88
|
+
script:
|
|
89
|
+
- docker build -f docker/Dockerfile.api -t $DOCKER_IMAGE/api:$CI_COMMIT_SHA .
|
|
90
|
+
- docker push $DOCKER_IMAGE/api:$CI_COMMIT_SHA
|
|
91
|
+
only:
|
|
92
|
+
- main
|
|
93
|
+
- develop
|
|
94
|
+
|
|
95
|
+
# ===== DEPLOY =====
|
|
96
|
+
deploy:staging:
|
|
97
|
+
stage: deploy-staging
|
|
98
|
+
script:
|
|
99
|
+
- kubectl -n $K3S_NAMESPACE-staging set image deployment/api api=$DOCKER_IMAGE/api:$CI_COMMIT_SHA
|
|
100
|
+
- kubectl -n $K3S_NAMESPACE-staging rollout status deployment/api --timeout=120s
|
|
101
|
+
only:
|
|
102
|
+
- develop
|
|
103
|
+
|
|
104
|
+
deploy:prod:
|
|
105
|
+
stage: deploy-prod
|
|
106
|
+
script:
|
|
107
|
+
- kubectl -n $K3S_NAMESPACE set image deployment/api api=$DOCKER_IMAGE/api:$CI_COMMIT_SHA
|
|
108
|
+
- kubectl -n $K3S_NAMESPACE rollout status deployment/api --timeout=120s
|
|
109
|
+
only:
|
|
110
|
+
- main
|
|
111
|
+
when: manual
|
|
112
|
+
```
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clean-architecture
|
|
3
|
+
description: Implementar Clean Architecture com camadas de domínio, aplicação e infraestrutura. Use quando for criar módulos, organizar código em camadas, separar regras de negócio de infraestrutura, ou estruturar um projeto novo.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Clean Architecture
|
|
7
|
+
|
|
8
|
+
## Camadas
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
┌──────────────────────────────┐
|
|
12
|
+
│ API / CLI │ ← Controllers, Routers
|
|
13
|
+
├──────────────────────────────┤
|
|
14
|
+
│ APPLICATION │ ← Use Cases, DTOs
|
|
15
|
+
├──────────────────────────────┤
|
|
16
|
+
│ DOMAIN │ ← Entities, VOs, Events, Repos (interface)
|
|
17
|
+
├──────────────────────────────┤
|
|
18
|
+
│ INFRASTRUCTURE │ ← DB, HTTP clients, Frameworks
|
|
19
|
+
└──────────────────────────────┘
|
|
20
|
+
|
|
21
|
+
Dependency Rule: setas apontam para DENTRO (infra → domain)
|
|
22
|
+
Domain NUNCA importa de infrastructure
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Domain Layer
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
# domain/entities/demand.py
|
|
29
|
+
class Demand:
|
|
30
|
+
def __init__(self, id: DemandId, description: str):
|
|
31
|
+
self._id = id
|
|
32
|
+
self._description = description
|
|
33
|
+
self._status = DemandStatus.CREATED
|
|
34
|
+
self._events: list[DomainEvent] = []
|
|
35
|
+
|
|
36
|
+
def decompose(self, planner: TaskPlanner) -> list[Task]:
|
|
37
|
+
if self._status != DemandStatus.CREATED:
|
|
38
|
+
raise DemandAlreadyDecomposedException(self._id)
|
|
39
|
+
tasks = planner.plan(self._description)
|
|
40
|
+
self._status = DemandStatus.PLANNED
|
|
41
|
+
self._events.append(DemandDecomposed(self._id, [t.id for t in tasks]))
|
|
42
|
+
return tasks
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def pending_events(self) -> list[DomainEvent]:
|
|
46
|
+
return list(self._events)
|
|
47
|
+
|
|
48
|
+
# domain/repositories/demand_repository.py (PORT - apenas interface)
|
|
49
|
+
class DemandRepository(ABC):
|
|
50
|
+
@abstractmethod
|
|
51
|
+
def find_by_id(self, id: DemandId) -> Demand: ...
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def save(self, demand: Demand) -> None: ...
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Application Layer
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
# application/use_cases/decompose_demand.py
|
|
60
|
+
class DecomposeDemand:
|
|
61
|
+
def __init__(self, repo: DemandRepository, planner: TaskPlanner, event_bus: EventBus):
|
|
62
|
+
self._repo = repo
|
|
63
|
+
self._planner = planner
|
|
64
|
+
self._event_bus = event_bus
|
|
65
|
+
|
|
66
|
+
def execute(self, demand_id: str) -> DecomposeDemandOutput:
|
|
67
|
+
demand = self._repo.find_by_id(DemandId(demand_id))
|
|
68
|
+
tasks = demand.decompose(self._planner)
|
|
69
|
+
self._repo.save(demand)
|
|
70
|
+
for event in demand.pending_events:
|
|
71
|
+
self._event_bus.publish(event)
|
|
72
|
+
return DecomposeDemandOutput(tasks=[TaskDTO.from_entity(t) for t in tasks])
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Infrastructure Layer
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
# infrastructure/persistence/pg_demand_repository.py (ADAPTER)
|
|
79
|
+
class PgDemandRepository(DemandRepository):
|
|
80
|
+
def __init__(self, session: Session):
|
|
81
|
+
self._session = session
|
|
82
|
+
|
|
83
|
+
def find_by_id(self, id: DemandId) -> Demand:
|
|
84
|
+
model = self._session.query(DemandModel).get(str(id))
|
|
85
|
+
if not model:
|
|
86
|
+
raise DemandNotFoundException(id)
|
|
87
|
+
return self._to_entity(model)
|
|
88
|
+
|
|
89
|
+
def save(self, demand: Demand) -> None:
|
|
90
|
+
model = self._to_model(demand)
|
|
91
|
+
self._session.merge(model)
|
|
92
|
+
self._session.commit()
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Regra de ouro
|
|
96
|
+
|
|
97
|
+
Use Case orquestra → Entity contém regra → Repository persiste
|
|
98
|
+
|
|
99
|
+
Nunca colocar regra de negócio no Controller, Repository ou "Service" genérico.
|