Опирается на правила: NODE-6NODE-10 из Node Style Guide → раздел 2. Именование.

Важно знать

  • Файлы и директорииkebab-case; Nest-суффиксы через точку: .handler.ts, .module.ts, .controller.ts, .service.ts, .spec.ts.
  • Классы, интерфейсы, типы, decorator-фабрикиPascalCase; интерфейсы без префикса I.
  • Функции и методыcamelCase; всегда глагол или глагольная фраза (createOrder, не order).
  • Константы уровня модуля и DI-токеныUPPER_SNAKE_CASE; локальный const внутри функции — обычный camelCase.
  • Имена тестов — говорящие фразы: it('rejects order when balance is insufficient'), не it('test1').
  • enum для простых наборов — запрещён: string literal union + as const-объект (см. NODE-X12).
  • Аббревиатуры подчиняются общему PascalCase — HttpClient, OrderDto, ApiError, не HTTPClient, OrderDTO.
  • Правила ловит ESLint @typescript-eslint/naming-convention; семантику (глагол vs существительное в имени) — ревью.

Именование — самый дешёвый способ передать намерение. Прочитанное имя должно однозначно отвечать: что это за сущность, что она делает, какова её роль в слое. Когда имя отвечает на эти вопросы, необходимость в комментарии исчезает.

Файлы и директории

NODE-6: kebab-case везде, Nest-суффиксы через точку.

src/
  order/
    create-order.handler.ts
    create-order.handler.spec.ts
    order.controller.ts
    order.module.ts
    order.service.ts
    order.repository.ts
  customer/
    customer.controller.ts
    customer.module.ts

Суффикс через точку — не декорация. По нему IDE, ESLint и скрипты CI понимают роль файла без чтения содержимого. create-order.handler.ts — это Handler юз-кейса, order.controller.ts — Controller REST-слоя. Суффикс одинаков по всему монорепо — навигация по Cmd+P / Ctrl+P по .handler находит только Handler'ы.

Директории именуются по домену, не по роли слоя:

src/order/          ✓
src/handlers/       ✗  — роль, не домен
src/orderService/   ✗  — camelCase в директории
src/Order/          ✗  — PascalCase в директории

Классы и типы

NODE-7: PascalCase для классов, интерфейсов, type alias, enum (при осознанном использовании), decorator-фабрик.

class CreateOrderHandler {}
class OrderController {}
class CustomerRepository {}

interface OrderRepository {}
interface PaymentGateway {}

type OrderStatus = 'new' | 'paid' | 'cancelled';
type CreateOrderCommand = { customerId: string; items: OrderItem[] };

Интерфейсы — без префикса I. Префикс IOrderRepository — конвенция C#, не TypeScript. В TS interface и class взаимозаменяемы структурно; I-префикс только визуальный шум без семантики.

interface IOrderRepository {}   ✗ — C#-стиль
interface OrderRepository {}    ✓

Decorator-фабрики тоже PascalCase — они выглядят как встроенные декораторы NestJS:

@Injectable()
@Controller('orders')
@Module({ ... })

Функции и методы

NODE-8: camelCase, обязательно глагол или глагольная фраза.

async createOrder(command: CreateOrderCommand): Promise<OrderId> { ... }
async findOrderById(id: OrderId): Promise<Order | undefined> { ... }
async cancelOrder(id: OrderId): Promise<void> { ... }
isEligibleForDiscount(customer: Customer): boolean { ... }
hasActiveSberSubscription(customer: Customer): boolean { ... }

Глагол обязателен — метод выполняет действие. Существительное в имени метода вводит читателя в заблуждение: order() — это создать заказ? получить заказ? объект-заказ?

order()             ✗ — существительное
placeOrder()        ✓
getOrder()          ✓
fetchOrder()        ✓

products()          ✗
listProducts()      ✓
findProducts()      ✓

Глагольные префиксы по смыслу:

  • create* / place* / register* — команды, изменяющие состояние.
  • find* / get* / fetch* — запросы, возвращающие данные; find сигналит «может вернуть undefined», get — «всегда есть».
  • is* / has* / can* — предикаты, возвращают boolean.
  • handle* / process* — обработчики событий или команд.
async findProductById(id: ProductId): Promise<Product | undefined> { ... }
async getProductOrThrow(id: ProductId): Promise<Product> { ... }
canCustomerOrder(customer: Customer): boolean { ... }
handleOrderCreated(event: OrderCreatedEvent): Promise<void> { ... }

Константы и DI-токены

NODE-9: константы уровня модуля и DI-токены — UPPER_SNAKE_CASE; локальный const внутри функции — camelCase.

export const ORDER_REPOSITORY = Symbol('ORDER_REPOSITORY');
export const PAYMENT_GATEWAY = Symbol('PAYMENT_GATEWAY');
export const MAX_ORDER_ITEMS = 50;
export const DEFAULT_CURRENCY = 'RUB';
export const SBER_API_BASE_URL = 'https://api.sber.ru/v1';

UPPER_SNAKE_CASE — сигнал «это константа уровня модуля, существует один раз, не меняется». Читатель мгновенно отличает её от runtime-переменной или параметра функции.

DI-токены через Symbol — стандарт NestJS для типобезопасной инъекции:

export const ORDER_REPOSITORY = Symbol('ORDER_REPOSITORY');

@Module({
  providers: [
    {
      provide: ORDER_REPOSITORY,
      useClass: TypeOrmOrderRepository,
    },
  ],
})
export class OrderModule {}

@Injectable()
export class CreateOrderHandler {
  constructor(
    @Inject(ORDER_REPOSITORY)
    private readonly orderRepository: OrderRepository,
  ) {}
}

Локальный const внутри функции — обычный camelCase, не UPPER_SNAKE_CASE:

async createOrder(command: CreateOrderCommand): Promise<OrderId> {
  const customer = await this.customerRepository.findById(command.customerId);
  const order = Order.create(customer, command.items);
  await this.orderRepository.save(order);
  return order.id;
}

customer и order — локальные переменные, не константы. UPPER_SNAKE_CASE для них сломает читаемость.

Имена тестов

NODE-10: говорящие фразы, описывающие поведение сценария.

describe('CreateOrderHandler', () => {
  it('creates order when customer exists and items are valid', async () => { ... });
  it('rejects order when customer balance is insufficient', async () => { ... });
  it('rejects order when product is out of stock', async () => { ... });
  it('emits OrderCreatedEvent after successful creation', async () => { ... });
});

Формат: it('<глагол> <что происходит> when <условие>'). Читается как спецификация поведения без открытия файла теста. При провале в CI название теста объясняет сломавшийся сценарий.

it('test1', ...)                        ✗
it('should work', ...)                  ✗
it('creates order', ...)                ✗ — нет условия
it('creates order when valid', ...)     ✓

Cross-ref NODETEST-16: структура describe/it, один сценарий на один тест, Arrange-Act-Assert — в отдельной статье по тест-стратегии.

Что запрещено

АнтипаттернПравилоЧто взамен
create-order.Handler.ts, CreateOrder.tsNODE-6create-order.handler.ts
orderController/, OrderController/NODE-6order/
interface IOrderRepositoryNODE-7interface OrderRepository
type orderStatus = ...NODE-7type OrderStatus = ...
order(), products()NODE-8createOrder(), listProducts()
isExpanding() для метода с side-effectNODE-8expand()
const MAX_ITEMS = 50 внутри функцииNODE-9const maxItems = 50 (локальный — camelCase)
const orderRepository = Symbol(...) (module-level DI-токен)NODE-9ORDER_REPOSITORY = Symbol(...)
it('test1'), it('should work')NODE-10it('rejects order when balance is insufficient')
enum OrderStatus { New, Paid } для простого набораNODE-X12type OrderStatus = 'new' \| 'paid'

Куда дальше

  • node/imports.md — named exports, порядок импортов, path-aliases вместо deep relative.
  • node/expressions.md — unknown + narrowing, guard clause, типизация публичных сигнатур.
  • node/async.md — именование async-методов: глагол, find* vs get*, handle*.
  • node/immutability.md — readonly на полях, as const, DI-токены как private readonly.
  • node/tooling.md — настройка tsconfig.json, ESLint и Prettier, CI-прогон.
  • Раздел «Стандарты → Code Style» — хаб языковых биндингов: Java, Node, Python, Go.