siesa-agents 2.1.1 → 2.1.3
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 +83 -83
- package/bin/install.js +400 -399
- package/bin/prepare-publish.js +26 -26
- package/bin/restore-folders.js +26 -26
- package/bmad-core/agent-teams/team-all.yaml +15 -15
- package/bmad-core/agent-teams/team-fullstack.yaml +19 -19
- package/bmad-core/agent-teams/team-ide-minimal.yaml +11 -11
- package/bmad-core/agent-teams/team-no-ui.yaml +14 -14
- package/bmad-core/agents/analyst.md +84 -84
- package/bmad-core/agents/architect.md +94 -94
- package/bmad-core/agents/backend-agent.md +189 -189
- package/bmad-core/agents/bmad-master.md +110 -110
- package/bmad-core/agents/bmad-orchestrator.md +147 -147
- package/bmad-core/agents/dev.md +81 -81
- package/bmad-core/agents/frontend-agent.md +168 -168
- package/bmad-core/agents/pm.md +84 -84
- package/bmad-core/agents/po.md +79 -79
- package/bmad-core/agents/qa.md +91 -91
- package/bmad-core/agents/sm.md +65 -65
- package/bmad-core/agents/ux-expert.md +69 -69
- package/bmad-core/checklists/architect-checklist.md +440 -440
- package/bmad-core/checklists/backend-checklist.md +142 -142
- package/bmad-core/checklists/change-checklist.md +184 -184
- package/bmad-core/checklists/frontend-checklist.md +105 -105
- package/bmad-core/checklists/pm-checklist.md +372 -372
- package/bmad-core/checklists/po-master-checklist.md +434 -434
- package/bmad-core/checklists/story-dod-checklist.md +96 -96
- package/bmad-core/checklists/story-draft-checklist.md +155 -155
- package/bmad-core/core-config.yaml +22 -22
- package/bmad-core/data/backend-standards.md +439 -439
- package/bmad-core/data/bmad-kb.md +809 -809
- package/bmad-core/data/brainstorming-techniques.md +38 -38
- package/bmad-core/data/elicitation-methods.md +156 -156
- package/bmad-core/data/frontend-standards.md +323 -323
- package/bmad-core/data/technical-preferences.md +5 -5
- package/bmad-core/data/test-levels-framework.md +148 -148
- package/bmad-core/data/test-priorities-matrix.md +174 -174
- package/bmad-core/enhanced-ide-development-workflow.md +248 -248
- package/bmad-core/install-manifest.yaml +230 -230
- package/bmad-core/tasks/advanced-elicitation.md +119 -119
- package/bmad-core/tasks/apply-qa-fixes.md +150 -150
- package/bmad-core/tasks/brownfield-create-epic.md +162 -162
- package/bmad-core/tasks/brownfield-create-story.md +149 -149
- package/bmad-core/tasks/correct-course.md +72 -72
- package/bmad-core/tasks/create-brownfield-story.md +314 -314
- package/bmad-core/tasks/create-component.md +102 -102
- package/bmad-core/tasks/create-deep-research-prompt.md +280 -280
- package/bmad-core/tasks/create-doc.md +103 -103
- package/bmad-core/tasks/create-entity.md +132 -132
- package/bmad-core/tasks/create-feature.md +90 -90
- package/bmad-core/tasks/create-next-story.md +114 -114
- package/bmad-core/tasks/create-service.md +117 -117
- package/bmad-core/tasks/create-use-case.md +140 -140
- package/bmad-core/tasks/document-project.md +345 -345
- package/bmad-core/tasks/execute-checklist.md +88 -88
- package/bmad-core/tasks/facilitate-brainstorming-session.md +138 -138
- package/bmad-core/tasks/generate-ai-frontend-prompt.md +53 -53
- package/bmad-core/tasks/index-docs.md +175 -175
- package/bmad-core/tasks/kb-mode-interaction.md +77 -77
- package/bmad-core/tasks/nfr-assess.md +345 -345
- package/bmad-core/tasks/qa-gate.md +163 -163
- package/bmad-core/tasks/review-story.md +316 -316
- package/bmad-core/tasks/risk-profile.md +355 -355
- package/bmad-core/tasks/scaffold-backend.md +110 -110
- package/bmad-core/tasks/scaffold-frontend.md +78 -78
- package/bmad-core/tasks/shard-doc.md +187 -187
- package/bmad-core/tasks/test-design.md +176 -176
- package/bmad-core/tasks/trace-requirements.md +266 -266
- package/bmad-core/tasks/validate-next-story.md +136 -136
- package/bmad-core/templates/architecture-tmpl.yaml +662 -662
- package/bmad-core/templates/brainstorming-output-tmpl.yaml +156 -156
- package/bmad-core/templates/brownfield-architecture-tmpl.yaml +477 -477
- package/bmad-core/templates/brownfield-prd-tmpl.yaml +281 -281
- package/bmad-core/templates/competitor-analysis-tmpl.yaml +307 -307
- package/bmad-core/templates/front-end-architecture-tmpl.yaml +258 -258
- package/bmad-core/templates/front-end-spec-tmpl.yaml +350 -350
- package/bmad-core/templates/fullstack-architecture-tmpl.yaml +824 -824
- package/bmad-core/templates/market-research-tmpl.yaml +253 -253
- package/bmad-core/templates/prd-tmpl.yaml +203 -203
- package/bmad-core/templates/project-brief-tmpl.yaml +222 -222
- package/bmad-core/templates/qa-gate-tmpl.yaml +103 -103
- package/bmad-core/templates/story-tmpl.yaml +138 -138
- package/bmad-core/user-guide.md +530 -530
- package/bmad-core/utils/bmad-doc-template.md +327 -327
- package/bmad-core/utils/workflow-management.md +71 -71
- package/bmad-core/workflows/brownfield-fullstack.yaml +298 -298
- package/bmad-core/workflows/brownfield-service.yaml +188 -188
- package/bmad-core/workflows/brownfield-ui.yaml +198 -198
- package/bmad-core/workflows/greenfield-fullstack.yaml +241 -241
- package/bmad-core/workflows/greenfield-service.yaml +207 -207
- package/bmad-core/workflows/greenfield-ui.yaml +236 -236
- package/bmad-core/working-in-the-brownfield.md +606 -606
- package/claude/commands/BMad/agents/analyst.md +88 -0
- package/claude/commands/BMad/agents/architect.md +89 -0
- package/claude/commands/BMad/agents/backend.md +188 -0
- package/claude/commands/BMad/agents/bmad-master.md +114 -0
- package/claude/commands/BMad/agents/bmad-orchestrator.md +151 -0
- package/claude/commands/BMad/agents/dev.md +85 -0
- package/claude/commands/BMad/agents/frontend.md +151 -0
- package/claude/commands/BMad/agents/pm.md +88 -0
- package/claude/commands/BMad/agents/po.md +83 -0
- package/claude/commands/BMad/agents/qa.md +95 -0
- package/claude/commands/BMad/agents/sm.md +69 -0
- package/claude/commands/BMad/agents/ux-expert.md +73 -0
- package/claude/commands/BMad/tasks/advanced-elicitation.md +123 -0
- package/claude/commands/BMad/tasks/apply-qa-fixes.md +154 -0
- package/claude/commands/BMad/tasks/brownfield-create-epic.md +166 -0
- package/claude/commands/BMad/tasks/brownfield-create-story.md +153 -0
- package/claude/commands/BMad/tasks/correct-course.md +76 -0
- package/claude/commands/BMad/tasks/create-brownfield-story.md +318 -0
- package/claude/commands/BMad/tasks/create-deep-research-prompt.md +284 -0
- package/claude/commands/BMad/tasks/create-doc.md +107 -0
- package/claude/commands/BMad/tasks/create-next-story.md +118 -0
- package/claude/commands/BMad/tasks/document-project.md +349 -0
- package/claude/commands/BMad/tasks/execute-checklist.md +92 -0
- package/claude/commands/BMad/tasks/facilitate-brainstorming-session.md +142 -0
- package/claude/commands/BMad/tasks/generate-ai-frontend-prompt.md +57 -0
- package/claude/commands/BMad/tasks/index-docs.md +179 -0
- package/claude/commands/BMad/tasks/kb-mode-interaction.md +81 -0
- package/claude/commands/BMad/tasks/nfr-assess.md +349 -0
- package/claude/commands/BMad/tasks/qa-gate.md +167 -0
- package/claude/commands/BMad/tasks/review-story.md +320 -0
- package/claude/commands/BMad/tasks/risk-profile.md +359 -0
- package/claude/commands/BMad/tasks/shard-doc.md +191 -0
- package/claude/commands/BMad/tasks/test-design.md +180 -0
- package/claude/commands/BMad/tasks/trace-requirements.md +270 -0
- package/claude/commands/BMad/tasks/validate-next-story.md +140 -0
- package/claude/settings.local.json +20 -0
- package/github/b-mad-expert.md +742 -742
- package/github/chatmodes/analyst.chatmode.md +89 -89
- package/github/chatmodes/architect.chatmode.md +97 -97
- package/github/chatmodes/backend.chatmode.md +194 -194
- package/github/chatmodes/bmad-master.chatmode.md +115 -115
- package/github/chatmodes/bmad-orchestrator.chatmode.md +152 -152
- package/github/chatmodes/dev.chatmode.md +86 -86
- package/github/chatmodes/frontend.chatmode.md +157 -157
- package/github/chatmodes/pm.chatmode.md +89 -89
- package/github/chatmodes/po.chatmode.md +84 -84
- package/github/chatmodes/qa.chatmode.md +96 -96
- package/github/chatmodes/sm.chatmode.md +70 -70
- package/github/chatmodes/ux-expert.chatmode.md +74 -74
- package/index.js +9 -9
- package/package.json +37 -36
- package/vscode/mcp.json +11 -11
- package/vscode/settings.json +12 -12
|
@@ -1,440 +1,440 @@
|
|
|
1
|
-
# Backend Development Standards
|
|
2
|
-
|
|
3
|
-
## Architecture Principles
|
|
4
|
-
|
|
5
|
-
### Hexagonal Architecture Implementation
|
|
6
|
-
- **Application Core**: Domain entities, value objects, aggregates, and domain services
|
|
7
|
-
- **Primary Ports**: Use cases, commands, queries, and application services
|
|
8
|
-
- **Primary Adapters**: REST controllers, GraphQL resolvers, message handlers
|
|
9
|
-
- **Secondary Ports**: Repository interfaces, external service interfaces
|
|
10
|
-
- **Secondary Adapters**: Prisma repositories, HTTP clients, message publishers
|
|
11
|
-
|
|
12
|
-
### Dependency Rules
|
|
13
|
-
- Application core must not depend on external frameworks
|
|
14
|
-
- All dependencies point inward toward the domain
|
|
15
|
-
- Use dependency inversion for all external concerns
|
|
16
|
-
- Interfaces defined in application layer, implementations in infrastructure
|
|
17
|
-
|
|
18
|
-
## Technology Stack Standards
|
|
19
|
-
|
|
20
|
-
### Core Technologies
|
|
21
|
-
- **NestJS**: 10+ with TypeScript and decorators
|
|
22
|
-
- **TypeScript**: Strict mode enabled, no `any` types
|
|
23
|
-
- **Prisma**: ORM for database operations (no raw queries)
|
|
24
|
-
- **Jest**: Unit and integration testing
|
|
25
|
-
- **Class-validator**: Request validation and transformation
|
|
26
|
-
|
|
27
|
-
### Framework Selection Rules
|
|
28
|
-
- **Default**: Always use NestJS 10+ with TypeScript
|
|
29
|
-
- **Database**: Prisma ORM only - no raw SQL queries allowed
|
|
30
|
-
- **Testing**: TDD approach with Jest and Supertest
|
|
31
|
-
- **Documentation**: Swagger/OpenAPI for all endpoints
|
|
32
|
-
|
|
33
|
-
### Development Tools
|
|
34
|
-
- **Nx**: MonoRepo management and build system
|
|
35
|
-
- **ESLint + Prettier**: Code quality and formatting
|
|
36
|
-
- **Husky**: Git hooks for pre-commit validation
|
|
37
|
-
- **Winston**: Structured logging
|
|
38
|
-
- **Redis**: Caching and message transport
|
|
39
|
-
|
|
40
|
-
## Domain-Driven Design Standards
|
|
41
|
-
|
|
42
|
-
### Entity Structure
|
|
43
|
-
```typescript
|
|
44
|
-
export class UserEntity extends AggregateRoot {
|
|
45
|
-
private constructor(
|
|
46
|
-
public readonly id: UserId,
|
|
47
|
-
private _email: EmailValueObject,
|
|
48
|
-
private _name: NameValueObject,
|
|
49
|
-
private _createdAt: Date,
|
|
50
|
-
) {
|
|
51
|
-
super();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
static create(props: CreateUserProps): UserEntity {
|
|
55
|
-
const user = new UserEntity(
|
|
56
|
-
UserId.generate(),
|
|
57
|
-
EmailValueObject.create(props.email),
|
|
58
|
-
NameValueObject.create(props.name),
|
|
59
|
-
new Date(),
|
|
60
|
-
);
|
|
61
|
-
user.addDomainEvent(new UserCreatedEvent(user.id));
|
|
62
|
-
return user;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
public updateEmail(newEmail: EmailValueObject): void {
|
|
66
|
-
if (this._email.equals(newEmail)) return;
|
|
67
|
-
this._email = newEmail;
|
|
68
|
-
this.addDomainEvent(new UserEmailUpdatedEvent(this.id, newEmail));
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
get email(): EmailValueObject {
|
|
72
|
-
return this._email;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Value Object Structure
|
|
78
|
-
```typescript
|
|
79
|
-
export class EmailValueObject {
|
|
80
|
-
private constructor(private readonly value: string) {
|
|
81
|
-
this.validate(value);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
static create(value: string): EmailValueObject {
|
|
85
|
-
return new EmailValueObject(value);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
private validate(value: string): void {
|
|
89
|
-
if (!value || !this.isValidEmail(value)) {
|
|
90
|
-
throw new InvalidEmailException(value);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
private isValidEmail(email: string): boolean {
|
|
95
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
96
|
-
return emailRegex.test(email);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
equals(other: EmailValueObject): boolean {
|
|
100
|
-
return this.value === other.value;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
toString(): string {
|
|
104
|
-
return this.value;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Repository Interface Pattern
|
|
110
|
-
```typescript
|
|
111
|
-
export interface UserRepositoryInterface {
|
|
112
|
-
save(user: UserEntity): Promise<UserEntity>;
|
|
113
|
-
findById(id: UserId): Promise<UserEntity | null>;
|
|
114
|
-
findByEmail(email: EmailValueObject): Promise<UserEntity | null>;
|
|
115
|
-
findAll(criteria: FindUsersCriteria): Promise<UserEntity[]>;
|
|
116
|
-
delete(id: UserId): Promise<void>;
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Use Case Standards
|
|
121
|
-
|
|
122
|
-
### Use Case Structure
|
|
123
|
-
```typescript
|
|
124
|
-
@Injectable()
|
|
125
|
-
export class CreateUserUseCase {
|
|
126
|
-
constructor(
|
|
127
|
-
@Inject(USER_REPOSITORY)
|
|
128
|
-
private readonly userRepository: UserRepositoryInterface,
|
|
129
|
-
@Inject(EVENT_BUS)
|
|
130
|
-
private readonly eventBus: EventBusInterface,
|
|
131
|
-
) {}
|
|
132
|
-
|
|
133
|
-
async execute(command: CreateUserCommand): Promise<UserResponseDto> {
|
|
134
|
-
// 1. Validate business rules
|
|
135
|
-
await this.validateUserDoesNotExist(command.email);
|
|
136
|
-
|
|
137
|
-
// 2. Create domain entity
|
|
138
|
-
const user = UserEntity.create({
|
|
139
|
-
email: command.email,
|
|
140
|
-
name: command.name,
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// 3. Persist entity
|
|
144
|
-
const savedUser = await this.userRepository.save(user);
|
|
145
|
-
|
|
146
|
-
// 4. Publish domain events
|
|
147
|
-
await this.eventBus.publishAll(savedUser.getUncommittedEvents());
|
|
148
|
-
savedUser.markEventsAsCommitted();
|
|
149
|
-
|
|
150
|
-
// 5. Return response DTO
|
|
151
|
-
return UserResponseDto.fromEntity(savedUser);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
private async validateUserDoesNotExist(email: string): Promise<void> {
|
|
155
|
-
const emailVO = EmailValueObject.create(email);
|
|
156
|
-
const existingUser = await this.userRepository.findByEmail(emailVO);
|
|
157
|
-
if (existingUser) {
|
|
158
|
-
throw new UserAlreadyExistsException(email);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### Command/Query Structure
|
|
165
|
-
```typescript
|
|
166
|
-
export class CreateUserCommand {
|
|
167
|
-
@IsEmail()
|
|
168
|
-
@IsNotEmpty()
|
|
169
|
-
readonly email: string;
|
|
170
|
-
|
|
171
|
-
@IsString()
|
|
172
|
-
@IsNotEmpty()
|
|
173
|
-
@Length(2, 50)
|
|
174
|
-
readonly name: string;
|
|
175
|
-
|
|
176
|
-
@IsOptional()
|
|
177
|
-
@IsString()
|
|
178
|
-
readonly organizationId?: string;
|
|
179
|
-
}
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
## Testing Standards
|
|
183
|
-
|
|
184
|
-
### Testing Strategy
|
|
185
|
-
- **Unit Tests**: Domain entities, value objects, use cases
|
|
186
|
-
- **Integration Tests**: Repository implementations, external services
|
|
187
|
-
- **E2E Tests**: Complete API workflows
|
|
188
|
-
- **Contract Tests**: External service integrations
|
|
189
|
-
|
|
190
|
-
### Test Structure
|
|
191
|
-
```typescript
|
|
192
|
-
describe('CreateUserUseCase', () => {
|
|
193
|
-
let useCase: CreateUserUseCase;
|
|
194
|
-
let userRepository: jest.Mocked<UserRepositoryInterface>;
|
|
195
|
-
let eventBus: jest.Mocked<EventBusInterface>;
|
|
196
|
-
|
|
197
|
-
beforeEach(async () => {
|
|
198
|
-
const module = await Test.createTestingModule({
|
|
199
|
-
providers: [
|
|
200
|
-
CreateUserUseCase,
|
|
201
|
-
{
|
|
202
|
-
provide: USER_REPOSITORY,
|
|
203
|
-
useValue: {
|
|
204
|
-
save: jest.fn(),
|
|
205
|
-
findByEmail: jest.fn(),
|
|
206
|
-
},
|
|
207
|
-
},
|
|
208
|
-
{
|
|
209
|
-
provide: EVENT_BUS,
|
|
210
|
-
useValue: {
|
|
211
|
-
publishAll: jest.fn(),
|
|
212
|
-
},
|
|
213
|
-
},
|
|
214
|
-
],
|
|
215
|
-
}).compile();
|
|
216
|
-
|
|
217
|
-
useCase = module.get(CreateUserUseCase);
|
|
218
|
-
userRepository = module.get(USER_REPOSITORY);
|
|
219
|
-
eventBus = module.get(EVENT_BUS);
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
describe('execute', () => {
|
|
223
|
-
it('should create user successfully', async () => {
|
|
224
|
-
// Arrange
|
|
225
|
-
const command = new CreateUserCommand();
|
|
226
|
-
command.email = 'test@example.com';
|
|
227
|
-
command.name = 'Test User';
|
|
228
|
-
|
|
229
|
-
const expectedUser = UserEntity.create({
|
|
230
|
-
email: command.email,
|
|
231
|
-
name: command.name,
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
userRepository.findByEmail.mockResolvedValue(null);
|
|
235
|
-
userRepository.save.mockResolvedValue(expectedUser);
|
|
236
|
-
|
|
237
|
-
// Act
|
|
238
|
-
const result = await useCase.execute(command);
|
|
239
|
-
|
|
240
|
-
// Assert
|
|
241
|
-
expect(result.email).toBe(command.email);
|
|
242
|
-
expect(userRepository.save).toHaveBeenCalledWith(expect.any(UserEntity));
|
|
243
|
-
expect(eventBus.publishAll).toHaveBeenCalled();
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it('should throw error when user already exists', async () => {
|
|
247
|
-
// Arrange
|
|
248
|
-
const command = new CreateUserCommand();
|
|
249
|
-
command.email = 'existing@example.com';
|
|
250
|
-
command.name = 'Test User';
|
|
251
|
-
|
|
252
|
-
const existingUser = UserEntity.create({
|
|
253
|
-
email: command.email,
|
|
254
|
-
name: 'Existing User',
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
userRepository.findByEmail.mockResolvedValue(existingUser);
|
|
258
|
-
|
|
259
|
-
// Act & Assert
|
|
260
|
-
await expect(useCase.execute(command)).rejects.toThrow(UserAlreadyExistsException);
|
|
261
|
-
});
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
## API Controller Standards
|
|
267
|
-
|
|
268
|
-
### Controller Structure
|
|
269
|
-
```typescript
|
|
270
|
-
@Controller('users')
|
|
271
|
-
@ApiTags('Users')
|
|
272
|
-
export class UserController {
|
|
273
|
-
constructor(
|
|
274
|
-
private readonly createUserUseCase: CreateUserUseCase,
|
|
275
|
-
private readonly getUserUseCase: GetUserUseCase,
|
|
276
|
-
) {}
|
|
277
|
-
|
|
278
|
-
@Post()
|
|
279
|
-
@ApiOperation({ summary: 'Create a new user' })
|
|
280
|
-
@ApiResponse({ status: 201, description: 'User created successfully', type: UserResponseDto })
|
|
281
|
-
@ApiResponse({ status: 400, description: 'Bad request' })
|
|
282
|
-
@ApiResponse({ status: 409, description: 'User already exists' })
|
|
283
|
-
async createUser(@Body() createUserDto: CreateUserDto): Promise<UserResponseDto> {
|
|
284
|
-
const command = new CreateUserCommand();
|
|
285
|
-
Object.assign(command, createUserDto);
|
|
286
|
-
return this.createUserUseCase.execute(command);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
@Get(':id')
|
|
290
|
-
@ApiOperation({ summary: 'Get user by ID' })
|
|
291
|
-
@ApiParam({ name: 'id', description: 'User ID' })
|
|
292
|
-
@ApiResponse({ status: 200, description: 'User found', type: UserResponseDto })
|
|
293
|
-
@ApiResponse({ status: 404, description: 'User not found' })
|
|
294
|
-
async getUser(@Param('id', ParseUUIDPipe) id: string): Promise<UserResponseDto> {
|
|
295
|
-
const query = new GetUserQuery(id);
|
|
296
|
-
return this.getUserUseCase.execute(query);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
## Database Standards
|
|
302
|
-
|
|
303
|
-
### Prisma Schema Patterns
|
|
304
|
-
```prisma
|
|
305
|
-
model User {
|
|
306
|
-
id String @id @default(cuid())
|
|
307
|
-
email String @unique
|
|
308
|
-
name String
|
|
309
|
-
createdAt DateTime @default(now())
|
|
310
|
-
updatedAt DateTime @updatedAt
|
|
311
|
-
|
|
312
|
-
// Relationships
|
|
313
|
-
orders Order[]
|
|
314
|
-
|
|
315
|
-
@@map("users")
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
model Order {
|
|
319
|
-
id String @id @default(cuid())
|
|
320
|
-
total Decimal @db.Decimal(10, 2)
|
|
321
|
-
status OrderStatus
|
|
322
|
-
userId String
|
|
323
|
-
|
|
324
|
-
// Relationships
|
|
325
|
-
user User @relation(fields: [userId], references: [id])
|
|
326
|
-
items OrderItem[]
|
|
327
|
-
|
|
328
|
-
@@map("orders")
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
enum OrderStatus {
|
|
332
|
-
PENDING
|
|
333
|
-
CONFIRMED
|
|
334
|
-
SHIPPED
|
|
335
|
-
DELIVERED
|
|
336
|
-
CANCELLED
|
|
337
|
-
}
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
### Repository Implementation
|
|
341
|
-
```typescript
|
|
342
|
-
@Injectable()
|
|
343
|
-
export class PrismaUserRepository implements UserRepositoryInterface {
|
|
344
|
-
constructor(private readonly prisma: PrismaService) {}
|
|
345
|
-
|
|
346
|
-
async save(user: UserEntity): Promise<UserEntity> {
|
|
347
|
-
const data = {
|
|
348
|
-
id: user.id.toString(),
|
|
349
|
-
email: user.email.toString(),
|
|
350
|
-
name: user.name.toString(),
|
|
351
|
-
};
|
|
352
|
-
|
|
353
|
-
const savedUser = await this.prisma.user.upsert({
|
|
354
|
-
where: { id: data.id },
|
|
355
|
-
update: data,
|
|
356
|
-
create: data,
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
return this.toDomain(savedUser);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
async findById(id: UserId): Promise<UserEntity | null> {
|
|
363
|
-
const user = await this.prisma.user.findUnique({
|
|
364
|
-
where: { id: id.toString() },
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
return user ? this.toDomain(user) : null;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
private toDomain(prismaUser: User): UserEntity {
|
|
371
|
-
return UserEntity.reconstitute({
|
|
372
|
-
id: UserId.create(prismaUser.id),
|
|
373
|
-
email: EmailValueObject.create(prismaUser.email),
|
|
374
|
-
name: NameValueObject.create(prismaUser.name),
|
|
375
|
-
createdAt: prismaUser.createdAt,
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
## Security Standards
|
|
382
|
-
|
|
383
|
-
### Authentication & Authorization
|
|
384
|
-
- JWT tokens with proper expiration
|
|
385
|
-
- Role-based access control (RBAC)
|
|
386
|
-
- Input validation on all endpoints
|
|
387
|
-
- Rate limiting for public endpoints
|
|
388
|
-
- HTTPS only in production
|
|
389
|
-
|
|
390
|
-
### Data Protection
|
|
391
|
-
- Encrypt sensitive data at rest
|
|
392
|
-
- Use environment variables for secrets
|
|
393
|
-
- Implement audit logging
|
|
394
|
-
- Regular security updates
|
|
395
|
-
- OWASP compliance
|
|
396
|
-
|
|
397
|
-
## Performance Standards
|
|
398
|
-
|
|
399
|
-
### Database Optimization
|
|
400
|
-
- Proper indexing strategies
|
|
401
|
-
- Connection pooling
|
|
402
|
-
- Query optimization
|
|
403
|
-
- Pagination for large datasets
|
|
404
|
-
- Database monitoring
|
|
405
|
-
|
|
406
|
-
### Caching Strategy
|
|
407
|
-
- Redis for session data
|
|
408
|
-
- Application-level caching
|
|
409
|
-
- HTTP caching headers
|
|
410
|
-
- CDN for static assets
|
|
411
|
-
- Cache invalidation patterns
|
|
412
|
-
|
|
413
|
-
## MonoRepo Organization
|
|
414
|
-
|
|
415
|
-
### Shared Libraries Structure
|
|
416
|
-
```
|
|
417
|
-
libs/
|
|
418
|
-
├── common/
|
|
419
|
-
│ ├── decorators/
|
|
420
|
-
│ ├── filters/
|
|
421
|
-
│ ├── guards/
|
|
422
|
-
│ ├── interceptors/
|
|
423
|
-
│ ├── pipes/
|
|
424
|
-
│ └── utils/
|
|
425
|
-
├── domain-core/
|
|
426
|
-
│ ├── base/
|
|
427
|
-
│ ├── interfaces/
|
|
428
|
-
│ └── exceptions/
|
|
429
|
-
└── database/
|
|
430
|
-
├── base-repository.ts
|
|
431
|
-
├── transaction.decorator.ts
|
|
432
|
-
└── prisma.service.ts
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
### Service Independence
|
|
436
|
-
- Each microservice has its own database
|
|
437
|
-
- Shared code through libraries only
|
|
438
|
-
- Independent deployment pipelines
|
|
439
|
-
- Service-to-service communication via events
|
|
1
|
+
# Backend Development Standards
|
|
2
|
+
|
|
3
|
+
## Architecture Principles
|
|
4
|
+
|
|
5
|
+
### Hexagonal Architecture Implementation
|
|
6
|
+
- **Application Core**: Domain entities, value objects, aggregates, and domain services
|
|
7
|
+
- **Primary Ports**: Use cases, commands, queries, and application services
|
|
8
|
+
- **Primary Adapters**: REST controllers, GraphQL resolvers, message handlers
|
|
9
|
+
- **Secondary Ports**: Repository interfaces, external service interfaces
|
|
10
|
+
- **Secondary Adapters**: Prisma repositories, HTTP clients, message publishers
|
|
11
|
+
|
|
12
|
+
### Dependency Rules
|
|
13
|
+
- Application core must not depend on external frameworks
|
|
14
|
+
- All dependencies point inward toward the domain
|
|
15
|
+
- Use dependency inversion for all external concerns
|
|
16
|
+
- Interfaces defined in application layer, implementations in infrastructure
|
|
17
|
+
|
|
18
|
+
## Technology Stack Standards
|
|
19
|
+
|
|
20
|
+
### Core Technologies
|
|
21
|
+
- **NestJS**: 10+ with TypeScript and decorators
|
|
22
|
+
- **TypeScript**: Strict mode enabled, no `any` types
|
|
23
|
+
- **Prisma**: ORM for database operations (no raw queries)
|
|
24
|
+
- **Jest**: Unit and integration testing
|
|
25
|
+
- **Class-validator**: Request validation and transformation
|
|
26
|
+
|
|
27
|
+
### Framework Selection Rules
|
|
28
|
+
- **Default**: Always use NestJS 10+ with TypeScript
|
|
29
|
+
- **Database**: Prisma ORM only - no raw SQL queries allowed
|
|
30
|
+
- **Testing**: TDD approach with Jest and Supertest
|
|
31
|
+
- **Documentation**: Swagger/OpenAPI for all endpoints
|
|
32
|
+
|
|
33
|
+
### Development Tools
|
|
34
|
+
- **Nx**: MonoRepo management and build system
|
|
35
|
+
- **ESLint + Prettier**: Code quality and formatting
|
|
36
|
+
- **Husky**: Git hooks for pre-commit validation
|
|
37
|
+
- **Winston**: Structured logging
|
|
38
|
+
- **Redis**: Caching and message transport
|
|
39
|
+
|
|
40
|
+
## Domain-Driven Design Standards
|
|
41
|
+
|
|
42
|
+
### Entity Structure
|
|
43
|
+
```typescript
|
|
44
|
+
export class UserEntity extends AggregateRoot {
|
|
45
|
+
private constructor(
|
|
46
|
+
public readonly id: UserId,
|
|
47
|
+
private _email: EmailValueObject,
|
|
48
|
+
private _name: NameValueObject,
|
|
49
|
+
private _createdAt: Date,
|
|
50
|
+
) {
|
|
51
|
+
super();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static create(props: CreateUserProps): UserEntity {
|
|
55
|
+
const user = new UserEntity(
|
|
56
|
+
UserId.generate(),
|
|
57
|
+
EmailValueObject.create(props.email),
|
|
58
|
+
NameValueObject.create(props.name),
|
|
59
|
+
new Date(),
|
|
60
|
+
);
|
|
61
|
+
user.addDomainEvent(new UserCreatedEvent(user.id));
|
|
62
|
+
return user;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public updateEmail(newEmail: EmailValueObject): void {
|
|
66
|
+
if (this._email.equals(newEmail)) return;
|
|
67
|
+
this._email = newEmail;
|
|
68
|
+
this.addDomainEvent(new UserEmailUpdatedEvent(this.id, newEmail));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
get email(): EmailValueObject {
|
|
72
|
+
return this._email;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Value Object Structure
|
|
78
|
+
```typescript
|
|
79
|
+
export class EmailValueObject {
|
|
80
|
+
private constructor(private readonly value: string) {
|
|
81
|
+
this.validate(value);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static create(value: string): EmailValueObject {
|
|
85
|
+
return new EmailValueObject(value);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private validate(value: string): void {
|
|
89
|
+
if (!value || !this.isValidEmail(value)) {
|
|
90
|
+
throw new InvalidEmailException(value);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private isValidEmail(email: string): boolean {
|
|
95
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
96
|
+
return emailRegex.test(email);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
equals(other: EmailValueObject): boolean {
|
|
100
|
+
return this.value === other.value;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
toString(): string {
|
|
104
|
+
return this.value;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Repository Interface Pattern
|
|
110
|
+
```typescript
|
|
111
|
+
export interface UserRepositoryInterface {
|
|
112
|
+
save(user: UserEntity): Promise<UserEntity>;
|
|
113
|
+
findById(id: UserId): Promise<UserEntity | null>;
|
|
114
|
+
findByEmail(email: EmailValueObject): Promise<UserEntity | null>;
|
|
115
|
+
findAll(criteria: FindUsersCriteria): Promise<UserEntity[]>;
|
|
116
|
+
delete(id: UserId): Promise<void>;
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Use Case Standards
|
|
121
|
+
|
|
122
|
+
### Use Case Structure
|
|
123
|
+
```typescript
|
|
124
|
+
@Injectable()
|
|
125
|
+
export class CreateUserUseCase {
|
|
126
|
+
constructor(
|
|
127
|
+
@Inject(USER_REPOSITORY)
|
|
128
|
+
private readonly userRepository: UserRepositoryInterface,
|
|
129
|
+
@Inject(EVENT_BUS)
|
|
130
|
+
private readonly eventBus: EventBusInterface,
|
|
131
|
+
) {}
|
|
132
|
+
|
|
133
|
+
async execute(command: CreateUserCommand): Promise<UserResponseDto> {
|
|
134
|
+
// 1. Validate business rules
|
|
135
|
+
await this.validateUserDoesNotExist(command.email);
|
|
136
|
+
|
|
137
|
+
// 2. Create domain entity
|
|
138
|
+
const user = UserEntity.create({
|
|
139
|
+
email: command.email,
|
|
140
|
+
name: command.name,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// 3. Persist entity
|
|
144
|
+
const savedUser = await this.userRepository.save(user);
|
|
145
|
+
|
|
146
|
+
// 4. Publish domain events
|
|
147
|
+
await this.eventBus.publishAll(savedUser.getUncommittedEvents());
|
|
148
|
+
savedUser.markEventsAsCommitted();
|
|
149
|
+
|
|
150
|
+
// 5. Return response DTO
|
|
151
|
+
return UserResponseDto.fromEntity(savedUser);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private async validateUserDoesNotExist(email: string): Promise<void> {
|
|
155
|
+
const emailVO = EmailValueObject.create(email);
|
|
156
|
+
const existingUser = await this.userRepository.findByEmail(emailVO);
|
|
157
|
+
if (existingUser) {
|
|
158
|
+
throw new UserAlreadyExistsException(email);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Command/Query Structure
|
|
165
|
+
```typescript
|
|
166
|
+
export class CreateUserCommand {
|
|
167
|
+
@IsEmail()
|
|
168
|
+
@IsNotEmpty()
|
|
169
|
+
readonly email: string;
|
|
170
|
+
|
|
171
|
+
@IsString()
|
|
172
|
+
@IsNotEmpty()
|
|
173
|
+
@Length(2, 50)
|
|
174
|
+
readonly name: string;
|
|
175
|
+
|
|
176
|
+
@IsOptional()
|
|
177
|
+
@IsString()
|
|
178
|
+
readonly organizationId?: string;
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Testing Standards
|
|
183
|
+
|
|
184
|
+
### Testing Strategy
|
|
185
|
+
- **Unit Tests**: Domain entities, value objects, use cases
|
|
186
|
+
- **Integration Tests**: Repository implementations, external services
|
|
187
|
+
- **E2E Tests**: Complete API workflows
|
|
188
|
+
- **Contract Tests**: External service integrations
|
|
189
|
+
|
|
190
|
+
### Test Structure
|
|
191
|
+
```typescript
|
|
192
|
+
describe('CreateUserUseCase', () => {
|
|
193
|
+
let useCase: CreateUserUseCase;
|
|
194
|
+
let userRepository: jest.Mocked<UserRepositoryInterface>;
|
|
195
|
+
let eventBus: jest.Mocked<EventBusInterface>;
|
|
196
|
+
|
|
197
|
+
beforeEach(async () => {
|
|
198
|
+
const module = await Test.createTestingModule({
|
|
199
|
+
providers: [
|
|
200
|
+
CreateUserUseCase,
|
|
201
|
+
{
|
|
202
|
+
provide: USER_REPOSITORY,
|
|
203
|
+
useValue: {
|
|
204
|
+
save: jest.fn(),
|
|
205
|
+
findByEmail: jest.fn(),
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
provide: EVENT_BUS,
|
|
210
|
+
useValue: {
|
|
211
|
+
publishAll: jest.fn(),
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
}).compile();
|
|
216
|
+
|
|
217
|
+
useCase = module.get(CreateUserUseCase);
|
|
218
|
+
userRepository = module.get(USER_REPOSITORY);
|
|
219
|
+
eventBus = module.get(EVENT_BUS);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
describe('execute', () => {
|
|
223
|
+
it('should create user successfully', async () => {
|
|
224
|
+
// Arrange
|
|
225
|
+
const command = new CreateUserCommand();
|
|
226
|
+
command.email = 'test@example.com';
|
|
227
|
+
command.name = 'Test User';
|
|
228
|
+
|
|
229
|
+
const expectedUser = UserEntity.create({
|
|
230
|
+
email: command.email,
|
|
231
|
+
name: command.name,
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
userRepository.findByEmail.mockResolvedValue(null);
|
|
235
|
+
userRepository.save.mockResolvedValue(expectedUser);
|
|
236
|
+
|
|
237
|
+
// Act
|
|
238
|
+
const result = await useCase.execute(command);
|
|
239
|
+
|
|
240
|
+
// Assert
|
|
241
|
+
expect(result.email).toBe(command.email);
|
|
242
|
+
expect(userRepository.save).toHaveBeenCalledWith(expect.any(UserEntity));
|
|
243
|
+
expect(eventBus.publishAll).toHaveBeenCalled();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('should throw error when user already exists', async () => {
|
|
247
|
+
// Arrange
|
|
248
|
+
const command = new CreateUserCommand();
|
|
249
|
+
command.email = 'existing@example.com';
|
|
250
|
+
command.name = 'Test User';
|
|
251
|
+
|
|
252
|
+
const existingUser = UserEntity.create({
|
|
253
|
+
email: command.email,
|
|
254
|
+
name: 'Existing User',
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
userRepository.findByEmail.mockResolvedValue(existingUser);
|
|
258
|
+
|
|
259
|
+
// Act & Assert
|
|
260
|
+
await expect(useCase.execute(command)).rejects.toThrow(UserAlreadyExistsException);
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## API Controller Standards
|
|
267
|
+
|
|
268
|
+
### Controller Structure
|
|
269
|
+
```typescript
|
|
270
|
+
@Controller('users')
|
|
271
|
+
@ApiTags('Users')
|
|
272
|
+
export class UserController {
|
|
273
|
+
constructor(
|
|
274
|
+
private readonly createUserUseCase: CreateUserUseCase,
|
|
275
|
+
private readonly getUserUseCase: GetUserUseCase,
|
|
276
|
+
) {}
|
|
277
|
+
|
|
278
|
+
@Post()
|
|
279
|
+
@ApiOperation({ summary: 'Create a new user' })
|
|
280
|
+
@ApiResponse({ status: 201, description: 'User created successfully', type: UserResponseDto })
|
|
281
|
+
@ApiResponse({ status: 400, description: 'Bad request' })
|
|
282
|
+
@ApiResponse({ status: 409, description: 'User already exists' })
|
|
283
|
+
async createUser(@Body() createUserDto: CreateUserDto): Promise<UserResponseDto> {
|
|
284
|
+
const command = new CreateUserCommand();
|
|
285
|
+
Object.assign(command, createUserDto);
|
|
286
|
+
return this.createUserUseCase.execute(command);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
@Get(':id')
|
|
290
|
+
@ApiOperation({ summary: 'Get user by ID' })
|
|
291
|
+
@ApiParam({ name: 'id', description: 'User ID' })
|
|
292
|
+
@ApiResponse({ status: 200, description: 'User found', type: UserResponseDto })
|
|
293
|
+
@ApiResponse({ status: 404, description: 'User not found' })
|
|
294
|
+
async getUser(@Param('id', ParseUUIDPipe) id: string): Promise<UserResponseDto> {
|
|
295
|
+
const query = new GetUserQuery(id);
|
|
296
|
+
return this.getUserUseCase.execute(query);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Database Standards
|
|
302
|
+
|
|
303
|
+
### Prisma Schema Patterns
|
|
304
|
+
```prisma
|
|
305
|
+
model User {
|
|
306
|
+
id String @id @default(cuid())
|
|
307
|
+
email String @unique
|
|
308
|
+
name String
|
|
309
|
+
createdAt DateTime @default(now())
|
|
310
|
+
updatedAt DateTime @updatedAt
|
|
311
|
+
|
|
312
|
+
// Relationships
|
|
313
|
+
orders Order[]
|
|
314
|
+
|
|
315
|
+
@@map("users")
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
model Order {
|
|
319
|
+
id String @id @default(cuid())
|
|
320
|
+
total Decimal @db.Decimal(10, 2)
|
|
321
|
+
status OrderStatus
|
|
322
|
+
userId String
|
|
323
|
+
|
|
324
|
+
// Relationships
|
|
325
|
+
user User @relation(fields: [userId], references: [id])
|
|
326
|
+
items OrderItem[]
|
|
327
|
+
|
|
328
|
+
@@map("orders")
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
enum OrderStatus {
|
|
332
|
+
PENDING
|
|
333
|
+
CONFIRMED
|
|
334
|
+
SHIPPED
|
|
335
|
+
DELIVERED
|
|
336
|
+
CANCELLED
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Repository Implementation
|
|
341
|
+
```typescript
|
|
342
|
+
@Injectable()
|
|
343
|
+
export class PrismaUserRepository implements UserRepositoryInterface {
|
|
344
|
+
constructor(private readonly prisma: PrismaService) {}
|
|
345
|
+
|
|
346
|
+
async save(user: UserEntity): Promise<UserEntity> {
|
|
347
|
+
const data = {
|
|
348
|
+
id: user.id.toString(),
|
|
349
|
+
email: user.email.toString(),
|
|
350
|
+
name: user.name.toString(),
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
const savedUser = await this.prisma.user.upsert({
|
|
354
|
+
where: { id: data.id },
|
|
355
|
+
update: data,
|
|
356
|
+
create: data,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
return this.toDomain(savedUser);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
async findById(id: UserId): Promise<UserEntity | null> {
|
|
363
|
+
const user = await this.prisma.user.findUnique({
|
|
364
|
+
where: { id: id.toString() },
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
return user ? this.toDomain(user) : null;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
private toDomain(prismaUser: User): UserEntity {
|
|
371
|
+
return UserEntity.reconstitute({
|
|
372
|
+
id: UserId.create(prismaUser.id),
|
|
373
|
+
email: EmailValueObject.create(prismaUser.email),
|
|
374
|
+
name: NameValueObject.create(prismaUser.name),
|
|
375
|
+
createdAt: prismaUser.createdAt,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## Security Standards
|
|
382
|
+
|
|
383
|
+
### Authentication & Authorization
|
|
384
|
+
- JWT tokens with proper expiration
|
|
385
|
+
- Role-based access control (RBAC)
|
|
386
|
+
- Input validation on all endpoints
|
|
387
|
+
- Rate limiting for public endpoints
|
|
388
|
+
- HTTPS only in production
|
|
389
|
+
|
|
390
|
+
### Data Protection
|
|
391
|
+
- Encrypt sensitive data at rest
|
|
392
|
+
- Use environment variables for secrets
|
|
393
|
+
- Implement audit logging
|
|
394
|
+
- Regular security updates
|
|
395
|
+
- OWASP compliance
|
|
396
|
+
|
|
397
|
+
## Performance Standards
|
|
398
|
+
|
|
399
|
+
### Database Optimization
|
|
400
|
+
- Proper indexing strategies
|
|
401
|
+
- Connection pooling
|
|
402
|
+
- Query optimization
|
|
403
|
+
- Pagination for large datasets
|
|
404
|
+
- Database monitoring
|
|
405
|
+
|
|
406
|
+
### Caching Strategy
|
|
407
|
+
- Redis for session data
|
|
408
|
+
- Application-level caching
|
|
409
|
+
- HTTP caching headers
|
|
410
|
+
- CDN for static assets
|
|
411
|
+
- Cache invalidation patterns
|
|
412
|
+
|
|
413
|
+
## MonoRepo Organization
|
|
414
|
+
|
|
415
|
+
### Shared Libraries Structure
|
|
416
|
+
```
|
|
417
|
+
libs/
|
|
418
|
+
├── common/
|
|
419
|
+
│ ├── decorators/
|
|
420
|
+
│ ├── filters/
|
|
421
|
+
│ ├── guards/
|
|
422
|
+
│ ├── interceptors/
|
|
423
|
+
│ ├── pipes/
|
|
424
|
+
│ └── utils/
|
|
425
|
+
├── domain-core/
|
|
426
|
+
│ ├── base/
|
|
427
|
+
│ ├── interfaces/
|
|
428
|
+
│ └── exceptions/
|
|
429
|
+
└── database/
|
|
430
|
+
├── base-repository.ts
|
|
431
|
+
├── transaction.decorator.ts
|
|
432
|
+
└── prisma.service.ts
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### Service Independence
|
|
436
|
+
- Each microservice has its own database
|
|
437
|
+
- Shared code through libraries only
|
|
438
|
+
- Independent deployment pipelines
|
|
439
|
+
- Service-to-service communication via events
|
|
440
440
|
- No direct database access between services
|