Master Data is not currently an MDM suite with approvals, versions, quality rules, and an administration UI. It physically owns nine tenant-scoped catalog records, eight CSV seed steps, the shared IDataDictionaryResolver implementation, and a read-only governance query endpoint. Infrastructure is still the data owner, but Contracts and Application projects now exist to expose the governance report.
1. Current product surface
SysTranslationCatalogRecord is the ninth table but has no MasterData CSV/step; I18n provides its translation workflows.
Alongside Infrastructure, Contracts and Application projects now exist to expose the read-only governance report (see §12).
2. Physical structure
src/Platform/MasterData/└── BitzOrcas.Platform.MasterData.Infrastructure/ ├── Persistence/ # 9 CatalogRecords ├── Seeders/ # 8 ORM-neutral seed steps │ └── Assets/ # 8 caret-delimited CSVs ├── Dictionary/ ├── MasterDataDependencyInjection.cs ├── MasterDataFeatures.cs ├── MasterDataPermissions.cs └── MasterDataModule.csThe project references Application, Infrastructure, Persistence.Models, and the compile-time metadata generator, but no SqlSugar or EF Core adapter assembly.
3. Nine catalogs
| Record | Table | Current purpose |
|---|---|---|
SysLanguageCatalogRecord | SysLanguage | tenant language catalog read by I18n |
SysCountryCatalogRecord | SysCountry | ISO country/region fields |
SysIndustrySettingCatalogRecord | SysIndustrySetting | industry tree |
SysGeneralCodeGroupCatalogRecord | SysGeneralCodeGroup | dictionary group hierarchy |
SysGeneralCodeCatalogRecord | SysGeneralCode | code, group, hierarchy, display |
SysGeneralCodeTextCatalogRecord | SysGeneralCodeText | localized dictionary display |
SysExchangeRateCatalogRecord | SysExchangeRate | dated currency-pair snapshot |
SysPublicHolidayCatalogRecord | SysPublicHoliday | country/year holiday |
SysTranslationCatalogRecord | SysTranslation | I18n scoped translation |
All inherit BizEntityBase and declare tenant and soft-delete metadata.
4. Ownership transfer
Older code placed these rows and SqlSugar-specific seeders in Framework. Architecture gates keep those paths deleted and require owner-local models. This removes business reference data from Framework and runs the same step through IEntitySet<T> on supported ORMs.
MasterData is an explicit multi-table reference-catalog exception to the unified aggregate rule. These records are not an invitation to restore one-to-one *Entity + mapper files.
5. Host composition
The API references this project and currently composes it as follows:
// ① Generated ORM adapters provide all nine IEntitySet<T> services.services.AddBitzOrcasGeneratedPersistenceAdapters(persistenceProvider);
// ② MasterData registers only the dictionary resolver.services.AddBitzOrcasMasterDataPlatform();
// ③ Seed framework and generated manifest discover the eight steps.services.AddBitzOrcasSeeders(configuration);services.AddBitzOrcasGeneratedSeedSteps();JobHost also references the project. A reference does not prove seed execution; orchestration configuration and host lifecycle still decide that.
6. Real dictionary API
| Method | Behavior |
|---|---|
ResolveAsync(group, code, culture) | display name or original code |
ResolveListAsync(group, csvCodes, culture) | ordered comma-list resolution |
ResolveBatchAsync(requests, culture) | grouped request-to-text map |
ListEntriesAsync(group, culture) | active non-deleted entries by Sort |
InvalidateCacheAsync(group?) | removes the whole dictionary tag |
The previous page’s ReferenceKey, nullable ReferenceValue, and stable-key overload do not exist.
7. Correct consumption
// ① Business rows persist the stable code, never mutable display text.order.PaymentMethodCode = request.PaymentMethodCode;await orders.UpdateAsync(order, cancellationToken);
// ② Use the actual groupKey + code + culture signature.var displayName = await dictionaries.ResolveAsync( groupKey: "PAYMENTMETHODCODE", code: order.PaymentMethodCode, culture: currentCulture.Name, cancellationToken);
// ③ A miss returns the original code, so expose both machine and display forms.return new PaymentMethodDto(order.PaymentMethodCode, displayName);The contract cannot distinguish a missing translation from a display value equal to the code, and returns no effective culture.
8. Seed asset reality
| Asset | Current data rows |
|---|---|
| language | 2 |
| country | 0 (header only) |
| industry | 0 (header only) |
| general-code-group | 177 |
| general-code | 43,603 |
| general-code-text | 0 (header only) |
| exchange-rate | 0 (header only) |
| public-holiday | 347 |
Owning a seed step does not mean the product ships data for every catalog. See seeding and assets.
9. Guarantees and non-guarantees
| Current guarantee | Do not assume |
|---|---|
| compile-time metadata for nine records | master-data management API/UI |
| ORM-neutral seed steps | strict CSV schema validation |
| tenant-scoped dictionary cache keys | group invalidation is tenant-local |
| case-insensitive code map | normalized culture fallback chain |
| missing code returns original value | caller can detect the miss reason |
| API host registers the resolver | feature/permission endpoint enforcement |
10. Confirmed risks
- Localized seed matching and lookup use Code+Language but omit Class/Group.
- Localized maps use Code as a dictionary key; duplicates can throw.
- Any group invalidation removes the entire
dictionarytag. ListEntriesAsyncbypasses the 30-minute group cache.- CSV header/missing-field validation is disabled; key defects only warn.
- Exchange-rate CSV says
UpdateDate; the model/where key retainsModifyTimee. - Four assets contain only a header, yet a seed run can succeed with zero rows.
- 43,603 codes are queried/written one row at a time and need cold-start testing.
11. Handbook map
- Records, metadata, and ORM
- Seeding and assets
- Dictionary, cache, and localization
- Testing, operations, and GA
12. Governance query endpoint
GET /api/master-data/governance (resource master-data/catalog, Action Read) is an owner-local read-only governance report that lets operators see the scale of each reference catalog and the dictionary contents. Query parameters: GroupCode (optional, selects a dictionary group), Culture (optional, BCP-47), Search (optional), EntryLimit (default 100, max 200).
Returns MasterDataGovernanceReport:
Catalogs: count summaries for nine catalogs (languages,countries,industries,dictionaryGroups,dictionaryEntries,dictionaryTexts,exchangeRates,publicHolidays,translations), each with Total/Active/SeedManaged;DictionaryGroupsandEntries: when aGroupCodeis selected, returns paged dictionary entries for that group, including localized DisplayName;SelectedGroup,SelectedGroupTotal,Culture,EntryLimit,GeneratedAt.
The read store MasterDataGovernanceReadStore implements IMasterDataGovernanceReadStore via IEntitySet<T> projections; it degrades to the fail-closed UnavailableMasterDataGovernanceReadStore when not registered. Error codes: MasterData.Governance.StoreUnavailable (ServiceUnavailable), MasterData.Governance.InvalidQuery (Validation).
This endpoint is the same kind of read-only projection as the Operations adapter/connector report: it answers how much and what, not whether it is correct or current.
13. Source inspection
# Expect nine records, eight seed steps, and eight assets.find src/Platform/MasterData -type f | sort
# Verify actual default-host composition.rg -n "AddBitzOrcasMasterDataPlatform|AddBitzOrcasGeneratedSeedSteps" src/Hosts -g '*.cs'
# Legacy ownership paths should remain absent.find src/Framework src/Platform/MasterData -path '*MasterData*Entity.cs' -o -path '*SqlSugar*MasterData*'