Skip to content

no-impure-domain ​

The domain depends on nothing but itself: its own domain, the domain of the shared kernel and the domain building blocks of @alveolus/core.

Rule
layers/no-impure-domain
Category
Layers: what each layer may depend on
Reports
The domain importing a framework, a database, another layer or a package not allowed
Applies to
Every file in domain/, in every bounded context and the shared kernel
Turn off
"layers/no-impure-domain": "off"

Why ​

The Order aggregate carries TypeORM decorators so that it can be saved as is, and calls a mailer when it is placed. Upgrading the ORM now means touching the business rules, and testing that an empty order is refused needs a database and an SMTP server.

The fix

The domain imports nothing technical. Storage and email are ports declared by the domain and implemented by driven adapters. The business rules change when the business does, not when a library does, and run in a test without any infrastructure.

What it checks ​

Every import of a file in domain/, in a bounded context or in the shared kernel:

ImportAllowed when
A file of the projectIt is in the domain/ of the same context or of the shared kernel.
@alveolus/coreEvery imported name is a domain building block or part of Result: AggregateRoot, Entity, ValueObject, Identifier, DomainEvent, DomainError, DomainService, Port, Clock, IdGenerator, CommandRepository, QueryRepository, View, Result, ok, err, map, mapErr, andThen, combine and their Any… types.
Any other packageIt is listed in domainDependencies; when its entry lists names, every imported name is one of them.

Importing from the root @alveolus/core is fine: the rule checks each imported name, not the path.

What it reports ​

src/ordering/domain/aggregates/order.aggregate.ts:1
  layers/no-impure-domain: The domain imports @nestjs/common:
  add it to domainDependencies if the domain really needs it.

src/ordering/domain/aggregates/order.aggregate.ts:2
  layers/no-impure-domain: The domain imports UnitOfWork from
  @alveolus/core: only domain building blocks and Result are allowed.

src/ordering/domain/aggregates/order.aggregate.ts:5
  layers/no-impure-domain: The domain imports
  src/ordering/driven/smtp/adapters/mailer.adapter.ts
  (ordering driven): it may only import the domain.

A name not allowed from a restricted package is reported as well:

  layers/no-impure-domain: The domain imports format from date-fns:
  domainDependencies only allows addDays, isBefore.

Fix it ​

Keep infrastructure behind a port ​

So that the business rules survive a change of database, framework or mail provider, the domain declares what it needs as a port, and a driven adapter implements it. Transactions belong to the command handler, not to the domain.

❌ Avoid: src/ordering/domain/aggregates/order.aggregate.ts
ts
import { Injectable } from "@nestjs/common";
import { AggregateRoot, UnitOfWork } from "@alveolus/core";
import { Column, Entity } from "typeorm";

import { Mailer } from "../../driven/smtp/adapters/mailer.adapter";
✅ Prefer: src/ordering/domain/aggregates/order.aggregate.ts
ts
import { AggregateRoot, err, ok, type Result } from "@alveolus/core";

import { Money } from "../../../shared-kernel/domain/value-objects/money.value-object";
import { InvalidTotal } from "../errors/invalid-total.error";

Map storage outside the domain ​

So that the aggregate is not shaped by its table, it exposes a snapshot, and the repository adapter maps that snapshot to its storage. See Aggregates.

Allow a package ​

Some packages belong in a domain, such as a decimal library for money. Declare them, with true to allow everything they export, or with the names you allow:

alveolus.config.ts
ts
export default defineConfig({
	boundedContexts: { ordering: "ordering" },
	domainDependencies: {
		"date-fns": ["addDays", "isBefore"],
		"decimal.js": true,
	},
	root: "src",
});

The packages of domainDependencies are allowed in the application too.

Turn it off ​

alveolus.config.ts
ts
rules: { "layers/no-impure-domain": "off" },

On an existing project, prefer a baseline: new code keeps the domain pure while you clean up the old one.

See also ​

Released under the MIT License.