Skip to main content

Application Project Conventions

This page defines the mandatory project conventions for VEF application codebases.

It applies only to application projects built with VEF Framework Go. It does not define development conventions for the framework repository itself.

danger

All new application code MUST follow this page. When an existing area is modified, the touched files MUST remain compliant within the edited scope. These rules are mandatory, not optional suggestions.

tip

Read Project Structure first for the baseline module layout. Read this page for the naming rules, API naming rules, and project-level conventions that every VEF application MUST follow.

These rules combine:

  • the framework's actual API naming validation rules
  • the handler resolution rules used by api and internal/api
  • standard Go naming practices for application code

General Go Naming

ScopeRuleExamples
Package namesPackage names must be short, all-lowercase words. Mixed case and decorative separators must not be used. Singular names should be preferred by defaultapproval, security, storage, user, model
File namesFile names must stay lowercase and responsibility-based. When multiple words are needed, use snake_caseuser_resource.go, user_service.go, user_loader.go
Exported typesExported type names must use PascalCaseUserResource, UserService, LoginParams
Unexported typesUnexported type names must use lowerCamelCaseloginContext, routeEntry
Exported functions and methodsExported functions and methods must use PascalCase. Action-oriented names should start with a verbNewUserResource, CreateUser, FindPage
Unexported functions and methodsUnexported functions and methods must use lowerCamelCasebuildQuery, resolveHandler, parseRequest
InterfacesInterface names must express a capability or role. Prefer clear domain names or -er style when it reads naturallyUserLoader, TokenGenerator, PermissionChecker
VariablesVariable names must use lowerCamelCase. Very short names are only acceptable in tiny, obvious scopesuserID, requestMeta, authManager
ConstantsConstants must follow normal Go identifier naming. For grouped constants, keep a consistent domain prefix when it improves clarityDefaultRPCEndpoint, AuthTypePassword, UserMenuTypeDirectory
Struct fieldsStruct fields must use PascalCase in Go code. External naming styles belong in tags, not in Go identifiersCreatedAt, UserID, PermTokens

Additional required guidance:

  • package stutter must be avoided In package approval, prefer Instance or FlowService over names that redundantly repeat the package in every type
  • package names should generally stay singular unless a plural form is semantically unavoidable Prefer model, payload, resource, service, query, command over models, payloads, resources, services, queries, commands
  • common initialisms must keep normal Go form when that improves readability Examples: ID, URL, HTTP, JSON, API, JWT, RPC
  • names must stay domain-oriented and stable Prefer UserInfoLoader over vague names such as Loader2 or DataHelper

API Resource And Action Naming

VEF enforces naming rules for resource names and action names. Violating these rules causes resource registration to fail.

RPC resource names

RPC resource names must:

  • use slash-separated segments
  • keep each segment lowercase
  • use snake_case inside each segment when multiple words are needed
  • avoid leading slash, trailing slash, and duplicate slashes

Valid examples:

  • user
  • sys/user
  • sys/data_dict
  • approval/category

Required convention:

  • resource names should describe a business area or resource namespace
  • use nouns or noun phrases for resources, not verbs

RPC action names

RPC action names must use snake_case.

Valid examples:

  • create
  • find_page
  • get_user_info
  • resolve_challenge

Required convention:

  • action names should describe behavior, so verbs or verb phrases are preferred
  • keep action names stable once they become part of a public API contract
  • use get_, find_, create_, update_, delete_, resolve_, list_ consistently instead of mixing synonyms without a reason

Verb selection rules:

Verb or patternUse whenTypical scenariosExamplesDo not use for
get_xxxThe operation returns one deterministic object, one computed view, or one fixed piece of metadatacurrent user info, build info, schema detail, object metadata, detail by explicit keyget_user_info, get_build_info, get_table_schema, get_presigned_urlfuzzy search, paginated queries, bulk filtering
find_xxxThe operation performs query-style lookup driven by filters, search params, paging, or tree/options shapinglist pages, filtered lists, option lists, tree queries, single-record lookup under query semanticsfind_one, find_all, find_page, find_options, find_treeone fixed current-context object, imperative state transitions
list_xxxThe operation enumerates a straightforward collection without emphasizing search semanticslist tables, list views, list triggers, list files under a prefixlist_tables, list_views, list_triggers, list_filescomplex filter queries better expressed as find_xxx
create_xxxThe operation creates one new business objectcreate user, create flow, create draft, create token recordcreate_user, create_flow, create_draftupdates to existing objects, upsert-like semantics
create_many or create_xxx_batchThe operation creates multiple records in one requestbatch create employees, batch create tags, bulk initializationcreate_many, create_user_batchsingle-record create
update_xxxThe operation updates an existing object in placeedit profile, update flow config, update settingsupdate_user, update_flow, update_settingspartial state transitions that are semantically stronger verbs like approve, publish, enable
update_many or update_xxx_batchThe operation updates multiple existing records in one requestbatch status adjustment, bulk tag updateupdate_many, update_user_batchsingle-record update
delete_xxxThe operation removes one existing objectdelete user, delete file metadata, delete draftdelete_user, delete_draftsoft business cancellation or workflow termination when a domain verb is clearer
delete_many or delete_xxx_batchThe operation removes multiple records in one requestbulk delete users, bulk cleanupdelete_many, delete_user_batchsingle-record delete
enable_xxx / disable_xxxThe operation toggles a boolean-style availability or activation stateenable feature, disable account, disable integrationenable_user, disable_featurelong-running process lifecycle, publication lifecycle
start_xxx / stop_xxxThe operation starts or stops a running process, task, scheduler, or instancestart workflow instance, stop sync job, start replaystart_instance, stop_job, start_replaysimple boolean flag updates better expressed as enable/disable
submit_xxxThe operation submits a draft or request into the next processing stagesubmit form, submit approval request, submit applicationsubmit_form, submit_instancecreation itself when no stage transition is involved
approve_xxx / reject_xxxThe operation records an explicit business decisionapproval workflow, review workflow, moderationapprove_task, reject_task, approve_commentgeneric updates where no decision semantics exist
cancel_xxx / terminate_xxx / withdraw_xxxThe operation ends a business process for a specific domain reasoncancel order, terminate instance, withdraw applicationcancel_order, terminate_instance, withdraw_requestphysical deletion from storage or database
assign_xxx / unassign_xxxThe operation changes ownership or responsibilityassign task, unassign reviewer, assign departmentassign_task, unassign_reviewergeneric relation edits that are not about responsibility
add_xxx / remove_xxxThe operation adds or removes members, children, attachments, or lightweight associationsadd assignee, remove assignee, add cc, remove memberadd_assignee, remove_member, add_cccreation or deletion of the parent aggregate itself
bind_xxx / unbind_xxxThe operation creates or removes an explicit binding between two domains or external systemsbind role, unbind account, bind external appbind_role, unbind_accountloose membership updates that are better expressed as add/remove
publish_xxx / unpublish_xxxThe operation changes external visibility or released statepublish version, unpublish article, publish templatepublish_version, unpublish_articleinternal activation flags without release semantics
import_xxx / export_xxxThe operation ingests or emits data in batch/file/report formimport users, export employees, export reportimport_user, export_employee, export_reportordinary create/list operations
upload_xxx / download_xxxThe operation transfers file content or binary artifactsupload avatar, download attachment, upload objectupload_avatar, download_attachmentmetadata lookup without file transfer
generate_xxxThe operation produces a server-generated artifact, token, code, preview, or URLgenerate code, generate token, generate presigned URLgenerate_code, generate_token, generate_previewordinary reads where the value already exists as stored state
resolve_xxxThe operation resolves a challenge, conflict, alias, or pending state into a concrete resultresolve challenge, resolve conflict, resolve dependencyresolve_challenge, resolve_conflictordinary updates where no resolution semantics exist
validate_xxx / verify_xxxThe operation checks validity or correctness without committing state changesvalidate config, verify token, verify signaturevalidate_config, verify_tokenactions that mutate state
refresh_xxx / sync_xxx / rebuild_xxxThe operation recomputes, synchronizes, or refreshes derived state from another sourcerefresh token, sync departments, rebuild indexrefresh, sync_department, rebuild_indexfirst-time creation or ordinary read-only queries

Preferred framework-aligned patterns:

  • for standard CRUD-style reads, prefer the built-in find_one, find_all, find_page, find_options, find_tree, and find_tree_options vocabulary
  • for standard CRUD-style writes, prefer create, create_many, update, update_many, delete, and delete_many
  • do not mix near-synonyms such as query_page, search_list, remove_user, and delete_user inside the same bounded context unless the semantics are intentionally different

Common counterexamples:

Incorrect nameCorrect nameWhy it is incorrect
GetUserInfoget_user_infoRPC action names must use snake_case, not PascalCase
get-user-infoget_user_infoRPC action names must not use kebab-case
query_pagefind_pageStandard paginated query actions should align with built-in CRUD vocabulary
search_listfind_all or find_pagesearch and list are vague together; use the query shape that matches the real behavior
remove_userdelete_userDo not mix near-synonyms for the same deletion semantics inside one bounded context
get_user_listfind_all or find_pageCollection queries should not be named as single-object get_xxx actions
update_statusenable_xxx, disable_xxx, approve_xxx, or reject_xxx when those are the real semanticsGeneric update verbs are too weak when the business action is a specific state transition
handle_taskapprove_task, reject_task, assign_task, or another explicit domain verbhandle does not communicate the actual business intent
process_ordersubmit_order, cancel_order, complete_order, or another explicit lifecycle verbprocess is too broad to be a stable API action contract
sync_data_and_rebuild_indexsplit into sync_data and rebuild_index, or choose the single dominant actionOne action name should express one primary responsibility

RPC Action Naming Decision Order

Use this decision order when naming a new RPC action:

  1. Decide whether the action is a read, a write, a state transition, a relation change, or an integration/utility operation.
  2. If it is a standard CRUD read or write, use the built-in CRUD vocabulary first instead of inventing a synonym.
  3. If it is not standard CRUD, choose the narrowest verb that describes the business intent exactly.
  4. If two verbs seem possible, prefer the one that better matches the observable outcome of the API contract.
  5. If the action appears to contain multiple responsibilities, split the action or rename it around the dominant responsibility.

Decision flow:

RPC handler method names

When you do not specify Handler explicitly for an RPC operation, VEF resolves the handler method from the action name by converting it to PascalCase.

Examples:

  • find_page -> FindPage
  • get_user_info -> GetUserInfo
  • resolve_challenge -> ResolveChallenge

That means:

  • RPC action names should stay readable after PascalCase conversion
  • handler method names should be PascalCase verbs or verb phrases

REST resource names

REST resource names must:

  • use slash-separated segments
  • keep each segment lowercase
  • use kebab-case inside each segment when multiple words are needed

Valid examples:

  • users
  • sys/user
  • sys/data-dict
  • user-profiles

Required convention:

  • let the resource path describe the collection or domain boundary
  • let the HTTP method carry the main action semantics instead of putting verbs into the resource name

REST action names

REST action names must use one of these formats:

  • <method>
  • <method> <sub-resource>

Where:

  • <method> is a lowercase HTTP verb such as get, post, put, delete, or patch
  • <sub-resource> uses kebab-case

Valid examples:

  • get
  • post
  • delete
  • get profile
  • post admin
  • get user-friends

Required convention:

  • keep the method token lowercase
  • keep sub-resources noun-based and path-like
  • do not use snake_case for REST sub-resources

API versions

Resource versions must follow the v<number> format.

Valid examples:

  • v1
  • v2
  • v10

Versions should only be incremented when the external contract actually changes.

Handler, Params, And Meta Types

Application-owned API types must use names that reflect their role in the request pipeline.

Type kindRecommended patternExamples
Resource structs<Domain>ResourceUserResource, FlowResource
Params structs<Action>Params or <Domain><Action>ParamsLoginParams, CreateUserParams
Meta structs<Domain>Meta or <Action>MetaUserMeta, ExportMeta
Search structs<Domain>SearchUserSearch, OrderSearch
Service structs/interfaces<Domain>Service, <Capability>Loader, <Capability>ResolverUserService, UserLoader, DepartmentResolver

Type names must remain specific enough to make sense when read from another package.

Struct Fields, JSON, And Tags

VEF applications must keep these naming layers distinct:

  • Go struct field names use PascalCase
  • JSON field names use camelCase
  • database columns use snake_case

Example:

type User struct {
ID string `json:"id" bun:"id,pk"`
UserName string `json:"userName" bun:"user_name"`
CreatedAt string `json:"createdAt" bun:"created_at"`
}

Each layer must keep its own naming style consistently. Mixing naming styles inside the same layer is not acceptable.

Test Naming

Tests must follow the project testing naming rules:

ElementPatternExamples
Test suite<Feature>TestSuiteUserResourceTestSuite
Test methodTest<Feature>TestLogin, TestFindPage
Subtest namePascalCasePasswordExpired, EmptyInput, InvalidToken

Sub-scenarios must not be encoded with underscores in top-level test method names.

Use:

  • TestLogin with subtests such as PasswordExpired

Do not use:

  • TestLogin_PasswordExpired

See also