Опирается на правила:
NODE-6…NODE-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.ts | NODE-6 | create-order.handler.ts |
orderController/, OrderController/ | NODE-6 | order/ |
interface IOrderRepository | NODE-7 | interface OrderRepository |
type orderStatus = ... | NODE-7 | type OrderStatus = ... |
order(), products() | NODE-8 | createOrder(), listProducts() |
isExpanding() для метода с side-effect | NODE-8 | expand() |
const MAX_ITEMS = 50 внутри функции | NODE-9 | const maxItems = 50 (локальный — camelCase) |
const orderRepository = Symbol(...) (module-level DI-токен) | NODE-9 | ORDER_REPOSITORY = Symbol(...) |
it('test1'), it('should work') | NODE-10 | it('rejects order when balance is insufficient') |
enum OrderStatus { New, Paid } для простого набора | NODE-X12 | type OrderStatus = 'new' \| 'paid' |
Куда дальше
- node/imports.md — named exports, порядок импортов, path-aliases вместо deep relative.
- node/expressions.md —
unknown+ narrowing, guard clause, типизация публичных сигнатур. - node/async.md — именование async-методов: глагол,
find*vsget*,handle*. - node/immutability.md —
readonlyна полях,as const, DI-токены какprivate readonly. - node/tooling.md — настройка
tsconfig.json, ESLint и Prettier, CI-прогон. - Раздел «Стандарты → Code Style» — хаб языковых биндингов: Java, Node, Python, Go.