Skip to content
Back to skills

Wiring

ASecurity

Explains how the classes of a bounded context reach the composition root in a ddd-ts-kit project. Ports as abstract classes used as injection tokens, the declared style (decorators of the kernel, dependencies stated by the constructor signature, a generated barrel) or the explicit module file, values bound in the platform, fakes in tests. Use when binding an adapter to a port, adding a dependency to a handler, replacing an adapter by a fake, composing main.ts, or when a WiringError, Declarati...

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 16, 2026
developmentbashreacttestingapidatabase

Works with

  • api

Security analysis

A100/100

Scanned October 16, 2026

npx -y skills add gbourgeat/ddd-ts-kit --skill wiring --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Wiring?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Wiring
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/gbourgeat-wiring/badge)](https://www.skillsdirectory.com/skills/gbourgeat-wiring)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: wiring
description: >-
  Explains how the classes of a bounded context reach the composition root in a ddd-ts-kit
  project. Ports as abstract classes used as injection tokens, the declared style
  (decorators of the kernel, dependencies stated by the constructor signature, a generated
  barrel) or the explicit module file, values bound in the platform, fakes in tests. Use
  when binding an adapter to a port, adding a dependency to a handler, replacing an adapter
  by a fake, composing main.ts, or when a WiringError, DeclarationError, ports/*,
  declarations/* or composition/only-in-module is reported.
allowed-tools:
  - Read
  - Bash(npx ddd-ts-kit generate *)
  - Bash(npx ddd-ts-kit docs)
  - Bash(npx ddd-ts-kit check *)
  - Bash(npx ddd-ts-kit list *)
---

# Wiring

`stack.style` in `ddd-ts-kit.config.json` says which style the project uses. Exact API:
README of `@ddd-ts-kit/di`. The generators write all of this; read here what to do when you
change it by hand.

## Ports, in both styles

- A port is an **abstract class with abstract members only**, one per file:
  `domain/repositories/<name>.repository.ts` (extends the kernel `Repository<Aggregate>`) or
  `application/ports/<name>.port.ts`. The class is the contract **and** the injection token:
  no constant, no `token()`, no `*.ports.ts` file (`ports/abstract-class`,
  `ports/no-token-constant`).
- An adapter `implements` its port, never `extends` it (`ports/implements-never-extends`).
- A dependency is named by the class of its port, never by an adapter or a fake
  (`ports/no-concrete-dependency`). Where a list is written (the explicit style, a
  `bind(Port)`), the port is imported as a value (`ports/value-import`).
- A value that is not a port (settings, a database handle) is carried the same way: an
  abstract class with readonly members, bound to a plain object in the platform.

## Declared style (default with tsyringe)

A class says what it is on its own file, and that is its registration. There is no
`<context>.module.ts`.

| Class | Decorator of `@ddd-ts-kit/kernel/declarations` |
|---|---|
| command, query | `@Requires(permission)` or `@Public()` |
| handler | `@CommandHandler(Message)`, `@QueryHandler(Message)` |
| reaction to a domain event, publisher | `@DomainEventHandler(Event)`, attached under the `static readonly eventName` of the event class; `{ phase: 'after-commit' }` for an effect outside the transaction (never a publisher) |
| integration event handler | `@IntegrationEventHandler(definition, { consumer })` |
| scheduled job | `@Scheduled('<context>.<job>', { every } or { cron, timeZone })` |
| projector of a read model | `@Projection('<context>.<name>')`, with `@On(definition)` on each method that applies an event |
| policy | `@Policy(permission, { name })` |
| adapter | `@Repository(Port)`, `@Gateway(Port)`, `@Adapter(Port, { kind })` |

- Nothing under `domain/` carries a decorator. Names (`consumer`, `name`) are string literals.
- **The constructor states the dependencies, and nothing repeats it.** Each parameter is
  typed by one class, which is its token: a port, a kernel port (`Clock`, `CommandBus`,
  `Logger`...), another declared class, or an abstract class the platform binds to a value.
  Never a union, an interface, a primitive, `?`, a default value, a rest parameter
  (`declarations/constructor-injection`). A port that only types a parameter is imported with
  `import type`.
- To add a dependency to a handler: add the constructor parameter. Then `npx ddd-ts-kit docs`
  (`npm run dev` does it by itself on every reload). Nothing else, and no list in the
  decorator.
- The generated file `<context>.declarations.ts` holds one `export *` per role file, then one
  `injected(Class, [Tokens])` per class whose constructor takes something, read from the
  source. After adding, moving or deleting a role file, or changing a constructor, by hand:
  `npx ddd-ts-kit docs` (`declarations/barrel-current` names the class whose list is stale).
  Never edit it. With `stack.assembly: discovery` the composition root reads the role files
  itself (`discoverContext`) and the lists from the generated `<context>.dependencies.ts`.
- `WiringError MISSING_DEPENDENCIES` or `DEPENDENCY_ARITY` at startup: the generated file is
  stale or was not loaded. Run `npx ddd-ts-kit docs`; in a test, assemble the context from its
  generated file (`contextModule('<context>', declarations)`), never from a class imported
  alone.
- Compiler metadata is never used: `emitDecoratorMetadata` is absent with standard
  decorators and under `tsx`. Do not add `reflect-metadata` calls or `@Inject`.
- `stack.dependencies: listed` (a 0.1 project; what a config without the key means): the
  decorators list their dependencies, `@CommandHandler(Message, [Ports])`, in the order of the
  constructor. Keep the form the project uses; to move to signatures, set
  `"dependencies": "signature"`, `npx eslint . --fix`, `npx ddd-ts-kit docs`.
- A value or a factory, which no decorator can say, is bound in
  `src/platform/platform.module.ts` (and its test twin): that file belongs to the platform,
  not to a context.

## Explicit style (NestJS, manual wiring, or by choice)

Every binding is written in `<context>.module.ts`, the only file of a context allowed to
know every layer (`composition/only-in-module`):
`builder.bind(Port).toClass(Adapter, [Ports]).as('<kind>')`,
`builder.command(Message).requires(permission).handledBy(Handler, [Ports])` (or `.public()`),
`builder.on(definition, { consumer }).handledBy(Handler, [Ports])`. Explicit stays explicit:
the list of each statement names the ports of the constructor, in order, and the compiler
checks it; nothing is read from signatures here. Generators add the lines between the
markers. With NestJS or manual wiring the rule is the same in another syntax;
keep `@Injectable()` out of `domain/` and `application/`.

## Composition root

`src/main.ts` starts tracing first, loads the configuration, opens persistence, then calls
`composeApplication` of `src/platform/compose-application.ts`, where each context is one line
(`contextModule('<context>', declarations)`, or the module of an explicit context) added by
`generate context`. Adapters are instantiated, and declarations assembled, nowhere else
(`composition/only-in-module`). `createApplication` refuses duplicate bindings, missing
dependencies and cycles before anything is instantiated: a `WiringError` names the port and
the module.

Controllers, guards, request mappers and serializers are declared classes too (`@Controller`,
`@Guard`, `@RequestMapper`, `@Serializer` of `@ddd-ts-kit/http`): the generated file of the
context exports them and registers what each constructor takes, and the composition root mounts
every controller with `controllerRoutes(resolve(DeclaredEdges).of('controller'), { resolve })`.
No file lists the routes of a declared context. In the explicit style the list of routes
builds each controller by hand: `...controllerRoutes([new IssueInvoiceController(commands)])`.

## Tests

```ts
const application = createTestApplication({
  modules: [platformTestModule, contextModule('ordering', orderingDeclarations)],
  fakes: [fake(OrderRepository, orders), fake(PaymentGateway, payments)],
});
```

The test application brings a fixed clock, sequential identifiers, in-memory buses, logger,
metrics and error reporter. Replace every adapter that leaves the process by its fake; never
mock a module. Details: skill `ddd-ts-kit:testing`.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.