void-domain-driven-design

Applies domain-driven design patterns to TypeScript codebases with bounded contexts, aggregates, and branded value objects.

Updated May 29, 2026
One-click install
npx skills add https://github.com/voidcorp-core/void-harness --skill void-domain-driven-design-voidcorp-core
Or copy as Structured Prompt for Agent
Please help me install this Agent Skill.
Skill: void-domain-driven-design
Source: https://github.com/voidcorp-core/void-harness/tree/main/packages/core/skills/void-domain-driven-design
Command: npx skills add https://github.com/voidcorp-core/void-harness --skill void-domain-driven-design-voidcorp-core

SYSTEM DOCUMENTATION & REQUIREMENTS

What problem does it solve? Domain logic drifts into anemic models, generic repositories, and cross-context type imports as codebases grow, eroding invariants and shared vocabulary. This Skill enforces tactical DDD discipline so aggregates stay always-valid, value objects carry semantics, and bounded contexts stay isolated. ## Core Features & Use Cases - Bounded Contexts & Ubiquitous Language: Defines one model per context with anti-corruption adapters at boundaries, banning cross-context type imports. - Aggregates & Value Objects: Enforces small aggregates with smart constructors returning Result, branded types for domain primitives, and discriminated-union state machines. - Opinionated Bans: Rejects CQRS, event sourcing, mediator, generic Repository<T>, anemic models, and Manager/Helper/Util service classes by default. - Use Case: When modeling an Order flow in TypeScript, the Skill guides you to a small Order aggregate referencing CustomerId, an Email branded type with a smart constructor, and an OrdersPort repository interface instead of a generic Repository<T>. ## Quick Start Ask the agent to model a new Order aggregate with value objects and a repository port following domain-driven design rules.

Frequently Asked Questions about void-domain-driven-design

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I model aggregates in TypeScript with DDD?

Use small aggregates with a private constructor and a static create method returning Result, so invariants hold by construction. Reference other aggregates by ID rather than embedding full objects, and route every mutation through methods that preserve invariants.

What is a branded type value object in TypeScript?

A branded type intersects a primitive with a unique brand tag, like string & { __brand: 'Email' }, so the compiler rejects raw strings. A smart constructor validates input and returns Result<Email, ValidationError> instead of throwing.

Should I use a class or discriminated union for domain state?

Use a class for aggregates with many invariants and behavior methods, such as Order or Account. Use a discriminated union for workflows with distinct data per state, like a BookingFlow, so the compiler enforces valid transitions.

Does this DDD approach use CQRS or event sourcing?

No. CQRS, event sourcing, mediator patterns, and generic Repository<T> are banned by default under the project's philosophy. Reintroducing any of them requires an explicit architecture decision record through the void-decide skill.

Why are cross-context type imports forbidden?

Direct imports of another bounded context's types collapse context boundaries into a shared model, which is a big ball of mud. Communication across contexts must go through an anti-corruption adapter that translates between models.

When should I use a domain service instead of an aggregate method?

Use a domain service only when an operation spans multiple aggregates, such as transferring funds between two accounts. Single-aggregate operations belong as methods on the aggregate itself to avoid anemic service dumping grounds.