Skip to content
bitzorcas
中EN

Concept

Menu Catalog and Navigation

Source-verified handbook for the global menu catalog, nine HTTP use cases, authorization visibility, tree projections, cache synchronization, seeding, and current product boundaries.

Last updated

Menu is the platform’s global navigation-catalog owner. It manages the structure and display fields in SysModule and projects flat lists, trees, and client navigation according to a user’s role grants. It does not decide whether a target API can be called, and it is not a feature manager or client router.

1. Current product surface

220-sys_module.csv

global SysModule catalog

MenuTreeBuilder

current user roles

MenuVisibilityResolver

Authorization narrow reader

flat / tree / navigation

menu write use cases

local tag removal + cross-instance notice

There are three projects. Contracts contains four output DTOs; Application owns nine use cases and tree policy; Infrastructure owns the global row, IEntitySet store, seed step, and cache-sync consumer.

2. Nine HTTP use cases

Method and pathUse caseResource permission
POST /api/menusCreateMenucreate
PUT /api/menus/{id}UpdateMenuupdate
DELETE /api/menus/{id}DeleteMenudelete
PATCH /api/menus/{id}/sortSortMenuupdate
PATCH /api/menus/{id}/toggleToggleMenuupdate
GET /api/menus/{id}GetMenuDetailview
GET /api/menus/flatGetMenuFlatview
GET /api/menus/treeGetMenuTreeview
GET /api/menus/navigationGetNavigationauthentication only; no IAuthorizedRequest

GenerateEndpointAttribute.RequireAuthorization defaults to true, so navigation still requires a signed-in caller. It skips the menus.menu.view resource decision, not authentication.

3. Permissions and governance

The marker is AppModule("Menu", Code="menus"), with allowed dependencies on Authorization and Identity. Its current permission catalog has four values:

menus.menu.view
menus.menu.create
menus.menu.update
menus.menu.delete

Menu has no feature catalog. The old page’s “feature-aware visibility” claim was unsupported; current visibility is decided only by the admin role shortcut or module codes returned by Authorization.

4. Request-to-navigation path

MenuStoreCacheMenuTreeBuilderAuthorization readerVisibilityResolverGetNavigation.HandlerGenerated endpointClientMenuStoreCacheMenuTreeBuilderAuthorization readerVisibilityResolverGetNavigation.HandlerGenerated endpointClientGET /api/menus/navigation + bearer tokenauthenticated QueryGetNavigationAsync(currentUser)ResolveVisibleModuleCodesAsync()granted module codes for rolesget-or-create user-scoped entryload enabled global modules on missnested NavigationItem[]

Role name admin is matched case-insensitively. Its presence makes the resolver return null, which means all enabled catalog rows. A non-admin with no role gets an empty navigation result.

5. Correct consumption

Read client navigation
// ① The generated endpoint authenticates this query but applies no menu-admin permission.
var result = await mediator.Send(
new GetNavigation.Query(),
cancellationToken);
// ② The result is role-filtered and includes ancestors of visible nodes.
if (result.IsFailure)
return result.Error;
// ③ Use it only for navigation; every target endpoint enforces its own policy.
return result.Value.Select(RenderNavigationNode).ToList();

The navigation DTO exposes only Id, Name, Icon, LinkUrl, and Children. Code, Scope, Enabled, and authorization data are not returned.

6. Global, not tenant-scoped

MenuModuleCatalogRecord inherits EntityBase; its table metadata has no IsTenant=true. Structure is shared by all tenants, while tenant differences come from tenant-owned role-to-module relations in Authorization.

Changing a name, link, parent, order, or enabled flag therefore affects every tenant. Tenant-custom menus require an explicit override model and merge policy; the current row must not be treated as tenant data.

7. Seed baseline

MenuModuleSeedStep runs at order 220 and uses Code as its natural key. The asset currently ships nine root nodes: Operations, PlatformBilling, Files, Notifications, Webhooks, Catalog, Tickets, Chat, and Sandbox.

The step updates parent, display data, legacy MVC fields, order, description, flags, Scope, and soft deletion, but it does not replace the matching Code.

8. Current guarantees

  • Code has a database unique index.
  • ParentId has a normal index.
  • The store uses IEntitySet<MenuModuleCatalogRecord> and stays ORM-neutral.
  • Authorization relations are read only through IAuthorizationAssignmentReader.
  • Queries load only enabled, non-deleted catalog rows.
  • Flat, tree, and navigation projections are cached by user and visibility set.
  • Successful writes remove the local menu tag and publish a sync notice.
  • The API shell supplies a fail-closed unavailable store without database configuration.

9. Current non-guarantees

  • Create/update do not validate parent existence, self-parenting, or ancestor cycles.
  • Update does not reject blank Name/Code or proactively detect duplicate Code.
  • Delete is neither transactional nor protected from referenced authorization data.
  • Sort writes one integer and does not rebalance siblings.
  • Toggling a parent does not update descendant state.
  • Tree recursion has no cycle or depth guard.
  • There is no Menu feature, audit timeline, version history, or tenant override.
  • Database writes and cache events have no outbox atomicity.

10. When to use Menu

Use it for stable product navigation, module entry points, and legacy controller/action compatibility metadata. A page-local tab, button, or temporary onboarding hint should not become a global SysModule row.

Before adding a node, define its stable Code, parent, target URL, Scope, menu flag, default enabled state, and the roles that receive module visibility.

11. Handbook map

12. Source inspection

Terminal window
# Inspect all nine command/query endpoint declarations.
find src/Platform/Menu -path '*Commands*' -o -path '*Queries*' | sort
# Verify global metadata, seeding, and the Authorization narrow port.
rg -n "BitzTable|WhereColumns|IAuthorizationAssignmentReader" src/Platform/Menu -g '*.cs'
# A Menu feature catalog should not be present.
rg -n "FeatureCatalog|FeatureDefinition" src/Platform/Menu -g '*.cs'

Back to platform modules · Authorization module

100%

Scroll or use controls to zoom · drag when enlarged · double-click for 100% / 200%