Install
$ agentstack add skill-camilooscargbaptista-cto-toolkit-domain-modeling ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Domain Modeling (DDD Practical)
When to Use
- Starting a new module/service and need to define boundaries
- Complex business logic that doesn't fit in simple CRUD
- Team struggling with "where does this logic go?"
- Preparing for microservices decomposition
Event Storming (Discovery)
The Process
1. Invite: developers + domain experts + product
2. Orange stickies: Domain Events (past tense)
"Abastecimento Validado", "Ciclo de Faturamento Fechado"
3. Blue stickies: Commands (what triggers events)
"Validar Abastecimento", "Fechar Ciclo"
4. Yellow stickies: Aggregates (who handles commands)
"Abastecimento", "CicloFaturamento"
5. Pink stickies: External Systems
"Gateway Pagamento", "Emissor NF-e"
6. Group into Bounded Contexts
Example — ZECA Domain
┌─── Refueling Context ──────────────────────────┐
│ Events: │
│ RefeuelingCodeGenerated │
│ RefuelingValidated │
│ RefuelingCancelled │
│ Aggregates: │
│ RefuelingCode, Refueling │
│ Commands: │
│ GenerateCode, ValidateRefueling, CancelCode │
└─────────────────────────────────────────────────┘
┌─── Billing Context ────────────────────────────┐
│ Events: │
│ BillingCycleOpened │
│ FeeCalculated │
│ CycleClosed │
│ InvoiceGenerated │
│ Aggregates: │
│ BillingCycle, Invoice │
│ Commands: │
│ CalculateFee, CloseCycle, GenerateInvoice │
└─────────────────────────────────────────────────┘
Bounded Contexts
Rules:
1. Each context owns its data (no shared database tables)
2. Same word can mean different things in different contexts
"User" in Auth = credentials + session
"User" in Billing = payment info + billing address
3. Communication between contexts via events or APIs
4. One team per context (ideally)
Aggregates
// Aggregate = consistency boundary
// One transaction per aggregate
// Reference other aggregates by ID, not by object
// ✅ Good aggregate design
class BillingCycle {
private id: string;
private stationId: string; // Reference by ID
private status: CycleStatus;
private transactions: Transaction[]; // Owned by this aggregate
private totalFees: Money;
// Business logic lives HERE
addTransaction(refueling: RefuelingEvent): void {
if (this.status !== 'ACTIVE') {
throw new DomainError('Cannot add to closed cycle');
}
const fee = this.calculateFee(refueling);
this.transactions.push(new Transaction(refueling.id, fee));
this.totalFees = this.totalFees.add(fee);
}
close(): void {
if (this.transactions.length === 0) {
throw new DomainError('Cannot close empty cycle');
}
this.status = 'CLOSED';
// Raises domain event
this.raise(new BillingCycleClosed(this.id, this.totalFees));
}
}
// ❌ Bad: anemic domain model
class BadBillingCycle {
id: string;
stationId: string;
status: string; // Public, no validation
transactions: any[]; // No encapsulation
totalFees: number; // Primitive obsession
}
// All logic in BillingCycleService → anemic!
Value Objects
// Immutable, compared by value (not by ID)
class Money {
constructor(
private readonly amount: number,
private readonly currency: string = 'BRL',
) {
if (amount {
const cycle = await this.cycleRepo.findActive(event.stationId);
cycle.addTransaction(event);
await this.cycleRepo.save(cycle);
}
}
Quality Gates
- [ ] Business logic in domain objects (not services)
- [ ] Value Objects for domain concepts (Money, CPF, Email)
- [ ] Aggregates enforce invariants
- [ ] Bounded contexts communicate via events/APIs
- [ ] No direct database queries in domain layer
- [ ] Domain events for cross-context communication
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: camilooscargbaptista
- Source: camilooscargbaptista/cto-toolkit
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.