@ryuenn3123/agentic-senior-core 1.8.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/.agent-context/blueprints/api-nextjs.md +184 -0
- package/.agent-context/blueprints/aspnet-api.md +247 -0
- package/.agent-context/blueprints/ci-github-actions.md +226 -0
- package/.agent-context/blueprints/ci-gitlab.md +200 -0
- package/.agent-context/blueprints/fastapi-service.md +210 -0
- package/.agent-context/blueprints/go-service.md +217 -0
- package/.agent-context/blueprints/graphql-grpc-api.md +51 -0
- package/.agent-context/blueprints/infrastructure-as-code.md +62 -0
- package/.agent-context/blueprints/kubernetes-manifests.md +76 -0
- package/.agent-context/blueprints/laravel-api.md +223 -0
- package/.agent-context/blueprints/nestjs-logic.md +247 -0
- package/.agent-context/blueprints/observability.md +227 -0
- package/.agent-context/blueprints/spring-boot-api.md +218 -0
- package/.agent-context/policies/llm-judge-threshold.json +20 -0
- package/.agent-context/profiles/platform.md +13 -0
- package/.agent-context/profiles/regulated.md +13 -0
- package/.agent-context/profiles/startup.md +13 -0
- package/.agent-context/prompts/init-project.md +86 -0
- package/.agent-context/prompts/refactor.md +45 -0
- package/.agent-context/prompts/review-code.md +47 -0
- package/.agent-context/review-checklists/architecture-review.md +70 -0
- package/.agent-context/review-checklists/frontend-usability.md +33 -0
- package/.agent-context/review-checklists/performance-audit.md +65 -0
- package/.agent-context/review-checklists/pr-checklist.md +97 -0
- package/.agent-context/review-checklists/release-operations.md +29 -0
- package/.agent-context/review-checklists/security-audit.md +113 -0
- package/.agent-context/rules/api-docs.md +186 -0
- package/.agent-context/rules/architecture.md +198 -0
- package/.agent-context/rules/database-design.md +202 -0
- package/.agent-context/rules/efficiency-vs-hype.md +143 -0
- package/.agent-context/rules/error-handling.md +234 -0
- package/.agent-context/rules/event-driven.md +226 -0
- package/.agent-context/rules/frontend-architecture.md +66 -0
- package/.agent-context/rules/git-workflow.md +200 -0
- package/.agent-context/rules/microservices.md +174 -0
- package/.agent-context/rules/naming-conv.md +141 -0
- package/.agent-context/rules/performance.md +168 -0
- package/.agent-context/rules/realtime.md +47 -0
- package/.agent-context/rules/security.md +195 -0
- package/.agent-context/rules/testing.md +178 -0
- package/.agent-context/stacks/csharp.md +149 -0
- package/.agent-context/stacks/go.md +181 -0
- package/.agent-context/stacks/java.md +135 -0
- package/.agent-context/stacks/php.md +178 -0
- package/.agent-context/stacks/python.md +153 -0
- package/.agent-context/stacks/ruby.md +80 -0
- package/.agent-context/stacks/rust.md +86 -0
- package/.agent-context/stacks/typescript.md +317 -0
- package/.agent-context/state/architecture-map.md +25 -0
- package/.agent-context/state/dependency-map.md +32 -0
- package/.agent-override.md +36 -0
- package/.agents/workflows/init-project.md +29 -0
- package/.agents/workflows/refactor.md +29 -0
- package/.agents/workflows/review-code.md +29 -0
- package/.cursorrules +140 -0
- package/.gemini/instructions.md +97 -0
- package/.github/ISSUE_TEMPLATE/v1.7-frontend-work-item.yml +54 -0
- package/.github/copilot-instructions.md +104 -0
- package/.github/workflows/benchmark-detection.yml +38 -0
- package/.github/workflows/frontend-usability-gate.yml +36 -0
- package/.github/workflows/release-gate.yml +32 -0
- package/.github/workflows/sbom-compliance.yml +32 -0
- package/.windsurfrules +106 -0
- package/AGENTS.md +131 -0
- package/CONTRIBUTING.md +136 -0
- package/LICENSE +21 -0
- package/README.md +239 -0
- package/bin/agentic-senior-core.js +1147 -0
- package/mcp.json +29 -0
- package/package.json +50 -0
- package/scripts/detection-benchmark.mjs +138 -0
- package/scripts/frontend-usability-audit.mjs +87 -0
- package/scripts/generate-sbom.mjs +61 -0
- package/scripts/init-project.ps1 +105 -0
- package/scripts/init-project.sh +131 -0
- package/scripts/llm-judge.mjs +664 -0
- package/scripts/release-gate.mjs +116 -0
- package/scripts/validate.mjs +554 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Infrastructure as Code (IaC) Blueprint
|
|
2
|
+
|
|
3
|
+
> ClickOps is a sin. If it's not in code, it doesn't exist. Treats your servers like cattle, not pets.
|
|
4
|
+
|
|
5
|
+
## 1. Tooling (March 2026)
|
|
6
|
+
- **Standard:** Terraform / OpenTofu (HCL) or Pulumi (TypeScript/Go/Python).
|
|
7
|
+
- **BANNED:** AWS CloudFormation (verbose, slow, vendor-locked) unless mandated by enterprise policy. AWS CDK is acceptable but requires rigorous linting to prevent runaway loops.
|
|
8
|
+
|
|
9
|
+
## 2. Directory Structure (Blast Radius Management)
|
|
10
|
+
Never put all your infrastructure into a single `main.tf` or a single state file.
|
|
11
|
+
- **BANNED:** Flat structure. A single mistake could destroy the database while updating an S3 bucket.
|
|
12
|
+
- **REQUIRED (Component/Environment Split):**
|
|
13
|
+
```
|
|
14
|
+
infrastructure/
|
|
15
|
+
modules/
|
|
16
|
+
vpc/
|
|
17
|
+
rds/
|
|
18
|
+
eks/
|
|
19
|
+
environments/
|
|
20
|
+
dev/
|
|
21
|
+
vpc/ (depends on modules/vpc)
|
|
22
|
+
rds/ (runs its own state file)
|
|
23
|
+
prod/
|
|
24
|
+
vpc/
|
|
25
|
+
rds/
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 3. Remote State & Locking
|
|
29
|
+
State files contain sensitive data (passwords, IP addresses) and represent the source of truth for your physical architecture.
|
|
30
|
+
- **Rule:** ALWAYS use a remote backend (S3, GCS, HCP Terraform) with state locking (DynamoDB, native GCS/HCP).
|
|
31
|
+
- **BANNED:** Committing `terraform.tfstate` or `Pulumi.dev.yaml` to Git. Ensure `*.tfstate` is in `.gitignore`.
|
|
32
|
+
|
|
33
|
+
## 4. Hardcoded Values & Secrets
|
|
34
|
+
- **Rule:** NEVER hardcode ARNs, IPs, or passwords in your IaC.
|
|
35
|
+
- **REQUIRED:** Pass variables via `.tfvars` (which are gitignored) or environment variables (`TF_VAR_db_password`). Use a Secret Manager (AWS Secrets Manager, HashiCorp Vault) and reference the secret dynamically.
|
|
36
|
+
- **Example:**
|
|
37
|
+
```hcl
|
|
38
|
+
# ❌ BANNED
|
|
39
|
+
password = "SuperSecretPassword123!"
|
|
40
|
+
|
|
41
|
+
# ✅ REQUIRED
|
|
42
|
+
data "aws_secretsmanager_secret_version" "db" {
|
|
43
|
+
secret_id = aws_secretsmanager_secret.db.id
|
|
44
|
+
}
|
|
45
|
+
password = jsondecode(data.aws_secretsmanager_secret_version.db.secret_string)["password"]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 5. Principle of Least Privilege (IAM)
|
|
49
|
+
- **Rule:** Wildcards (`*`) in IAM policies are strictly banned unless absolutely necessary (e.g., specific S3 actions).
|
|
50
|
+
- **BANNED:** Assigning `AmazonS3FullAccess` to a worker node that only needs to read from one bucket.
|
|
51
|
+
- **REQUIRED:** Explicit resource ARNs and explicit Actions.
|
|
52
|
+
```hcl
|
|
53
|
+
statement {
|
|
54
|
+
effect = "Allow"
|
|
55
|
+
actions = ["s3:GetObject"]
|
|
56
|
+
resources = ["arn:aws:s3:::my-specific-bucket/uploads/*"]
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 6. Immutable Infrastructure
|
|
61
|
+
- **Rule:** If a server is unhealthy, terminate it. Do not SSH into it to fix it.
|
|
62
|
+
- **Rule:** Use Auto Scaling Groups (ASGs) even if the desired capacity is 1. This ensures the component is self-healing.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Kubernetes Manifests Blueprint
|
|
2
|
+
|
|
3
|
+
> Kubernetes is a declarative state machine. Tell it what you want, not how to do it.
|
|
4
|
+
|
|
5
|
+
## 1. Bare Minimum Requirements
|
|
6
|
+
Every application deployed to Kubernetes MUST include at minimum:
|
|
7
|
+
1. `Deployment` (or `StatefulSet`/`DaemonSet`)
|
|
8
|
+
2. `Service`
|
|
9
|
+
3. `ConfigMap` (for non-sensitive config)
|
|
10
|
+
4. `Secret` (managed externally, e.g., via ExternalSecrets)
|
|
11
|
+
5. `Ingress` (or `Gateway APIRoute`)
|
|
12
|
+
|
|
13
|
+
## 2. Resource Limits & Requests (Mandatory)
|
|
14
|
+
A container without resource limits is a noisy neighbor that will eventually crash the node.
|
|
15
|
+
- **Rule:** EVERY container in a Pod MUST have BOTH `requests` and `limits` defined for CPU and Memory.
|
|
16
|
+
- **BANNED:** Omitting the `resources` block.
|
|
17
|
+
- **REQUIRED:**
|
|
18
|
+
```yaml
|
|
19
|
+
resources:
|
|
20
|
+
requests:
|
|
21
|
+
memory: "256Mi"
|
|
22
|
+
cpu: "100m"
|
|
23
|
+
limits:
|
|
24
|
+
memory: "512Mi"
|
|
25
|
+
cpu: "500m"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 3. Liveness & Readiness Probes (Mandatory)
|
|
29
|
+
Kubernetes needs to know when your app is ready to serve traffic and if it needs to be restarted.
|
|
30
|
+
- **Rule:** EVERY container MUST define a `readinessProbe` and a `livenessProbe`.
|
|
31
|
+
- **BANNED:** Assuming the container is ready just because the process started.
|
|
32
|
+
- **REQUIRED:**
|
|
33
|
+
```yaml
|
|
34
|
+
livenessProbe:
|
|
35
|
+
httpGet:
|
|
36
|
+
path: /healthz
|
|
37
|
+
port: 8080
|
|
38
|
+
initialDelaySeconds: 5
|
|
39
|
+
periodSeconds: 10
|
|
40
|
+
readinessProbe:
|
|
41
|
+
httpGet:
|
|
42
|
+
path: /ready
|
|
43
|
+
port: 8080
|
|
44
|
+
initialDelaySeconds: 5
|
|
45
|
+
periodSeconds: 5
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 4. The `latest` Tag Fallacy
|
|
49
|
+
- **Rule:** NEVER use the `:latest` tag for container images in production.
|
|
50
|
+
- **Why:** Deployments containing the `latest` tag cannot be easily rolled back, and nodes may cache different versions of `latest`.
|
|
51
|
+
- **REQUIRED:** Use explicit semantic versioning (`:v1.2.3`) or Git SHA hashes (`:sha-7a8f9b`).
|
|
52
|
+
|
|
53
|
+
## 5. Configuration & Secrets Management
|
|
54
|
+
- **Rule:** Application configuration must be injected via Environment Variables originating from `ConfigMaps` or `Secrets`.
|
|
55
|
+
- **BANNED:** Hardcoding environment variables in the `Deployment` manifest.
|
|
56
|
+
- **BANNED:** Storing Base64-encoded `Secret` manifests in version control.
|
|
57
|
+
- **REQUIRED:** Use tools like `External Secrets Operator` to pull secrets from AWS Secrets Manager / HashiCorp Vault into the cluster securely.
|
|
58
|
+
```yaml
|
|
59
|
+
envFrom:
|
|
60
|
+
- configMapRef:
|
|
61
|
+
name: app-config
|
|
62
|
+
- secretRef:
|
|
63
|
+
name: app-secrets
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 6. Security Context
|
|
67
|
+
By default, containers run as `root`. This is a massive security risk.
|
|
68
|
+
- **Rule:** Containers MUST run as a non-root user. The filesystem should be read-only where possible.
|
|
69
|
+
- **REQUIRED:**
|
|
70
|
+
```yaml
|
|
71
|
+
securityContext:
|
|
72
|
+
runAsNonRoot: true
|
|
73
|
+
runAsUser: 1000
|
|
74
|
+
readOnlyRootFilesystem: true
|
|
75
|
+
allowPrivilegeEscalation: false
|
|
76
|
+
```
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# Blueprint: Laravel API
|
|
2
|
+
|
|
3
|
+
> PHP backend API service using Laravel 12, PHP 8.5+, Form Requests, Eloquent, and Scribe for docs.
|
|
4
|
+
|
|
5
|
+
## Tech Stack
|
|
6
|
+
|
|
7
|
+
| Layer | Technology |
|
|
8
|
+
|-------|-----------|
|
|
9
|
+
| Framework | Laravel 12 |
|
|
10
|
+
| Validation | Form Requests |
|
|
11
|
+
| ORM | Eloquent |
|
|
12
|
+
| Migration | Laravel Migrations |
|
|
13
|
+
| Testing | Pest PHP |
|
|
14
|
+
| Static analysis | PHPStan (level 8) |
|
|
15
|
+
| Formatting | Laravel Pint |
|
|
16
|
+
| API docs | Scribe |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Project Structure
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
project-name/
|
|
24
|
+
├── app/
|
|
25
|
+
│ ├── Modules/
|
|
26
|
+
│ │ └── User/
|
|
27
|
+
│ │ ├── Controllers/
|
|
28
|
+
│ │ │ └── UserController.php
|
|
29
|
+
│ │ ├── Services/
|
|
30
|
+
│ │ │ └── UserService.php
|
|
31
|
+
│ │ ├── Repositories/
|
|
32
|
+
│ │ │ └── UserRepository.php
|
|
33
|
+
│ │ ├── Requests/
|
|
34
|
+
│ │ │ ├── StoreUserRequest.php
|
|
35
|
+
│ │ │ └── UpdateUserRequest.php
|
|
36
|
+
│ │ ├── Resources/
|
|
37
|
+
│ │ │ └── UserResource.php
|
|
38
|
+
│ │ ├── Models/
|
|
39
|
+
│ │ │ └── User.php
|
|
40
|
+
│ │ ├── Policies/
|
|
41
|
+
│ │ │ └── UserPolicy.php
|
|
42
|
+
│ │ └── Exceptions/
|
|
43
|
+
│ │ └── UserNotFoundException.php
|
|
44
|
+
│ │
|
|
45
|
+
│ ├── Shared/
|
|
46
|
+
│ │ ├── Exceptions/
|
|
47
|
+
│ │ │ └── Handler.php
|
|
48
|
+
│ │ └── Traits/
|
|
49
|
+
│ │ └── ApiResponse.php
|
|
50
|
+
│ │
|
|
51
|
+
│ └── Providers/
|
|
52
|
+
│
|
|
53
|
+
├── database/
|
|
54
|
+
│ ├── factories/
|
|
55
|
+
│ │ └── UserFactory.php
|
|
56
|
+
│ ├── migrations/
|
|
57
|
+
│ └── seeders/
|
|
58
|
+
│
|
|
59
|
+
├── routes/
|
|
60
|
+
│ └── api.php
|
|
61
|
+
│
|
|
62
|
+
├── tests/
|
|
63
|
+
│ ├── Feature/
|
|
64
|
+
│ │ └── User/
|
|
65
|
+
│ │ └── UserEndpointTest.php
|
|
66
|
+
│ └── Unit/
|
|
67
|
+
│ └── User/
|
|
68
|
+
│ └── UserServiceTest.php
|
|
69
|
+
│
|
|
70
|
+
├── phpstan.neon
|
|
71
|
+
├── .env.example
|
|
72
|
+
├── composer.json
|
|
73
|
+
└── Dockerfile
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## File Patterns
|
|
79
|
+
|
|
80
|
+
### Controller — Thin Transport Layer
|
|
81
|
+
```php
|
|
82
|
+
<?php
|
|
83
|
+
|
|
84
|
+
declare(strict_types=1);
|
|
85
|
+
|
|
86
|
+
namespace App\Modules\User\Controllers;
|
|
87
|
+
|
|
88
|
+
use App\Modules\User\Requests\StoreUserRequest;
|
|
89
|
+
use App\Modules\User\Resources\UserResource;
|
|
90
|
+
use App\Modules\User\Services\UserService;
|
|
91
|
+
use Illuminate\Http\JsonResponse;
|
|
92
|
+
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
|
93
|
+
use Symfony\Component\HttpFoundation\Response;
|
|
94
|
+
|
|
95
|
+
final class UserController
|
|
96
|
+
{
|
|
97
|
+
public function __construct(
|
|
98
|
+
private readonly UserService $userService,
|
|
99
|
+
) {}
|
|
100
|
+
|
|
101
|
+
public function store(StoreUserRequest $request): JsonResponse
|
|
102
|
+
{
|
|
103
|
+
$user = $this->userService->create($request->validated());
|
|
104
|
+
|
|
105
|
+
return (new UserResource($user))
|
|
106
|
+
->response()
|
|
107
|
+
->setStatusCode(Response::HTTP_CREATED);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public function index(): AnonymousResourceCollection
|
|
111
|
+
{
|
|
112
|
+
return UserResource::collection(
|
|
113
|
+
$this->userService->listPaginated()
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Form Request — Boundary Validation
|
|
120
|
+
```php
|
|
121
|
+
<?php
|
|
122
|
+
|
|
123
|
+
declare(strict_types=1);
|
|
124
|
+
|
|
125
|
+
namespace App\Modules\User\Requests;
|
|
126
|
+
|
|
127
|
+
use Illuminate\Foundation\Http\FormRequest;
|
|
128
|
+
|
|
129
|
+
final class StoreUserRequest extends FormRequest
|
|
130
|
+
{
|
|
131
|
+
/** @return array<string, array<int, string>> */
|
|
132
|
+
public function rules(): array
|
|
133
|
+
{
|
|
134
|
+
return [
|
|
135
|
+
'name' => ['required', 'string', 'max:100'],
|
|
136
|
+
'email' => ['required', 'email', 'unique:users,email'],
|
|
137
|
+
'password' => ['required', 'string', 'min:8', 'confirmed'],
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Service — Business Logic
|
|
144
|
+
```php
|
|
145
|
+
<?php
|
|
146
|
+
|
|
147
|
+
declare(strict_types=1);
|
|
148
|
+
|
|
149
|
+
namespace App\Modules\User\Services;
|
|
150
|
+
|
|
151
|
+
use App\Modules\User\Models\User;
|
|
152
|
+
use App\Modules\User\Repositories\UserRepository;
|
|
153
|
+
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
|
154
|
+
use Illuminate\Support\Facades\Hash;
|
|
155
|
+
use Illuminate\Support\Facades\Log;
|
|
156
|
+
|
|
157
|
+
final class UserService
|
|
158
|
+
{
|
|
159
|
+
public function __construct(
|
|
160
|
+
private readonly UserRepository $userRepository,
|
|
161
|
+
) {}
|
|
162
|
+
|
|
163
|
+
/** @param array{name: string, email: string, password: string} $data */
|
|
164
|
+
public function create(array $data): User
|
|
165
|
+
{
|
|
166
|
+
$data['password'] = Hash::make($data['password']);
|
|
167
|
+
$user = $this->userRepository->create($data);
|
|
168
|
+
|
|
169
|
+
Log::info('User created', ['user_id' => $user->id, 'email' => $user->email]);
|
|
170
|
+
|
|
171
|
+
return $user;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
public function listPaginated(int $perPage = 15): LengthAwarePaginator
|
|
175
|
+
{
|
|
176
|
+
return $this->userRepository->paginate($perPage);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### API Resource — Response Transformer
|
|
182
|
+
```php
|
|
183
|
+
<?php
|
|
184
|
+
|
|
185
|
+
declare(strict_types=1);
|
|
186
|
+
|
|
187
|
+
namespace App\Modules\User\Resources;
|
|
188
|
+
|
|
189
|
+
use Illuminate\Http\Request;
|
|
190
|
+
use Illuminate\Http\Resources\Json\JsonResource;
|
|
191
|
+
|
|
192
|
+
/** @mixin \App\Modules\User\Models\User */
|
|
193
|
+
final class UserResource extends JsonResource
|
|
194
|
+
{
|
|
195
|
+
/** @return array<string, mixed> */
|
|
196
|
+
public function toArray(Request $request): array
|
|
197
|
+
{
|
|
198
|
+
return [
|
|
199
|
+
'id' => $this->id,
|
|
200
|
+
'name' => $this->name,
|
|
201
|
+
'email' => $this->email,
|
|
202
|
+
'created_at' => $this->created_at?->toISOString(),
|
|
203
|
+
];
|
|
204
|
+
// NEVER expose: password, remember_token, email_verified_at (unless needed)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Scaffolding Checklist
|
|
212
|
+
|
|
213
|
+
- [ ] Create Laravel project: `composer create-project laravel/laravel`
|
|
214
|
+
- [ ] Set up modular structure under `app/Modules/`
|
|
215
|
+
- [ ] Create shared error handler with consistent JSON responses
|
|
216
|
+
- [ ] Create shared `ApiResponse` trait for standard response format
|
|
217
|
+
- [ ] Install and configure PHPStan level 8
|
|
218
|
+
- [ ] Install and configure Laravel Pint
|
|
219
|
+
- [ ] Install Pest PHP for testing
|
|
220
|
+
- [ ] Install Scribe for API documentation
|
|
221
|
+
- [ ] Create first module following the pattern above
|
|
222
|
+
- [ ] Create `.env.example` with all required variables
|
|
223
|
+
- [ ] Run `php artisan test`, `./vendor/bin/phpstan`, `./vendor/bin/pint` — zero errors
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# Blueprint: NestJS Module (Clean Architecture)
|
|
2
|
+
|
|
3
|
+
> This blueprint defines how to scaffold a NestJS application or module.
|
|
4
|
+
> Every module follows strict layering. No shortcuts.
|
|
5
|
+
|
|
6
|
+
## Tech Stack
|
|
7
|
+
- **Runtime:** Node.js 20+ / Bun
|
|
8
|
+
- **Framework:** NestJS 10+
|
|
9
|
+
- **Validation:** Zod + nestjs-zod (or class-validator)
|
|
10
|
+
- **ORM:** Prisma (or Drizzle)
|
|
11
|
+
- **Documentation:** @nestjs/swagger (OpenAPI auto-generated)
|
|
12
|
+
- **Testing:** Vitest (or Jest)
|
|
13
|
+
- **Logger:** nestjs-pino
|
|
14
|
+
|
|
15
|
+
## Project Structure
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
project-name/
|
|
19
|
+
├── src/
|
|
20
|
+
│ ├── main.ts # Bootstrap + Swagger setup
|
|
21
|
+
│ ├── app.module.ts # Root module
|
|
22
|
+
│ │
|
|
23
|
+
│ ├── modules/ # Feature modules
|
|
24
|
+
│ │ ├── user/
|
|
25
|
+
│ │ │ ├── user.module.ts # Module registration
|
|
26
|
+
│ │ │ ├── user.controller.ts # Transport layer (HTTP)
|
|
27
|
+
│ │ │ ├── user.service.ts # Application layer (business logic)
|
|
28
|
+
│ │ │ ├── user.repository.ts # Infrastructure layer (data access)
|
|
29
|
+
│ │ │ ├── dto/
|
|
30
|
+
│ │ │ │ ├── create-user.dto.ts # Input validation schemas
|
|
31
|
+
│ │ │ │ ├── update-user.dto.ts
|
|
32
|
+
│ │ │ │ └── user-response.dto.ts # Output serialization
|
|
33
|
+
│ │ │ ├── entities/
|
|
34
|
+
│ │ │ │ └── user.entity.ts # Domain entity
|
|
35
|
+
│ │ │ ├── guards/
|
|
36
|
+
│ │ │ │ └── user-owner.guard.ts # Authorization guard
|
|
37
|
+
│ │ │ └── __tests__/
|
|
38
|
+
│ │ │ ├── user.controller.spec.ts
|
|
39
|
+
│ │ │ └── user.service.spec.ts
|
|
40
|
+
│ │ │
|
|
41
|
+
│ │ ├── auth/
|
|
42
|
+
│ │ │ ├── auth.module.ts
|
|
43
|
+
│ │ │ ├── auth.controller.ts
|
|
44
|
+
│ │ │ ├── auth.service.ts
|
|
45
|
+
│ │ │ ├── strategies/
|
|
46
|
+
│ │ │ │ ├── jwt.strategy.ts
|
|
47
|
+
│ │ │ │ └── local.strategy.ts
|
|
48
|
+
│ │ │ └── guards/
|
|
49
|
+
│ │ │ └── jwt-auth.guard.ts
|
|
50
|
+
│ │ │
|
|
51
|
+
│ │ └── health/
|
|
52
|
+
│ │ ├── health.module.ts
|
|
53
|
+
│ │ └── health.controller.ts
|
|
54
|
+
│ │
|
|
55
|
+
│ ├── shared/ # Cross-cutting concerns
|
|
56
|
+
│ │ ├── config/
|
|
57
|
+
│ │ │ ├── app.config.ts # Zod-validated config
|
|
58
|
+
│ │ │ └── database.config.ts
|
|
59
|
+
│ │ ├── errors/
|
|
60
|
+
│ │ │ ├── app-error.ts # Base error class
|
|
61
|
+
│ │ │ └── http-exception.filter.ts # Global exception filter
|
|
62
|
+
│ │ ├── interceptors/
|
|
63
|
+
│ │ │ ├── logging.interceptor.ts # Request logging
|
|
64
|
+
│ │ │ └── transform.interceptor.ts # Response transformation
|
|
65
|
+
│ │ ├── pipes/
|
|
66
|
+
│ │ │ └── zod-validation.pipe.ts # Zod validation pipe
|
|
67
|
+
│ │ ├── decorators/
|
|
68
|
+
│ │ │ └── current-user.decorator.ts
|
|
69
|
+
│ │ └── lib/
|
|
70
|
+
│ │ ├── prisma.service.ts # Prisma client lifecycle
|
|
71
|
+
│ │ └── logger.module.ts # Pino logger module
|
|
72
|
+
│ │
|
|
73
|
+
│ └── common/
|
|
74
|
+
│ └── types/
|
|
75
|
+
│ └── api-response.ts # Standardized API response type
|
|
76
|
+
│
|
|
77
|
+
├── prisma/
|
|
78
|
+
│ ├── schema.prisma
|
|
79
|
+
│ └── migrations/
|
|
80
|
+
│
|
|
81
|
+
├── test/ # E2E tests
|
|
82
|
+
│ └── app.e2e-spec.ts
|
|
83
|
+
│
|
|
84
|
+
├── .env.example
|
|
85
|
+
├── nest-cli.json
|
|
86
|
+
├── tsconfig.json # Strict mode!
|
|
87
|
+
├── tsconfig.build.json
|
|
88
|
+
├── vitest.config.ts
|
|
89
|
+
└── package.json
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Module Pattern (The Law)
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// src/modules/user/user.module.ts
|
|
96
|
+
import { Module } from '@nestjs/common';
|
|
97
|
+
import { UserController } from './user.controller';
|
|
98
|
+
import { UserService } from './user.service';
|
|
99
|
+
import { UserRepository } from './user.repository';
|
|
100
|
+
|
|
101
|
+
@Module({
|
|
102
|
+
controllers: [UserController],
|
|
103
|
+
providers: [UserService, UserRepository],
|
|
104
|
+
exports: [UserService], // Only export the service — never the repository
|
|
105
|
+
})
|
|
106
|
+
export class UserModule {}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Controller Pattern (Transport ONLY)
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// src/modules/user/user.controller.ts
|
|
113
|
+
import { Controller, Get, Post, Body, Param, Query, UseGuards } from '@nestjs/common';
|
|
114
|
+
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagger';
|
|
115
|
+
import { UserService } from './user.service';
|
|
116
|
+
import { CreateUserDto } from './dto/create-user.dto';
|
|
117
|
+
import { UserResponseDto } from './dto/user-response.dto';
|
|
118
|
+
import { JwtAuthGuard } from '@/shared/guards/jwt-auth.guard';
|
|
119
|
+
|
|
120
|
+
@ApiTags('Users')
|
|
121
|
+
@Controller('users')
|
|
122
|
+
export class UserController {
|
|
123
|
+
constructor(private readonly userService: UserService) {}
|
|
124
|
+
|
|
125
|
+
@ApiOperation({ summary: 'Create a new user' })
|
|
126
|
+
@ApiResponse({ status: 201, type: UserResponseDto })
|
|
127
|
+
@ApiResponse({ status: 400, description: 'Validation error' })
|
|
128
|
+
@ApiResponse({ status: 409, description: 'Email already exists' })
|
|
129
|
+
@Post()
|
|
130
|
+
async create(@Body() dto: CreateUserDto): Promise<UserResponseDto> {
|
|
131
|
+
// Controller does: validate input (via pipe) → call service → return response
|
|
132
|
+
// Controller does NOT: query database, check business rules, format data
|
|
133
|
+
return this.userService.create(dto);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@ApiBearerAuth()
|
|
137
|
+
@UseGuards(JwtAuthGuard)
|
|
138
|
+
@ApiOperation({ summary: 'List users with pagination' })
|
|
139
|
+
@Get()
|
|
140
|
+
async findAll(
|
|
141
|
+
@Query('page') page: number = 1,
|
|
142
|
+
@Query('limit') limit: number = 20,
|
|
143
|
+
): Promise<UserResponseDto[]> {
|
|
144
|
+
return this.userService.findAll({ page, limit });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Service Pattern (Business Logic)
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
// src/modules/user/user.service.ts
|
|
153
|
+
import { Injectable, ConflictException } from '@nestjs/common';
|
|
154
|
+
import { UserRepository } from './user.repository';
|
|
155
|
+
import { CreateUserDto } from './dto/create-user.dto';
|
|
156
|
+
import { hash } from 'bcrypt';
|
|
157
|
+
|
|
158
|
+
@Injectable()
|
|
159
|
+
export class UserService {
|
|
160
|
+
constructor(private readonly userRepository: UserRepository) {}
|
|
161
|
+
|
|
162
|
+
async create(dto: CreateUserDto): Promise<UserResponseDto> {
|
|
163
|
+
// Business rule: email must be unique
|
|
164
|
+
const existingUser = await this.userRepository.findByEmail(dto.email);
|
|
165
|
+
if (existingUser) {
|
|
166
|
+
throw new ConflictException('Email already registered');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Business rule: hash password before storage
|
|
170
|
+
const hashedPassword = await hash(dto.password, 12);
|
|
171
|
+
|
|
172
|
+
return this.userRepository.create({
|
|
173
|
+
...dto,
|
|
174
|
+
password: hashedPassword,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Repository Pattern (Data Access Only)
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
// src/modules/user/user.repository.ts
|
|
184
|
+
import { Injectable } from '@nestjs/common';
|
|
185
|
+
import { PrismaService } from '@/shared/lib/prisma.service';
|
|
186
|
+
import { User } from '@prisma/client';
|
|
187
|
+
|
|
188
|
+
@Injectable()
|
|
189
|
+
export class UserRepository {
|
|
190
|
+
constructor(private readonly prisma: PrismaService) {}
|
|
191
|
+
|
|
192
|
+
async findByEmail(email: string): Promise<User | null> {
|
|
193
|
+
return this.prisma.user.findUnique({ where: { email } });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async create(data: Omit<User, 'id' | 'createdAt' | 'updatedAt'>): Promise<User> {
|
|
197
|
+
return this.prisma.user.create({ data });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async findAll(params: { skip: number; take: number }): Promise<User[]> {
|
|
201
|
+
return this.prisma.user.findMany({
|
|
202
|
+
skip: params.skip,
|
|
203
|
+
take: params.take,
|
|
204
|
+
select: { id: true, email: true, name: true, createdAt: true },
|
|
205
|
+
// NEVER select password
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Swagger Setup (Mandatory)
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
// src/main.ts
|
|
215
|
+
import { NestFactory } from '@nestjs/core';
|
|
216
|
+
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
|
217
|
+
import { AppModule } from './app.module';
|
|
218
|
+
|
|
219
|
+
async function bootstrap() {
|
|
220
|
+
const app = await NestFactory.create(AppModule);
|
|
221
|
+
|
|
222
|
+
// Swagger — auto-generates API documentation
|
|
223
|
+
const config = new DocumentBuilder()
|
|
224
|
+
.setTitle('API Documentation')
|
|
225
|
+
.setVersion('1.0')
|
|
226
|
+
.addBearerAuth()
|
|
227
|
+
.build();
|
|
228
|
+
|
|
229
|
+
const document = SwaggerModule.createDocument(app, config);
|
|
230
|
+
SwaggerModule.setup('docs', app, document);
|
|
231
|
+
|
|
232
|
+
await app.listen(3000);
|
|
233
|
+
}
|
|
234
|
+
bootstrap();
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Scaffolding Checklist
|
|
238
|
+
|
|
239
|
+
- [ ] Every module follows Controller → Service → Repository layering
|
|
240
|
+
- [ ] DTOs use Zod or class-validator for ALL input validation
|
|
241
|
+
- [ ] Swagger decorators on every controller method
|
|
242
|
+
- [ ] Global exception filter catches and formats all errors
|
|
243
|
+
- [ ] Repository NEVER exposes raw Prisma client outside its module
|
|
244
|
+
- [ ] Service is exported, Repository is NOT (module encapsulation)
|
|
245
|
+
- [ ] Tests exist for service logic (unit) and controller routes (integration)
|
|
246
|
+
- [ ] Environment variables validated with Zod at startup
|
|
247
|
+
- [ ] Health check endpoint exists at `/health`
|