# Pattern 12-ter — Within-module N-satellite CRUD scaffold cookie-cutter

> **Discovered:** S34 Plan 3 Item 3 (2026-05-27) — Implementer Case 2 spawn cho 5 satellite CRUD scaffold cùng parent EmployeeProfile.
> **Distinguishes from Pattern 12-bis:** Pattern 12-bis = CROSS-module mirror (PE → Contract). Pattern 12-ter = WITHIN-module N-satellite cùng parent.

## Pattern

Khi parent entity có N satellite (1-N FK Cascade) và spec yêu cầu CRUD endpoint cho tất cả satellite:

- **1 file features mega-scaffold** với N region (`#region {EntityName}`) — KHÔNG split N file riêng (clutter folder)
- **Mỗi region 3 record + 3 handler:** Create/Update/Delete cookie-cutter ~120 LOC/region
- **Parent verify pattern** consistent: `AnyAsync(x => x.Id == request.EmployeeProfileId && !x.IsDeleted, ct)` → NotFoundException nếu parent missing
- **Soft delete pattern** consistent: `IsDeleted=true + DeletedAt=UtcNow + DeletedBy=currentUser.UserId`
- **Controller extend** thay vì new controller — sub-routes `/api/parent/{id}/{satellite-path}/{satId}` mirror RESTful parent-child convention
- **Per-action policy override** class-level Read default → Create/Update/Delete per endpoint

## Decision

**Khi nào dùng Pattern 12-ter (vs split):**
- N ≤ 7 satellite cùng parent → 1 mega file `{Parent}SatelliteFeatures.cs` (S34 case: 5 satellite, ~621 LOC OK)
- N > 7 satellite OR mỗi satellite có business logic khác biệt (vd workflow, codeGen riêng) → split file per satellite

## Example S34

`Application/Hrm/EmployeeSatelliteFeatures.cs` 621 LOC = 5 region × 3 verb:
- WorkHistory
- Education
- FamilyRelation
- Skill (polymorphic Kind discriminator — treat field bình thường, không branch handler)
- Document (metadata-only — file upload IFileStorage defer riêng)

`EmployeesController.cs` 70 → 234 LOC: 5 region × 3 endpoint = 15 satellite endpoint extend parent controller.

## Reusable cross-project

- VIPIX: Tags + Categories + Collections satellite cho Photo entity
- NAMGROUP: QtCongTac + QtDaoTao + QuanHeThanNhan satellite cho NhanVien (đã port S33+S34 vào SOL)
- ASHICO: Multi-site relationship satellite
- DH_Y_DUOC: Sáng kiến comments + reviews + attachments

## Pattern relationship

| Pattern | Scope | Token cost | Notes |
|---|---|---|---|
| 12-bis | Cross-module mirror (PE → Contract) | ~25K per Implementer spawn | Mirror entity + Mig + Config + DbSet |
| **12-ter** | Within-module N-satellite scaffold | ~22K per spawn (5× cookie-cutter) | 1 mega file + controller extend |
| 16-bis | Cross-app FE 4-place mirror | ~15K per Implementer spawn | types + page + menuKeys + Layout staticMap |

## Anti-pattern

❌ KHÔNG split N file `{Parent}{Satellite}Features.cs` × N — folder clutter + duplicated using statements + harder cross-satellite refactor
❌ KHÔNG inline satellite vào parent Features.cs — file > 1000 LOC khó maintain
❌ KHÔNG inline satellite endpoint vào parent controller without region marker — flat list 20+ endpoint khó scan

## Verify checklist

- [ ] 1 file features có N region marker (`#region {EntityName}` + `#endregion`)
- [ ] Mỗi region đầy đủ Create/Update/Delete + Validator + Handler
- [ ] Parent verify pattern consistent N region
- [ ] Soft delete pattern consistent (IsDeleted/DeletedAt/DeletedBy)
- [ ] Controller extend dùng region marker
- [ ] Per-action policy override per verb
- [ ] dotnet build clean 0 err
- [ ] dotnet test baseline preserve (no regression — satellite scaffold không đụng parent)
