FForm Platform
enzh-CN

Developer guide

A practical path from a first editable form to advanced server and client extensions.

FormPlatform Beginner, Intermediate, and Advanced Developer Guide

Before creating or changing a form control, also read Client Component Contracts.

中文:DEVELOPER_GUIDE.zh-CN.md

The levels describe capability, not job title. A beginner can deliver a feature with the Designer and existing Data Models. An intermediate developer extends services and controls. An advanced developer maintains platform boundaries, performance, security, migrations, and release compatibility.

1. Foundation for every developer

Read System Architecture first. Form definition, form metadata, management data, and business records are different domains. Client visibility is not authorization. Fixed DDL is not initializer work. Third-party modules never reference the Host assembly.

Recommended tools are .NET 10 SDK, Node.js 22, PostgreSQL 17 (MySQL and SQL Server are supported), Git, and browser developer tools. Docker is required for PostgreSQL Testcontainers tests.

Use User Secrets for local credentials:

dotnet user-secrets --project src/FormPlatform.Host/FormPlatform.csproj set "ManagementDatabase:ConnectionString" "Host=localhost;Database=formplatform;Username=postgres;Password=..."
dotnet user-secrets --project src/FormPlatform.Host/FormPlatform.csproj set "FormStorage:ConnectionString" "Host=localhost;Database=formplatform;Username=postgres;Password=..."

Never commit real passwords to `appsettings.json`.

2. Beginner: deliver business features with forms

2.1 Outcomes

You should be able to start both applications, understand when a rebuild is needed, create Data Models and mappings, use the major controls, configure validation/conditions/events/substitutions, diagnose browser/server errors, and deliver CRUD without Host code changes.

2.2 Running locally

# terminal 1
dotnet run --project src/FormPlatform.Host/FormPlatform.csproj --urls http://localhost:5080

# terminal 2
cd src/FormPlatform.Host/ClientApp
npm run dev

Open Vite on `http://localhost:5173`. Database-only form edits need no npm or .NET rebuild. Vite hot-updates Vue/JS/CSS. C# changes require rebuild/restart. A published Host serves `wwwroot`, so client changes require `npm run build`.

2.3 Database table to form

1. Import a database table in Manage Data Models and preview differences.

2. Verify object/schema/key, nullability, calculated flags, and .NET types.

3. Mark database-generated IDs as Calculated/Generated.

4. Give foreign-key references semantic names such as `createdBy` and `updatedBy`.

5. Generate a form from the Data Model.

6. Verify each input `propertyName` to attribute mapping.

7. Create in Viewer, navigate to the returned record ID, reload, and verify update.

`propertyName` is a form data key. The Data Model attribute maps the column. Do not save through a control key such as `app_users.user_name`.

2.4 Control and value rules

  • Debounce or blur text input dependencies; do not validate the whole form per character.
  • Preserve boolean values for Radio/Checkbox; an unselected nullable value is null.
  • Hidden controls are not submitted.
  • Required/custom rules are validation; a tooltip is not.
  • Error CSS styles the control; message positioning is separate.
  • Pagination may validate/save per page; final Submit validates all pages.
  • Runtime collections may use APIs; Designer uses preview rows.

2.5 Substitution and i18n

Use `{name}`, `{row.name}`, number/date formats, and substitutions in action parameters. System-form text uses `@message.key` backed by Chinese and English catalogs. User-authored business content normally remains literal data.

2.6 Beginner diagnostics

Inspect Network URL/method/status/payload/error body; identify Designer versus Preview versus Viewer; verify form/record/deployment IDs; inspect form type, anonymity, mapping, and entity metadata; use SQL logs to verify projection/filter/sort; and check system-form initializer synchronization if a restart overwrites a form.

2.7 Exercise

Create an `InventoryCategory` Data Model and CRUD form, then a product form with an AsyncSelect category and a searchable/sortable/exportable DataGrid. Verify authorization and bilingual labels.

3. Intermediate: extend services and runtime

3.1 Outcomes

You should be able to use ORM transactions, add domain services/APIs/actions/triggers, create controls that work in all modes, use common errors/toasts/i18n/ACL, add migrations, and write integration/E2E tests.

3.2 Server features

First-party endpoints belong in the appropriate `Hosting/Endpoints/*Endpoints.cs`; customer features should be modules. Endpoints bind and authorize; services implement rules.

group.MapPost("/", async Task<IResult> (
    SaveRequest request, ClaimsPrincipal principal,
    ItemService service, CancellationToken ct) =>
{
    var userId = principal.FindFirstValue(ClaimTypes.NameIdentifier)
        ?? throw new UnauthorizedAccessException("Login is required.");
    var saved = await service.SaveAsync(userId, request, ct);
    return Results.Created($"/api/items/{saved.Id}", saved);
});

Reusable conflicts throw `PlatformApiException`; do not invent `{ message }` or `{ error }` payloads.

3.3 ORM principles

Begin a unit of work, perform related reads/validation/writes in it, and commit explicitly. Query Specifications use registered entity, attribute, and reference names; the ORM quotes identifiers and parameterizes values.

Project only needed fields, filter/sort in the database, use cursor/keyset pagination for large sets, define distinct references for repeated foreign tables, and include every owner/tenant boundary in server filters. Handwritten SQL remains parameterized and identifier-whitelisted.

3.4 Actions, triggers, and submissions

Server Actions implement `IServerActionsProvider`. Before triggers may change pending values; after triggers must respect transaction state. Substitute action parameters before typed parsing. General actions distinguish tokens such as `@userId`, `@Datetime`, and `@id` from literal strings and never expose arbitrary calculated-column writes.

Dependency tracking should evaluate only affected rules. Hovering an unrelated control must not run another field's custom validation.

3.5 New controls

A control needs a runtime component, Designer preview, property schema, relevant events, defaults, data/non-data classification, and async-loading policy. Business state is Vue reactive state, not `querySelector` mutations. DOM-required focus, measurement, print, or third-party integration is isolated behind refs and lifecycle hooks.

Verify Designer has no real API calls and is visually stable; Preview and Viewer share value/null/boolean semantics; disabled/read-only/required/error styles work; events are control-specific; attrs/emits are declared; and i18n, print, and container layouts work.

3.6 Authorization and migrations

Define the capability before the button. Read, design, delete, and survey assistance are independent checks; respondent routes also validate deployment assignment and time window. Client conditions improve UX but never replace endpoint checks.

Add a new immutable migration ID for each schema change. Never edit applied SQL because checksum enforcement will stop startup. Plan locking, batching, and recovery for data backfills.

3.7 Exercise

Add a transactional inventory-adjustment Server Action with an audit row and 409 conflict response. Add a summary API and chart control with Designer mock data. Cover both with PostgreSQL integration and Playwright tests.

4. Advanced: maintain the platform and ecosystem

4.1 Outcomes

Advanced developers design SDK/Host boundaries, audit identity and ACL risks, plan high-volume queries/caches/background work, maintain cross-provider migrations, establish reproducible CI/rollback, and decide whether a feature belongs in a form, control, module, or Core.

4.2 Boundary decisions

Prefer, in order: edit a form for layout/field/event change; create a reusable control/Runtime API for shared interaction; create a module for tables/APIs/actions/triggers; add to Core only when every installation needs it and platform lifecycle/security is involved.

The SDK exposes stable contracts, not Host implementation. New SDK APIs require version-range, XML documentation, binary-compatibility, and all-module CI review.

4.3 Performance

Use database ACL candidate filtering and cursor pagination, explicit projections, batch form+metadata reads, content hashes/ETags for global CSS, cancellable requests, and background work for expensive mail/export/file operations. Avoid unbounded queries, large OFFSET, N+1 access, and sensitive parameter logging.

A keyset cursor contains stable sort keys plus ID. Deleting the referenced record does not invalidate `(sortKey,id) > cursor`, but concurrent writes provide a weak snapshot. Use transaction snapshots or server-side export jobs when strict consistency is required.

4.4 Security, providers, and time

Modules are trusted in-process code; isolate untrusted code behind a separate process/API. Authorize both upload and download, validate file limits/types, use OIDC provider subject to create an internal identity, hash passwords, hide internal 500 details, and rotate all secrets.

Use `DateTimeOffset`/UTC for instants and Date for calendar dates. `datetime-local` has no offset and needs an explicit server timezone policy. PostgreSQL transactional DDL, MySQL implicit commit, and SQL Server conditional DDL require provider-aware migration review.

4.5 Release compatibility

Manifest version must match assembly Major/Minor/Build. Compatibility ranges are minimum-inclusive and maximum-exclusive. A safe release backs up, validates migrations, publishes Host/SDK, rebuilds all modules, deploys client assets, and performs smoke tests. Never pair a new manifest with an old DLL.

4.6 Advanced review checklist

Review SDK boundaries, unbounded/N+1 queries, authorization and ownership, common errors/i18n/trace IDs, irreversible migrations and recovery, Designer/Preview/Viewer/print consistency, PostgreSQL/Playwright coverage, and upgrade behavior for old forms/manifests/caches.

5. Team workflow

State the impact on forms, client, server, database, authorization, and tests before changing code. Keep changes focused and preserve unrelated dirty work. Update both handbook languages for public behavior and append `docs/OPENCODE_CHANGES.md`.

Review in this order: contract/security, data/migrations, domain behavior, API error contract, Vue reactivity/Designer, i18n/UX, tests/deployment.

6. Further reading