Skip to content

placement ​

Each class lives in the folder of its kind, in a file whose name ends with that kind, one class per file. Knowing what a class is tells you where it is, and the other way round.

❌ Avoid: src/ordering/domain/order.ts
ts
import { AggregateRoot, Identifier } from "@alveolus/core";

export class OrderId extends Identifier<string, "OrderId"> {}

export class Order extends AggregateRoot<OrderId> {}
✅ Prefer: src/ordering/domain/aggregates/order.aggregate.ts
ts
import { AggregateRoot } from "@alveolus/core";

import type { OrderId } from "../value-objects/order-id.identifier";

export class Order extends AggregateRoot<OrderId> {}

What it checks ​

  • A file declares one class at most.
  • A class that extends a building block is in the folder and the file of its kind:
ExtendsFolderFile name ends with
AggregateRootdomain/aggregates/.aggregate.ts
Entitydomain/entities/.entity.ts
Identifierdomain/value-objects/.identifier.ts
ValueObjectdomain/value-objects/.value-object.ts
DomainEventdomain/events/.event.ts
DomainErrordomain/errors/.error.ts
DomainServicedomain/services/.service.ts
CommandRepository, QueryRepository (abstract)domain/repositories/.repository.ts
Port (abstract)domain/ports/.port.ts
CommandHandlerapplication/commands/.command.ts
QueryHandlerapplication/queries/.query.ts
EventTranslatorapplication/translators/.translator.ts
A port (concrete class)driven/<technology>/adapters/.adapter.ts
implements OpenHostService or AntiCorruptionLayer, without a portdriving/free

The types that belong to a class, such as its snapshot or its command input, stay in its file. Classes that extend no building block, such as a controller or a module, are not placed by this rule.

Why ​

A layout that every project shares is only useful if it holds. With one class per file, in the folder of its kind, finding a concept takes no search, a review shows at a glance what a change touches, and an agent puts new code where the existing code is.

What it reports ​

src/ordering/domain/order.ts:3
  placement: OrderId belongs in domain/value-objects/*.identifier.ts.

src/ordering/domain/order.ts:5
  placement: Order shares its file with OrderId: one class per file.

src/ordering/domain/order.ts:5
  placement: Order belongs in domain/aggregates/*.aggregate.ts.

Turn it off ​

ts
rules: { placement: "off" }

See also ​

Released under the MIT License.