cqrs-pattern — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited cqrs-pattern (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
This skill implements Command Query Responsibility Segregation pattern for NestJS applications.
Use this skill when the user asks to:
| Aspect | Command | Query |
|---|---|---|
| Purpose | Change state | Read state |
| Returns | void/result | data |
| Side effects | Yes | No |
| Idempotent | Depends | Yes |
src/modules/user/application/
├── commands/
│ ├── create-user/
│ │ ├── create-user.command.ts
│ │ └── create-user.handler.ts
│ ├── update-user/
│ │ └── update-user.handler.ts
│ └── delete-user/
│ └── delete-user.handler.ts// commands/create-user/create-user.command.ts
export class CreateUserCommand {
constructor(
public readonly email: string,
public readonly name: string,
public readonly password: string,
) {}
}
// commands/create-user/create-user.handler.ts
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import { CommandBus } from '@nestjs/cqrs';
@CommandHandler(CreateUserCommand)
export class CreateUserHandler implements ICommandHandler<CreateUserCommand> {
constructor(
private readonly userRepository: UserRepository,
private readonly eventBus: EventBus,
) {}
async execute(command: CreateUserCommand): Promise<string> {
// Validation
const existing = await this.userRepository.findByEmail(command.email);
if (existing) {
throw new ConflictException('Email already exists');
}
// Create domain entity
const user = User.create({
email: command.email,
name: command.name,
password: command.password,
});
// Save
await this.userRepository.save(user);
// Publish event
this.eventBus.publish(new UserCreatedEvent(user.id, user.email));
return user.id;
}
}src/modules/user/application/
└── queries/
├── get-user/
│ ├── get-user.query.ts
│ └── get-user.handler.ts
└── list-users/
└── list-users.handler.ts// queries/get-user/get-user.query.ts
export class GetUserQuery {
constructor(
public readonly userId: string,
) {}
}
// queries/get-user/get-user.handler.ts
@QueryHandler(GetUserQuery)
export class GetUserHandler implements IQueryHandler<GetUserQuery> {
constructor(
private readonly userRepository: UserRepository,
) {}
async execute(query: GetUserQuery): Promise<UserDTO> {
const user = await this.userRepository.findById(query.userId);
if (!user) {
throw new NotFoundException('User not found');
}
// Just return DTO, no business logic
return {
id: user.id,
email: user.email,
name: user.name,
createdAt: user.createdAt,
};
}
}@Controller('users')
export class UserController {
constructor(
private readonly commandBus: CommandBus,
private readonly queryBus: QueryBus,
) {}
@Post()
async create(@Body() dto: CreateUserDto): Promise<{ id: string }> {
const command = new CreateUserCommand(dto.email, dto.name, dto.password);
const userId = await this.commandBus.execute(command);
return { id: userId };
}
@Get(':id')
async get(@Param('id') id: string): Promise<UserDTO> {
const query = new GetUserQuery(id);
return await this.queryBus.execute(query);
}
@Get()
async list(@Query() dto: ListUsersDto): Promise<UserDTO[]> {
const query = new ListUsersQuery(dto.limit, dto.offset);
return await this.queryBus.execute(query);
}
}| Aspect | Benefit |
|---|---|
| Semantics | Command name = clear intention |
| Testability | Test each handler independently |
| Audit | Commands = change history |
| Optimization | Separate read/write databases |
| Scalability | Scale queries independently |
// ❌ DON'T: Mixed read/write
@Injectable()
export class UserService {
async getUser(id: string): Promise<User> {
const user = await this.repo.findOne({ where: { id } });
// Business logic in query!
if (user.isBlocked) {
throw new ForbiddenException();
}
return user;
}
async createUser(dto: Dto): Promise<User> {
// Query in command!
const existing = await this.repo.findByEmail(dto.email);
if (existing) {
throw new ConflictException();
}
return this.repo.save(dto);
}
}// ✅ DO: Separate Commands and Queries
// Query - just read, no logic
@QueryHandler(GetUserQuery)
export class GetUserHandler implements IQueryHandler<GetUserQuery> {
async execute(query: GetUserQuery): Promise<User> {
return this.repo.findOne({ where: { id: query.userId } });
}
}
// Command - validate and change state
@CommandHandler(CreateUserCommand)
export class CreateUserHandler implements ICommandHandler<CreateUserCommand> {
async execute(command: CreateUserCommand): Promise<User> {
// Validate here
// Change state here
// Publish events here
return this.repo.save(command);
}
}~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.