Type to search pages and rules.

Browse pages

Naming and Code Style

Names should expose business intent and architectural role without requiring a reader to open the file.

Markdown11 provisions

Intent#

Names should expose business intent and architectural role without requiring a reader to open the file. Fixed suffixes and file conventions reduce variation across agent sessions.

Standards#

Anchor aggregate-owned types on the aggregate root#

ruleguidance

Requirement An aggregate-owned type's qualified name MUST start with its aggregate root's full name.

Rationale The name then reveals its owner without folder context. This covers events, states, child business records, values, and discriminated unions. A type nested inside a declaring type inherits that type's prefix through its qualified name. PostState.Published therefore satisfies this rule, and PostStatePublishedState repeats the anchor twice.

Example PostPublishedEvent and PostState.Published both lead with Post. PublishedState declared at namespace level does not.

litespec/workspace/naming#anchor-aggregate-owned-types-on-the-aggregate-root

Use architectural suffixes#

ruleguidance

Requirement An architectural type MUST carry the suffix its role assigns in the table in this section.

Example

RolePatternExample
Command message{UseCase}CommandCreateDraftCommand
Command result{UseCase}CommandResultCreateDraftCommandResult
Command handler{UseCase}CommandHandlerCreateDraftCommandHandler
Command validator{UseCase}CommandValidatorCreateDraftCommandValidator
Query message{UseCase}QueryGetPostQuery
Query result{UseCase}QueryResultGetPostQueryResult
Query result item{UseCase}QueryResultItemListPostsQueryResultItem
Query handler{UseCase}QueryHandlerGetPostQueryHandler
Query validator{UseCase}QueryValidatorGetPostQueryValidator
Aggregate state base{Aggregate}StatePostState
Aggregate state case, flat{Aggregate}{State}StatePostPublishedState
Aggregate state case, nested{Aggregate}State.{State}PostState.Published
Domain event{Aggregate}{PastFact}EventPostPublishedEvent
Event handler{Action}On{PastFact}HandlerNotifySubscribersOnPostPublishedHandler
Repository interfaceI{Aggregate}RepositoryIPostRepository
Repository implementation{Aggregate}RepositoryPostRepository
Endpoint{UseCase}EndpointCreateDraftEndpoint
HTTP request model{UseCase}RequestModelCreateDraftRequestModel
HTTP response model{UseCase}ResponseModelCreateDraftResponseModel
HTTP response item model{UseCase}ResponseItemModelListPostsResponseItemModel
HTTP pagination modelPaginationModelPaginationModel
Polymorphic transport model base{Concept}ModelFulfillmentOutcomeModel
Polymorphic transport model case{Case}{Concept}ModelShippedFulfillmentOutcomeModel
API mapping class{UseCase}ApiMappingsCreateDraftApiMappings
Options class{ConfigurationPurpose}OptionsEmailOptions
Persistence configuration{PersistedType}ConfigurationPostConfiguration
Registration class{Layer}ServiceRegistrationInfrastructureServiceRegistration
Assembly marker{Layer}AssemblyMarkerApplicationAssemblyMarker

In the dotnet stack, the repository interface carries the I prefix, options classes carry the Options suffix, and each scanned project carries an assembly marker.

An event-reaction folder names the business fact after an On prefix. It omits the Event suffix. For example, OnPostPublished contains NotifySubscribersOnPostPublishedHandler for PostPublishedEvent. The On prefix already marks the reaction. Folder names carry no technical suffix, so OnPostPublishedEvent is wrong. The event type keeps its Event suffix (litespec/workspace/naming#anchor-aggregate-owned-types-on-the-aggregate-root).

The example does not shorten an Application type to {UseCase}Result, {UseCase}Handler, or {UseCase}Validator. The example does not use an unowned name such as PostSummary for a query-specific result item. The full role suffix distinguishes command coordination from query projection without opening the file.

The example does not shorten an HTTP transport type to {UseCase}Request or {UseCase}Response. Model marks the type as passive boundary data rather than an operation or rich business object. Other project-owned, passive HTTP DTOs also name their concrete role and end in Model, such as ListPostsResponseItemModel or PaginationModel. The example uses ApiMappings instead of the context-dependent Mappings suffix.

A polymorphic transport model mirrors a Domain discriminated union (litespec/backend/api#mirror-a-domain-closed-set-as-a-transport-model-of-the-same-shape). Its abstract base names the concept. Each sealed case names its case. Both end in Model, such as FulfillmentOutcomeModel and ShippedFulfillmentOutcomeModel.

The transport name drops the Domain union's aggregate prefix, such as OrderFulfillmentOutcome. Its discriminator string keeps the Domain union's stable case code unchanged.

litespec/workspace/naming#use-architectural-suffixes

Name asynchronous operations completely#

ruleguidance

Requirement An asynchronous operation MUST reveal its asynchrony in its name and accept cancellation explicitly when the operation is cancelable.

Rationale A caller then sees both the asynchrony and the cancellation contract from the signature. An operation is cancelable when it performs I/O or awaits.

litespec/workspace/naming#name-asynchronous-operations-completely

Name exceptions by failed rule#

ruleguidance

Requirement An exception name MUST state the rule that failed, using {DomainType}{Reason}Exception for a Domain rejection.

Rationale DomainType is the aggregate root or an anchored owned type, so the name always starts with the aggregate root.

litespec/workspace/naming#name-exceptions-by-failed-rule

Use intent-revealing boolean names#

ruleguidance

Requirement A boolean property or method MUST use Is, Has, Can, or a precise verb.

Rationale HasLines and CanPublish state a condition. LinesPresent and CheckPublish state neither a question nor an answer.

litespec/workspace/naming#use-intent-revealing-boolean-names

Use current language features#

ruleguidance

Requirement Production code MUST target the pinned language version and use its current construct over an older equivalent.

Rationale In the dotnet stack, collection expressions, pattern matching, and primary constructors express the same intent with less incidental code.

litespec/workspace/naming#use-current-language-features

Use predictable frontend names#

ruleguidance

Requirement A frontend folder MUST use kebab-case, a component file and its export PascalCase, and a React hook file use-{name}.

Rationale One predictable mapping lets a reader move between the documentation tree and the source tree without translation.

litespec/workspace/naming#use-predictable-frontend-names

Conventions#

Align business names across layers#

conventionguidance

Default Use one business name for a module across Domain, Application, endpoints, features, and documentation.

Replacement A consumer departs from this default only through an Exception record naming this provision, with a reason and a review date.

Rationale The Posts module maps to Domain/Posts, features/posts, and docs/domain/modules/posts without translation.

litespec/workspace/naming#align-business-names-across-layers

Keep namespaces aligned with folders#

conventionguidance

Default Start a namespace with the project name and follow the folders beneath the project root.

Replacement A consumer departs from this default only through an Exception record naming this provision, with a reason and a review date.

Rationale Including src or apps in a namespace couples the type name to a workspace layout decision. The project root is the directory holding the project file, so every folder above it is outside the namespace.

litespec/workspace/naming#keep-namespaces-aligned-with-folders

Avoid generic type names#

conventionguidance

Default Avoid Manager, Helper, Processor, Common, Utility, BaseService, DataService, MessageBus, Engine, Facade, Wrapper, and Factory when a narrower name exists.

Replacement A consumer departs from this default only through an Exception record naming this provision, with a reason and a review date.

Rationale A generic name invites unrelated responsibility, because nothing in it excludes the next addition. Factory is generic on a class and exact on a named static aggregate factory method, so the default names the class and not the method.

litespec/workspace/naming#avoid-generic-type-names

Derive boundary names from the ubiquitous term#

conventionguidance

Default Derive route segments and JSON field names from the current aggregate or ubiquitous term.

Replacement A consumer departs from this default only through an Exception record naming this provision, with a reason and a review date.

Rationale Renaming a Domain concept then renames its boundary names, so the contract and the language stay aligned.

litespec/workspace/naming#derive-boundary-names-from-the-ubiquitous-term

Verification#

IDMarkEvidence
litespec/workspace/naming#anchor-aggregate-owned-types-on-the-aggregate-rootguidanceDoes each aggregate-owned type name begin with its aggregate root name?
litespec/workspace/naming#use-architectural-suffixesguidanceDoes each architectural type carry the suffix its role requires?
litespec/workspace/naming#name-asynchronous-operations-completelyguidanceDoes each asynchronous operation reveal its asynchrony in its name and accept cancellation explicitly when cancelable?
litespec/workspace/naming#name-exceptions-by-failed-ruleguidanceDoes each Domain exception name lead with its root and name its reason?
litespec/workspace/naming#use-intent-revealing-boolean-namesguidanceDoes each boolean member use an intent-revealing prefix or verb?
litespec/workspace/naming#use-current-language-featuresguidanceDoes production code target the pinned language version with current constructs?
litespec/workspace/naming#use-predictable-frontend-namesguidanceDoes each frontend follow the folder, component, and hook naming form?
litespec/workspace/naming#align-business-names-across-layersguidanceDoes one business name span each module across layers and documentation?
litespec/workspace/naming#keep-namespaces-aligned-with-foldersguidanceDoes each namespace match its folder path beneath the project root?
litespec/workspace/naming#avoid-generic-type-namesguidanceDoes each type name avoid the generic names this rule lists when a narrower name exists?
litespec/workspace/naming#derive-boundary-names-from-the-ubiquitous-termguidanceDoes each route segment and field name derive from its current ubiquitous term?