Using the PearDrop CLI
The PearDrop CLI scaffolds domain code and configures framework features. This guide covers domain scaffolding (aggregates, commands, queries, events).
Other CLI guides:
- Feature Commands - Add authentication, multi-tenancy, Radzen UI, and modules
- Email & SMS Helpers - Add transactional email and SMS capabilities
Installation
The CLI is pinned to a specific version in your project via .config/dotnet-tools.json.
Restore it:
dotnet tool restore
Verify:
dotnet tool run peardrop --version
Or add to PATH:
# Windows - run once
$env:PATH += ";$(dotnet tool list -g | grep peardrop | awk '{print $2}')"
# Then use directly
peardrop --help
Quick Reference
Add an Aggregate
peardrop add aggregate Equipment \
--properties "Name:string,IsAvailable:bool,Category:string"
Creates:
Infrastructure/Domain/EquipmentAggregate/AggregateRoot/EquipmentAggregate.csInfrastructure/Domain/EquipmentAggregate/folder structure
Add a Command
# Simple command
peardrop add command CreateEquipment \
--aggregate Equipment \
--properties "Name:string,Category:string"
# Command with return type
peardrop add command ApproveCheckout \
--aggregate CheckoutRequest \
--properties "CheckoutId:Guid" \
--result CheckoutApprovedResult
Creates:
Infrastructure/Domain/EquipmentAggregate/Commands/CreateEquipmentCommand.csInfrastructure/Domain/EquipmentAggregate/CommandHandlers/CreateEquipmentCommandHandler.cs
Add a Query
# Query returning single item
peardrop add query GetEquipmentById \
--aggregate Equipment \
--properties "EquipmentId:Guid" \
--result EquipmentDetailDto
# Query returning list
peardrop add query ListEquipment \
--aggregate Equipment \
--result "List<EquipmentSummaryDto>"
Creates:
Queries/GetEquipmentByIdQuery.csQueries/QueryHandlers/GetEquipmentByIdQueryHandler.cs
Add an Integration Event
# Event with handler
peardrop add integration-event EquipmentCreated \
--aggregate Equipment \
--properties "EquipmentId:Guid,Name:string"
# Event without handler
peardrop add integration-event EquipmentDeleted \
--aggregate Equipment \
--no-handler
# Event with cross-library handler
peardrop add integration-event CheckoutRequested \
--aggregate Checkout \
--handler-project ../Notification.Module
Creates:
Infrastructure/Domain/EquipmentAggregate/IntegrationEvents/EquipmentCreatedIntegrationEvent.csInfrastructure/EventHandlers/EquipmentCreatedSubscriber.cs(if handler enabled)- Auto-updates
Constants/{ProjectName}CapTopics.cs
Typical Feature Workflow
Step 1: Plan
Identify what you need:
- Data to store (aggregate)
- Operations that change it (commands)
- Data users read (queries)
- UI pages (components)
Example: Equipment Checkout System
- Aggregate:
Equipment,CheckoutRequest - Commands:
CreateEquipment,RequestCheckout,ApproveCheckout,ReturnEquipment - Queries:
GetEquipmentById,ListAvailableEquipment,ListMyCheckouts - Components:
EquipmentList,EquipmentDetail,CheckoutForm,MyCheckouts
Step 2: Create Aggregates
# Create the aggregates
peardrop add aggregate Equipment \
--properties "Name:string,Description:string,Category:string,IsAvailable:bool"
peardrop add aggregate CheckoutRequest \
--properties "EquipmentId:Guid,UserId:Guid,RequestedDate:DateTime,Status:CheckoutStatus"
Now:
- ✅ Open
EquipmentAggregate.csand add business logic - ✅ Open
CheckoutRequestAggregate.csand add validation rules - No need to create files manually!
Step 3: Create Commands
# Write side - what changes the data
peardrop add command CreateEquipment \
--aggregate Equipment \
--properties "Name:string,Description:string,Category:string"
peardrop add command RequestCheckout \
--aggregate CheckoutRequest \
--properties "EquipmentId:Guid,RequiredDate:DateTime"
peardrop add command ApproveCheckout \
--aggregate CheckoutRequest \
--properties "CheckoutRequestId:Guid"
peardrop add command ReturnEquipment \
--aggregate CheckoutRequest \
--properties "CheckoutRequestId:Guid"
Now:
- ✅ Open each command handler
- ✅ Implement validation logic
- ✅ Call aggregate methods
- ✅ Run migrations:
dotnet ef migrations add CreateEquipmentAndCheckoutAggregates - ✅ Apply:
peardrop migrate
Step 4: Create Queries
# Read side - what users view
peardrop add query GetEquipmentById \
--aggregate Equipment \
--properties "EquipmentId:Guid" \
--result EquipmentDetailDto
peardrop add query ListEquipment \
--aggregate Equipment \
--result "List<EquipmentSummaryDto>"
peardrop add query ListMyCheckouts \
--aggregate CheckoutRequest \
--result "List<MyCheckoutDto>"
Now:
- ✅ Implement query handlers
- ✅ Create read models for optimal queries
- ✅ Add filters, pagination, etc.
Step 5: Create Components
# UI Components (not scaffolded by CLI)
# Create manually in Components/ folder
Reference: Project Structure
Step 6: Integration Events (Optional)
For cross-module communication:
peardrop add integration-event EquipmentCreated \
--aggregate Equipment \
--properties "EquipmentId:Guid,Name:string,Category:string"
peardrop add integration-event CheckoutApproved \
--aggregate CheckoutRequest \
--properties "CheckoutId:Guid,EquipmentId:Guid,UserId:Guid"
Property Types
Supported types in --properties:
| Type | Example |
|---|---|
string | Name:string |
int | Quantity:int |
long | FileSize:long |
decimal | Price:decimal |
bool | IsActive:bool |
DateTime | CreatedAt:DateTime |
DateOnly | DateRequested:DateOnly |
Guid | EquipmentId:Guid |
Enum | Status:CheckoutStatus |
List<T> | Tags:List<string> |
Options
| Option | Purpose | Example |
|---|---|---|
--aggregate | Which aggregate | --aggregate Equipment |
--properties | Properties to add | --properties "Name:string,Count:int" |
--result | Query/Command return type | --result EquipmentDetailDto |
--no-handler | Integration events only (no subscriber) | --no-handler |
--handler-project | Cross-module event handler | --handler-project ../Notification.Module |
After Running CLI Commands
After the CLI generates files, you typically:
- Edit the aggregate - Add business logic and validation
- Edit the command handler - Add validation and domain logic
- Edit the query handler - Create read models and projections
- Create read models - For optimal query performance
- Run migrations -
dotnet ef migrations add <Name> - Update database -
peardrop migrate
Example:
# 1. Generate
peardrop add command CreateEquipment --aggregate Equipment --properties "Name:string"
# 2. Edit handler file and add logic
# ... edit CreateEquipmentCommandHandler.cs ...
# 3. Create/update migrations
dotnet ef migrations add AddEquipment
# 4. Apply to DB
peardrop migrate
# 5. Test it!
Troubleshooting
"peardrop: command not found"
- Run:
dotnet tool restore - Or use:
dotnet tool run peardrop
"Invalid property type"
- Check Property Types above
- Use quotes:
"Tags:List<string>"
"Aggregate not found"
- Run:
dotnet buildfirst - Aggregate must exist before creating commands/queries
CLI generates wrong names
- Check
peardrop.jsonin project root - Verify
projectName,appProject,clientProject
Best Practices
✅ DO:
- Plan aggregates first (don't randomly create)
- Group related aggregates in bounded context folders
- Run migrations after each aggregate
- Name commands specifically (CreateEquipment not Update)
- Use result types for commands (clear intent)
❌ DON'T:
- Create random properties you won't use
- Skip implementing aggregate logic
- Mix multiple aggregates in one command
- Forget to run
dotnet ef migrations add - Leave handlers empty (implement the logic!)
Next Steps
- Your First Feature - Full walkthrough with CLI
- Project Structure - Understanding generated files
- CQRS Operations - Patterns for using commands & queries