Implement the 002-add-combatant feature that adds the possibility to add new combatants to an encounter
This commit is contained in:
77
specs/002-add-combatant/data-model.md
Normal file
77
specs/002-add-combatant/data-model.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Data Model: Add Combatant
|
||||
|
||||
**Feature**: 002-add-combatant
|
||||
**Date**: 2026-03-03
|
||||
|
||||
## Entities
|
||||
|
||||
### Combatant (existing, unchanged)
|
||||
|
||||
| Field | Type | Constraints |
|
||||
|-------|------|-------------|
|
||||
| id | CombatantId (branded string) | Unique, required |
|
||||
| name | string | Non-empty after trimming, required |
|
||||
|
||||
### Encounter (existing, unchanged)
|
||||
|
||||
| Field | Type | Constraints |
|
||||
|-------|------|-------------|
|
||||
| combatants | readonly Combatant[] | Ordered list, may be empty |
|
||||
| activeIndex | number | 0 <= activeIndex < combatants.length (or 0 if empty) |
|
||||
| roundNumber | number | Positive integer >= 1, only increases |
|
||||
|
||||
## Domain Events
|
||||
|
||||
### CombatantAdded (new)
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| type | "CombatantAdded" (literal) | Discriminant for the DomainEvent union |
|
||||
| combatantId | CombatantId | Id of the newly added combatant |
|
||||
| name | string | Name of the newly added combatant |
|
||||
| position | number | Zero-based index where the combatant was placed |
|
||||
|
||||
## State Transitions
|
||||
|
||||
### AddCombatant
|
||||
|
||||
**Input**: Encounter + CombatantId + name (string)
|
||||
|
||||
**Preconditions**:
|
||||
- Name must be non-empty after trimming
|
||||
|
||||
**Transition**:
|
||||
- New combatant `{ id, name: trimmedName }` appended to end of combatants list
|
||||
- activeIndex unchanged
|
||||
- roundNumber unchanged
|
||||
|
||||
**Postconditions**:
|
||||
- combatants.length increased by 1
|
||||
- New combatant is at index `combatants.length - 1`
|
||||
- All existing combatants preserve their order and index positions
|
||||
- INV-2 satisfied (activeIndex still valid for the now-larger list)
|
||||
|
||||
**Events emitted**: Exactly one `CombatantAdded`
|
||||
|
||||
**Error cases**:
|
||||
- Empty or whitespace-only name → DomainError `{ code: "invalid-name" }`
|
||||
|
||||
## Function Signatures
|
||||
|
||||
### Domain Layer
|
||||
|
||||
```
|
||||
addCombatant(encounter, id, name) → { encounter, events } | DomainError
|
||||
```
|
||||
|
||||
### Application Layer
|
||||
|
||||
```
|
||||
addCombatantUseCase(store, id, name) → DomainEvent[] | DomainError
|
||||
```
|
||||
|
||||
## Validation Rules
|
||||
|
||||
| Rule | Layer | Error Code |
|
||||
|------|-------|------------|
|
||||
| Name non-empty after trim | Domain | invalid-name |
|
||||
Reference in New Issue
Block a user