CommandResult
When a command is executed, it returns a CommandResult<TResponse> that provides comprehensive information about the execution outcome. The result includes success/failure status, validation errors, authorization status, exceptions, and optional response data.
Structure
The CommandResult interface provides the following properties:
interface ICommandResult<TResponse = object> {
readonly correlationId: Guid;
readonly isSuccess: boolean;
readonly isAuthorized: boolean;
readonly isValid: boolean;
readonly hasExceptions: boolean;
readonly validationResults: ValidationResult[];
readonly exceptionMessages: string[];
readonly exceptionStackTrace: string;
readonly response?: TResponse;
}
Status Properties
Understanding the different status properties is crucial for proper error handling:
isSuccess
Indicates whether the command executed successfully overall. This is the primary indicator of whether the operation completed as intended.
true: The command executed without any issues (authorized, valid, and no exceptions)false: The command failed for one or more reasons (could be authorization, validation, or exceptions)
Use this when:
- You want to know if the overall operation succeeded
- You need a single boolean check for success/failure
- You're implementing simple success/failure UI feedback
const result = await command.execute();
if (result.isSuccess) {
// Show success message
} else {
// Handle failure (check other properties for details)
}
isAuthorized
Indicates whether the user is authorized to execute this command.
true: The user has permission to execute the commandfalse: The user lacks the necessary permissions (HTTP 401/403)
Use this when:
- You need to distinguish authorization failures from other errors
- You want to show specific "access denied" messages
- You need to redirect users to login or show permission requests
const result = await command.execute();
if (!result.isAuthorized) {
// Show "Access Denied" message
// Redirect to login or request permissions
}
isValid
Indicates whether the command payload passed validation rules.
true: All validation rules passedfalse: One or more validation rules failed (checkvalidationResultsfor details)
Use this when:
- You need to display validation errors to users
- You want to highlight invalid form fields
- You're implementing client-side validation feedback
const result = await command.execute();
if (!result.isValid) {
// Display validation errors from result.validationResults
result.validationResults.forEach(error => {
console.log(`${error.members.join('.')}: ${error.message}`);
});
}
hasExceptions
Indicates whether any exceptions occurred during command execution.
true: One or more exceptions were thrownfalse: No exceptions occurred
Use this when:
- You need to catch unexpected server errors
- You want to log errors for debugging
- You need to show generic error messages for system failures
const result = await command.execute();
if (result.hasExceptions) {
// Log exception details
console.error('Exceptions:', result.exceptionMessages);
console.error('Stack trace:', result.exceptionStackTrace);
// Show user-friendly error message
showErrorMessage('An unexpected error occurred. Please try again.');
}
Understanding the Relationship
The status properties work together to provide a complete picture:
// Scenario 1: Complete success
// isSuccess = true, isAuthorized = true, isValid = true, hasExceptions = false
// Scenario 2: Authorization failure
// isSuccess = false, isAuthorized = false, isValid = true, hasExceptions = false
// Scenario 3: Validation failure
// isSuccess = false, isAuthorized = true, isValid = false, hasExceptions = false
// Scenario 4: Exception occurred
// isSuccess = false, isAuthorized = true, isValid = true, hasExceptions = true
// Scenario 5: Multiple failures (validation + exception)
// isSuccess = false, isAuthorized = true, isValid = false, hasExceptions = true
Key principle: If isSuccess is false, check the other properties to determine why it failed.
Accessing Data
Response Data
When the command returns data, access it through the response property:
interface CreateAccountResponse {
accountId: string;
accountNumber: string;
}
const result = await createAccountCommand.execute();
if (result.isSuccess && result.response) {
const { accountId, accountNumber } = result.response;
console.log(`Account created: ${accountNumber}`);
}
The response is typed according to the command's generic parameter, providing full type safety.
Validation Results
Access detailed validation errors through the validationResults array:
const result = await command.execute();
if (!result.isValid) {
result.validationResults.forEach(error => {
console.log(`Severity: ${error.severity}`);
console.log(`Message: ${error.message}`);
console.log(`Members: ${error.members.join('.')}`);
console.log(`State: ${JSON.stringify(error.state)}`);
});
}
Each ValidationResult contains:
severity: The severity level of the validation errormessage: Human-readable error messagemembers: Array of property names that failed validationstate: Additional context about the validation failure
Exception Details
Access exception information when hasExceptions is true:
const result = await command.execute();
if (result.hasExceptions) {
// Array of exception messages
result.exceptionMessages.forEach(msg => {
console.error('Exception:', msg);
});
// Full stack trace for debugging
console.error('Stack trace:', result.exceptionStackTrace);
}
Chaining Callbacks
CommandResult supports a fluent API for handling different outcomes:
const result = await command.execute();
result
.onSuccess((response) => {
console.log('Success!', response);
})
.onFailed((commandResult) => {
console.log('Failed:', commandResult);
})
.onUnauthorized(() => {
console.log('Not authorized');
})
.onValidationFailure((validationResults) => {
console.log('Validation errors:', validationResults);
})
.onException((messages, stackTrace) => {
console.error('Exception:', messages, stackTrace);
});
Each callback method returns the CommandResult instance, allowing you to chain multiple handlers.
Best Practices
1. Check isSuccess First
Always start by checking isSuccess for the overall outcome:
const result = await command.execute();
if (result.isSuccess) {
// Handle success
} else {
// Check specific failure reasons
if (!result.isAuthorized) { /* ... */ }
if (!result.isValid) { /* ... */ }
if (result.hasExceptions) { /* ... */ }
}
2. Provide Specific Error Messages
Use the specific status properties to give users meaningful feedback:
const result = await command.execute();
if (!result.isSuccess) {
if (!result.isAuthorized) {
showMessage('You do not have permission to perform this action.');
} else if (!result.isValid) {
showValidationErrors(result.validationResults);
} else if (result.hasExceptions) {
showMessage('An unexpected error occurred. Please contact support.');
}
}
3. Log Exceptions for Debugging
Always log exception details when hasExceptions is true:
if (result.hasExceptions) {
logger.error('Command execution failed', {
messages: result.exceptionMessages,
stackTrace: result.exceptionStackTrace,
correlationId: result.correlationId
});
}
4. Use Correlation ID for Tracking
The correlationId helps track command executions across the system:
console.log(`Command executed with correlation ID: ${result.correlationId}`);
This is especially useful for debugging and support scenarios.
Related Topics
- Commands - Core command concepts and usage
- React Commands - Using commands in React components
- Validation - Understanding validation rules