# Add Domain Entity

> Add a domain entity, aggregate, or value object to the .NET API — rich mutable class with private ctor + static factory (UUIDv7), invariants that throw, tenant_id, identity equality; records for value objects. Use when modeling new domain state under apps/api/{{ProjectName}}.Domain.

- Skill: `atherio-danp/add-domain-entity` (Agent Skill)
- Install (CLI): `npx skillmds@latest add atherio-danp/add-domain-entity`
- Raw SKILL.md: https://api.skillmd.com/api/skills/atherio-danp/add-domain-entity/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: atherio-danp (https://skillmd.com/u/atherio-danp)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/atherio-danp/add-domain-entity

---


> **Serena MCP is mandatory for C# code.** First call `mcp__serena__initial_instructions` to load the Serena tool manual, then use the Serena tools for ALL `.cs` reading / searching / navigation / creation / editing — prefer symbol navigation (`get_symbols_overview` / `find_symbol` / `find_referencing_symbols`) over whole-file reads. Native `Edit`/`Write` on `.cs` is hook-blocked (the TS/React frontend uses the native tools).

# Add a domain entity / value object

The procedure for our rich, base-class-free DDD model. Source of truth (the *facts* — read them):
`.claude/rules/backend/domain-model.md` and `docs/projectStandards/coding-standards.md`. The domain is
**persistence-ignorant** (no EF types/navigations). C# (`.cs`) edits via **Serena**.

## Entity / aggregate
1. Place in `{{ProjectName}}.Domain/<Feature>`.
2. **Rich mutable class** (NEVER a record). Shape:
   - a **private parameterless constructor** (EF materialization);
   - a `public static Create(...)` (or named) **factory** with guard clauses that throws on invalid input and
     **mints the id via `Guid.CreateVersion7()`** (never `Guid.NewGuid()` / `Guid.Empty`-until-save);
   - **all setters private** — state changes only through behaviour methods;
   - **invariants enforced inside every behaviour method** (throw on violation — the domain is the last line of
     defence);
   - a non-null **`TenantId`** preserved across mutations (the tenancy invariant);
   - **identity-based equality** declared on the type itself (no base `Entity`/`AggregateRoot`, no domain events).
3. **No primary constructors.**

## Value object
- A **record** (or `readonly record struct`) — immutable, value equality (e.g. `Money`, `ModelId`, `TenantId`).

## After
- EF mapping (config) and repository → the **`efcore-patterns`** skill. Build (`dotnet build`, warnings = errors).
- Tests (factory guards, invariant throws, behaviour) → the **`write-tests`** skill, per the plan's exact test list.

