Skip to content
bitzorcas
中EN

Concept

DocumentStructure templates, favorites, and recycle-bin coordination

A source-verified overview of folder templates, node graphs, historical snapshots, template application, user favorites, the Documents recycle-bin port, and current commercial boundaries.

Last updated

DocumentStructure is not the generic document tree module. It owns three focused capabilities: reusable folder templates and node snapshots, favorite relations for the current user, and an application port that coordinates soft-deleted Documents objects. DocumentCategory, document content, knowledge bases, and the recycle-bin store implementation remain owned by Documents.

1. What exists today

Implemented behavior includes:

  • tenant-unique folder-template names;
  • current template nodes with complete temporary-ID to persistent-ID remapping;
  • nonblank and request-unique node IDs, existing parents, cycle detection, and a maximum depth of ten;
  • an AOT-safe JSON snapshot of the replaced node set on update;
  • list, detail, create, update, delete, preview, and apply use cases;
  • Scriban compilation and rendering for dynamic node names;
  • add, list, and owner-delete operations for current-user favorites;
  • recycle-bin listing, restore, and purge dispatch for Document and Category;
  • store predicates that combine the supplied tenant with ICurrentTenant.EffectiveTenantId;
  • ORM-neutral persistence metadata and a shared test foundation for SqlSugar and EF Core.

The current module does not provide template-history APIs, version diff or rollback, publication approval, ETags, apply idempotency, atomic category-tree creation, failure compensation, a target-type allowlist, knowledge-base instance authorization, Scriban diagnostics, favorite target resolution, favorite permissions, purge retention/legal-hold policy, or complete HTTP security tests.

2. Runtime shape

Authenticated client

13 HTTP routes
11 generated + 2 handwritten

IAuthorizedRequest
resource inferred from request type

Template use cases
graph · snapshot · preview · apply

Favorite use cases
user relations

Recycle-bin use cases
query · restore · purge

DocumentStructureStore
4 owner-local tables

IRepository
Documents Application

DocumentRecycleBinStore
Documents Infrastructure

DocumentStructure Application currently references Documents Application and Contracts directly, and Apply depends on IRepository<DocumentCategory>. In the opposite direction, DocumentStructure Application owns IDocumentRecycleBinStore, while Documents Infrastructure supplies the adapter. The recycle direction is a narrow owner adapter; the apply direction exposes a wider cross-module dependency.

3. The thirteen HTTP routes

Method and routePurposeRegistration
POST /api/v1/document-structures/templatesCreate a templategenerated
GET /api/v1/document-structures/templatesList and filter templatesgenerated
GET /api/v1/document-structures/templates/{id}Template detailgenerated
PUT /api/v1/document-structures/templates/{id}Update and snapshotgenerated
DELETE /api/v1/document-structures/templates/{id}Soft-delete root and remove current nodesgenerated
POST /api/v1/document-structures/templates/{id}/previewPreview dynamic nameshandwritten
POST /api/v1/document-structures/templates/{id}/applyCreate a category treehandwritten
POST /api/v1/document-structures/favoritesAdd a favoritegenerated
GET /api/v1/document-structures/favoritesList current-user favoritesgenerated
DELETE /api/v1/document-structures/favorites/{id}Remove the caller’s favoritegenerated
GET /api/v1/document-structures/recycle-binPage through recycle-bin itemsgenerated
POST /api/v1/document-structures/recycle-bin/{id}/restoreRestore Document/Categorygenerated
DELETE /api/v1/document-structures/recycle-bin/{id}Permanently purge an itemgenerated

The handwritten route group applies authentication, userPolicy rate limiting, and the standard timeout. Generated requests implement IAuthorizedRequest but do not override Resource; they rely on AuthorizationResourceConventions.FromRequestType. The permission catalog contains template and recycle-bin permissions, but no favorite permission constants. Real HTTP policy tests must prove the resulting resource/action mapping.

4. A real create-template path

Create a two-level folder template
var result = await mediator.Send(new CreateFolderTemplate.Command(
Name: "Case standard layout",
Description: "Create material and delivery folders for each case",
TargetType: "KnowledgeBase",
Nodes:
[
// Id is temporary within this request; the store allocates the persistent key.
new("root-material", null, "Materials", null, "folder", "#2563eb", null, 10),
new("child-contract", "root-material", "Contracts", null, "file", "#475569", null, 20)
]), cancellationToken);
if (result.IsFailure)
return result.Error;
// Response IDs, including ParentNodeId, have been remapped to persistent IDs.
return result.Value!;

The handler validates the name and tenant-local duplication, validates the whole graph, then delegates root and node writes to the store. The database also has a unique (TenantId, Name) constraint. Concurrent requests can still both pass the pre-check; stable conflict mapping for the final unique violation is not demonstrated.

5. Actual apply sequence

"IRepository<DocumentCategory>""Scriban compiler""DocumentStructureStore""ApplyTemplate.Handler""IRepository<DocumentCategory>""Scriban compiler""DocumentStructureStore""ApplyTemplate.Handler"loop["roots, then resolvable children"]"User"TemplateId + TargetId + NamingContexttenant-scoped template + nodescompile/render or fall back to NameSaveAsync one categoryCategoryId or failureCreatedCategoryIds"User"

TargetType is neither compared with the template target nor used to dispatch to different owners. Every request treats TargetId as a KnowledgeBaseId and creates DocumentCategory records. The handler does not explicitly prove that the knowledge base exists or that the caller may manage it.

6. A favorite is not authorization

The favorite natural key is TenantId, UserId, TargetType, and TargetId. Add only checks that type and ID are nonblank, and writes user "0" if identity is absent. List filters relations by current tenant/user/group but neither resolves the target nor removes deleted or newly forbidden resources.

FavoriteItem is therefore a weak reference, not an accessible resource and not target detail. A consumer must resolve it through the owner’s authorized query. A stronger GA contract performs batched owner resolution on the server and marks or removes dangling references without leaking forbidden metadata.

7. Recycle bin as a cross-module coordination surface

IDocumentRecycleBinStore is declared in DocumentStructure Application and implemented by Documents Infrastructure. Handlers accept only exact Document or Category item types and dispatch accordingly. Query forwards KnowledgeBaseId, PageIndex, and PageSize to the store without normalization.

A success result does not prove that the object existed and carries no affected count. Recursive category restore/purge behavior, parent conflicts, attachments, search indexes, versions, favorites, and emitted events must be fixed by Documents-store integration tests rather than inferred from the port comment.

8. Reading path

9. Commercial GA red lines

  1. Apply validates tenant, target type, knowledge-base existence, and instance-level management permission before writing.
  2. The complete tree is created atomically, or compensation and recovery are proven.
  3. Unresolved nodes fail explicitly; partial IDs never masquerade as full success.
  4. An ApplicationId makes retries and concurrent replay idempotent.
  5. Scriban is genuinely asynchronous and cancellable, constrained by policy, and returns visible diagnostics.
  6. Snapshot schema/version supports reading, diff, rollback, and migration.
  7. Template updates use an ETag/version condition and map unique conflicts to typed errors.
  8. Favorites require a real user ID, target allowlist, owner existence, and authorization.
  9. Recycle-bin operations bind both knowledge-base and object authorization; purge enforces retention and legal hold.
  10. HTTP tests lock request-resource conventions and explicit favorite permissions.
  11. Template listing removes the per-template count N+1 and establishes large-tenant/tree baselines.
  12. Both ORM adapters pass transaction, concurrency, fault, recovery, and operational gates.

10. Source navigation

Terminal window
# Confirm eleven generated routes and two handwritten routes.
rg -n "GenerateEndpoint\(|MapPost\(" \
src/Platform/DocumentStructure src/Hosts/BitzOrcas.Api/Endpoints/DocumentStructureEndpoints.cs -g '*.cs'
# Expose the direct Documents dependency, sync-over-async, and silent fallback.
rg -n "IRepository<DocumentCategory>|GetAwaiter\(\)\.GetResult|CancellationToken.None|return node.Name" \
src/Platform/DocumentStructure -g '*.cs'
# These GA contracts are currently expected to have no matches.
rg -n "ApplicationId|RollbackTemplate|FavoriteTargetResolver|RowVersion" \
src/Platform/DocumentStructure -g '*.cs'

Back to the module catalog · Documents module · Authorization module

100%

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