name: rockets-business-logic description: This skill should be used when implementing business logic beyond standard CRUD — state machines, approval workflows, event-driven automation, notifications, file uploads, external API integration, or cross-entity orchestration. Use after CRUD modules are generated. Also covers the foundational rule that application services must inject model services, never repositories.
Rockets Business Logic Skill
Implements business logic patterns that go beyond standard CRUD. This is a conceptual skill — the AI adapts parameterized templates from BUSINESS_LOGIC_PATTERNS_GUIDE.md to the domain.
Scope
This skill covers all non-CRUD endpoint/workflow logic plus service-boundary enforcement. It also covers general custom code patterns (model service injection, cross-module dependencies).
Foundational Service Boundary Rules
Apply these rules before any pattern implementation:
- Application services MUST inject model services (or CRUD services when no model service exists), never repositories.
@InjectRepository(...)is forbidden in custom/workflow/non-CRUD services.- If cross-module logic needs an entity without model service, create/export the model service first.
- Modules must import providers from exporting modules; do not inject foreign entity repositories directly.
- Keep controller logic thin: parameter extraction + delegation only.
When to Create a Model Service
Create a model service for a module when:
- The module has custom logic beyond standard CRUD (aggregation, computed fields, business rules).
- Other modules need to consume that entity (cross-module dependency).
Modules with only standard CRUD may omit the model service.
Example: Right vs Wrong
// ✅ ReportService — injects model services only
@Injectable()
export class ReportService {
constructor(
private readonly userModelService: UserModelService,
private readonly taskModelService: TaskModelService,
) {}
async getUsersWithTaskCount(): Promise<UserTaskReportItemDto[]> {
const users = await this.userModelService.find({ order: { email: 'ASC' } });
// ... for each user: taskModelService.find({ where: { userId: user.id } })
}
}
// ❌ Wrong — direct repository in application service
@Injectable()
export class ReportService {
constructor(
@InjectRepository(UserEntity) private userRepo: Repository<UserEntity>,
@InjectRepository(TaskEntity) private taskRepo: Repository<TaskEntity>,
) {}
}
When to Use
After CRUD modules are generated (rockets-crud-generator), use this skill to implement:
- State machines with enforced transitions
- Custom action endpoints (approve, cancel, assign, complete)
- Cross-service workflow orchestration
- Event-driven automation (listeners)
- Email notifications with audit
- File upload pipelines with validation
- External API provider integration
- Deduplication / reference data sync
- Transactional multi-entity operations
- Denormalized views for reporting
Pattern Selection Decision Tree
What does the SBVR rule describe?
|
+-- Entity has status field with enumerated values?
| -> Pattern 1: State Machine + History
|
+-- Non-CRUD verb on entity (approve, cancel, assign, complete)?
| -> Pattern 2: Custom CRUD Actions
|
+-- Workflow crosses 3+ entities in sequence?
| -> Pattern 3: Cross-Service Orchestration
| +-- All entities in same DB and atomicity needed?
| -> Also add Pattern 9: Transactional Multi-Entity
|
+-- "System does X when Y happens" (decoupled side effect)?
| -> Pattern 4: Event-Driven Automation
|
+-- "System sends email/notification when..."?
| -> Pattern 5: Email Notification + Audit
|
+-- File format/size rules, role-based upload restrictions?
| -> Pattern 6: File Upload Pipeline
|
+-- "System queries/retrieves from external API"?
| -> Pattern 7: External API Provider
|
+-- "Check if exists locally, create if not" (reference data sync)?
| -> Pattern 8: Deduplication / Reference Sync
|
+-- Read-heavy joins, dashboard data, role-based filtering?
| -> Pattern 10: Denormalized View
Implementation Checklist Per Pattern
Pattern 1: State Machine
- Create status enum in
{entity}-status.constants.ts - Create transition map (current -> allowed next states with conditions)
- Create
{entity}-status.service.tsinjecting model service - Add status transition endpoint(s) to controller
- Register service in module providers + exports
- Verify: no direct status updates outside status service
Pattern 2: Custom Actions
- Add action method to model service (business logic)
- Add endpoint to controller (parameter extraction + delegation)
- Add ACL decorator for the action endpoint
- Verify: action logic in model service, not controller
Pattern 3: Orchestration
- Create
{workflow}-workflow.service.ts - Inject only model services from participating modules
- Import participating modules in host module
- Verify: no repository injection in workflow service
Pattern 4: Events
- Install
@nestjs/event-emitterif not present - Add
EventEmitterModule.forRoot()to AppModule - Create event class in
events/directory - Create listener in
listeners/directory - Register listener as provider in module
- Emit event from model service or status service
Pattern 5: Notifications
- Create notification service injecting audit model service
- Create email templates (Handlebars)
- Create audit record BEFORE dispatching email
- Handle email failure gracefully (log, don't throw)
Pattern 6: File Upload
- Create upload controller with Multer interceptor
- Create upload service with file validation
- Configure allowed MIME types and max file size
- Update entity with file path via model service
- Add role-based ACL to upload endpoint
Pattern 7: External API
- Install
@nestjs/axiosif not present - Create
{provider}-http.service.tswith timeout and error handling - Config values from
ConfigService(never hardcoded) - Return null on API failure (graceful degradation)
Pattern 8: Dedup/Sync
- Add
syncFromApi()to model service - Case-insensitive local lookup
- Update provenance fields if not already set
- Create new entity if not found locally
Pattern 9: Transactions
- Inject
DataSourcein workflow service - Wrap multi-entity operations in
dataSource.transaction() - Model services still handle business validation inside transaction
Pattern 10: Views
- Create
@ViewEntitywith SQL expression - Register in
TypeOrmModule.forFeature()andormconfig.ts - Read-only controller or model service method
- No create/update/delete on view entities
Validation Gate
After implementing business logic patterns:
yarn build— zero TypeScript errors- Behavioral rule coverage report — every B-rule and ST-rule from SBVR maps to implemented code
- Event listeners registered — check module providers
- Status transitions enforced — no direct status updates outside status service
References
development-guides/BUSINESS_LOGIC_PATTERNS_GUIDE.md— canonical pattern templatesdevelopment-guides/SBVR_EXTRACTION_GUIDE.md— rule classification and extractiondevelopment-guides/CRUD_PATTERNS_GUIDE.md— CRUD foundation (generate first)development-guides/SDK_SERVICES_GUIDE.md— model service rules