Master-Detail Source Reading Map
This page is a practical source-reading companion for one narrow backend question:
if my question is no longer “how do I use the master-detail workflow?” but “which files prove the current master-detail, nested-detail, and standalone-capable detail behavior?”, where should I read first, and in what order?
Use this page together with:
TIP
Master-detail reading path
- Master-Detail Workflow — understand the public workflow and DTO rules
- Master-Detail Source Reading Map — trace the generator and specimen evidence
You are here: step 2.
Why this page exists
The current public docs already explain the basic master-detail workflow.
What was still missing was one file-order map that proves the current implementation across:
- generator behavior
- first-level master-detail specimen
- second-level nested-detail specimen
- the boundary between aggregate-owned detail DTOs and standalone child-resource surfaces
This page fills that gap.
It is a source-order map for the current implementation, not another broad CRUD or relation concept page.
How to use this page
Use this rule of thumb:
- start with the workflow page first
- read the generator entrypoints to understand what the scaffold promises
- read the specimen files in parent-to-child order
- stop once the behavior is clear instead of rereading the whole suite
- hand off to fullstack contract-loop docs only when the question becomes frontend generation or consumer drift
The shortest accurate mental model
A practical master-detail implementation path looks like this:
:tools:masterDetailreceives the master/detail module and resource arguments- the generator normalizes detail DTO names, relation names, FK names, and detail mode
- the generator ensures the detail module/resource shape exists
- it patches the detail entity/meta side
- it renders nested detail DTOs into the parent DTO folder
- it patches the master model/service/DTO/locales
- it refreshes metadata for both the detail module and the master module
- the specimen modules then demonstrate how the same pattern can repeat recursively for nested-detail
That means the source-reading branch is not only “follow one CRUD module.”
It starts from the generator, then confirms the shape in real specimen modules.
Generator entrypoints
Start here:
vona/packages-cli/cli-set-api/src/lib/command/tools.masterDetail.tsvona/packages-cli/cli-set-api/src/lib/bean/cli.tools.masterDetail.ts
Read the command definition first when the question is:
- what CLI shape is officially supported?
- what arguments are part of the public workflow?
Read cli.tools.masterDetail.ts first when the question is:
- how are
detailMode, relation names, FK names, and detail DTO names normalized? - what exactly gets scaffolded or patched?
- how does the generator distinguish aggregate-only detail from standalone-capable detail?
What cli.tools.masterDetail.ts actually patches
The main execution path is:
_prepareArgv_ensureDetailModule_ensureDetailResourceShape_patchDetailModule_renderMasterDetailDtos_patchMasterModule_refreshMetadata
A practical reading of the responsibilities is:
_prepareArgv- defaults and validates
detailMode - derives
relationName,fk, and the nested detail DTO names - confirms the
detail*naming convention comes from the generator itself
- defaults and validates
_ensureDetailResourceShape- ensures the detail resource core exists
- branches into aggregate vs standalone handling
_handleAggregateDetailMode- refuses to silently collapse an existing standalone detail surface into aggregate-only mode
- removes generated standalone controller/service/DTO files when aggregate-only shape is intended
_handleStandaloneDetailMode- requires the standalone detail surface to remain present
_patchDetailModule- adds the FK field and schema/index support on the detail side
_renderMasterDetailDtos- emits
detail*DTO files into the master modulesrc/dtofolder
- emits
_patchMasterDto- patches
Create/Update/ViewDTOs so the parent DTOs consume the sibling nested detail DTOs - emits the current
include: { relationName: { dtoClass: DtoDetail... } }wiring pattern instead of inlining long relation column lists in every parent DTO
- patches
_refreshMetadata- regenerates
.metadata/index.tsfor both modules so the nested detail DTOs appear in generated contract surfaces
- regenerates
First-level specimen: training-student -> training-record
Use this order:
vona/src/suite/a-training/modules/training-student/src/model/student.tsvona/src/suite/a-training/modules/training-student/src/service/student.tsvona/src/suite/a-training/modules/training-student/src/dto/studentCreate.tsxvona/src/suite/a-training/modules/training-student/src/dto/studentUpdate.tsxvona/src/suite/a-training/modules/training-student/src/dto/studentView.tsxvona/src/suite/a-training/modules/training-student/src/dto/detailRecordBase.tsxvona/src/suite/a-training/modules/training-student/src/dto/detailRecordMutate.tsxvona/src/suite/a-training/modules/training-student/src/dto/detailRecordResItem.tsxvona/src/suite/a-training/modules/training-student/src/dto/detailRecordView.tsxvona/src/suite/a-training/modules/training-student/src/.metadata/index.tsvona/src/suite/a-training/modules/training-record/src/entity/record.tsx
What this sequence clarifies:
student.tsshows thehasMany('training-record:record', 'studentId', ...)ownership relationservice/student.tsshows the nested include lifecycle for create/view/update/deletestudentCreate.tsx,studentUpdate.tsx, andstudentView.tsxshow the parent DTOs consuming sibling nested detail DTOs throughdtoClass-basedincludewiringdetailRecordBase.tsxshows the reusable detail field surface that the mutate/view/res-item DTO family can build ondetailRecordMutate.tsxanddetailRecordView.tsxshow that first-level nested detail DTOs can also repeat the samedtoClasspattern for deeper nested relationsdetailRecord*.tsxshows thedetail*naming convention for first-level nested detail DTOs.metadata/index.tsconfirms those nested detail DTOs are part of the generated contract registryentity/record.tsxshows the detail-side FK persistence field that closes the aggregate thread
Second-level specimen: training-record -> training-recordsubject
Use this order:
vona/src/suite/a-training/modules/training-record/src/model/record.tsvona/src/suite/a-training/modules/training-record/src/service/record.tsvona/src/suite/a-training/modules/training-record/src/dto/recordCreate.tsxvona/src/suite/a-training/modules/training-record/src/dto/recordUpdate.tsxvona/src/suite/a-training/modules/training-record/src/dto/recordView.tsxvona/src/suite/a-training/modules/training-record/src/dto/detailRecordSubjectBase.tsxvona/src/suite/a-training/modules/training-record/src/dto/detailRecordSubjectMutate.tsxvona/src/suite/a-training/modules/training-record/src/dto/detailRecordSubjectResItem.tsxvona/src/suite/a-training/modules/training-record/src/dto/detailRecordSubjectView.tsxvona/src/suite/a-training/modules/training-record/src/.metadata/index.ts
What this sequence clarifies:
record.tsshows thatrecordis itself a parent throughtrainingRecordSubjects: $relation.hasMany(...)service/record.tsshows the include lifecycle for the second-level nested detail collectionrecordCreate.tsx,recordUpdate.tsx, andrecordView.tsxshow the immediate parent consuming siblingdetailRecordSubject*DTOs through the samedtoClass-based include patterndetailRecordSubject*.tsxproves the recursivedetail*naming rule.metadata/index.tsconfirms second-level nested detail DTOs are also exported into generated contract surfaces
The key point is that this is not a separate mechanism.
It is the same master-detail pattern repeated one level lower.
Standalone child surface vs nested detail surface
This is the boundary most worth reading carefully.
The training-record module proves that a detail can keep its own standalone resource surface while still participating as a nested detail under student.
Read these files for the standalone-capable detail surface:
vona/src/suite/a-training/modules/training-record/src/service/record.tsvona/src/suite/a-training/modules/training-record/src/dto/recordCreate.tsxvona/src/suite/a-training/modules/training-record/src/dto/recordUpdate.tsxvona/src/suite/a-training/modules/training-record/src/dto/recordView.tsx
Then compare that with the nested detail DTOs that live in the parent module:
vona/src/suite/a-training/modules/training-student/src/dto/detailRecordBase.tsxvona/src/suite/a-training/modules/training-student/src/dto/detailRecordMutate.tsxvona/src/suite/a-training/modules/training-student/src/dto/detailRecordResItem.tsxvona/src/suite/a-training/modules/training-student/src/dto/detailRecordView.tsx
That comparison shows the intended split:
- nested detail DTOs use
detail*names in the parent module because they describe the child in its parent-owned detail role - standalone child CRUD uses ordinary resource DTO names in the child module because it describes the child as its own resource surface
For training-recordsubject, the current source tree also helps prove the aggregate-only boundary.
Observed source state:
vona/src/suite/a-training/modules/training-recordsubject/src/currently contains entity/model/meta files- it does not currently contain
src/controller/,src/service/, orsrc/dto/ - the compiled
dist/tree still contains prior standalone declarations such asdist/dto/subjectCreate.d.ts
A practical interpretation is:
- source-level authoring for
training-recordsubjectis currently aggregate-oriented - nested second-level detail authoring lives in the parent
training-recordmodule throughdetailRecordSubject* - compiled artifacts alone should not be treated as the canonical source of truth when the source tree shows the current aggregate-only shape
Frontend/runtime touchpoints
If your next question becomes “how does this nested detail DTO shape connect to actual detail UI/runtime behavior?”, read these areas next:
zova/src/suite/cabloy-basic/modules/basic-details/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/detail/
Use them to confirm:
- nested detail fields are rendered through
basic-details - detail lists/rows/actions have a concrete runtime contract
- the backend DTO and metadata shape feeds a real frontend detail workflow rather than remaining only a backend convention
Suggested reading order for future source analysis
Use this order when you want the shortest reliable path:
vona/packages-cli/cli-set-api/src/lib/command/tools.masterDetail.tsvona/packages-cli/cli-set-api/src/lib/bean/cli.tools.masterDetail.tsvona/src/suite/a-training/modules/training-student/src/model/student.tsvona/src/suite/a-training/modules/training-student/src/service/student.tsvona/src/suite/a-training/modules/training-student/src/dto/studentCreate.tsxvona/src/suite/a-training/modules/training-student/src/dto/studentUpdate.tsxvona/src/suite/a-training/modules/training-student/src/dto/studentView.tsxvona/src/suite/a-training/modules/training-record/src/model/record.tsvona/src/suite/a-training/modules/training-record/src/service/record.tsvona/src/suite/a-training/modules/training-record/src/dto/recordCreate.tsxvona/src/suite/a-training/modules/training-record/src/dto/recordUpdate.tsxvona/src/suite/a-training/modules/training-record/src/dto/recordView.tsx- both module
.metadata/index.tsfiles - frontend
basic-detailsanda-openapidetail runtime types if the question crosses into UI/runtime behavior
Where to read next
- If you need the public workflow and naming/placement rules, return to Master-Detail Workflow.
- If you need broader ORM relation semantics, continue with Relations Guide.
- If you want one concrete backend module wiring page before returning to detail-specific reading, continue with Backend Resource/Module Contract Chain.
- If the next question becomes frontend consumer generation or backend/frontend drift, continue with Contract Loop Playbook.
Final takeaway
The cleanest way to read the current master-detail implementation is not to stay only at the workflow level and not to read every file in the suite.
Start from the CLI entrypoint, confirm what the generator promises, trace the first-level specimen, then trace the recursive second-level specimen.
That path is enough to prove the three current capabilities:
- master-detail
- nested-detail
- a detail resource that can remain aggregate-owned only or also keep its own standalone resource surface