Verbs Over Nouns
The Conventional Approach: Nouns#
Most object-oriented design organizes code around nouns—things:
class User {
updateProfile(newData: ProfileData): void {
// Update user profile logic
}
addCreditCard(card: CreditCard): void {
// Add credit card logic
}
updatePreferences(prefs: UserPreferences): void {
// Update preferences logic
}
addAddress(address: Address): void {
// Add address logic
}
// Many more methods...
}
We create a User class and bundle all operations that relate to users into it. We think in nouns: “User,” “Order,” “Product.”
This approach has consequences. The User class becomes enormous. It has dozens of methods doing different things. Changes for different reasons are bundled together. Testing requires understanding the entire class.
The Alternative: Verbs#
Instead of organizing around nouns (things), organize around verbs (behaviors):
// Command - One way to update profile
class UpdateUserProfile {
execute(command: UpdateUserProfileCommand): void {
// Profile update logic
}
}
// Command - One way to add credit card
class AddUserCreditCard {
execute(command: AddCreditCardCommand): void {
// Credit card validation and addition logic
}
}
// Command - One way to update preferences
class UpdateUserPreferences {
execute(command: UpdateUserPreferencesCommand): void {
// Preference update logic
}
}
Each action is its own class. Each class has one reason to change. Each class is small and focused.
A Practical Example#
Consider a user management system built two ways:
Noun-Based Organization#
User (massive class with everything)
├─ updateProfile()
├─ addCreditCard()
├─ removeCreditCard()
├─ updatePreferences()
├─ changePassword()
├─ deactivateAccount()
└─ ... many more methods
Verb-Based Organization#
UpdateUserProfile
└─ execute(command): void
AddUserCreditCard
└─ execute(command): void
RemoveUserCreditCard
└─ execute(command): void
UpdateUserPreferences
└─ execute(command): void
ChangeUserPassword
└─ execute(command): void
DeactivateUserAccount
└─ execute(command): void
Why Verbs Create Better Architecture#
1. Single Responsibility#
Each verb class has exactly one reason to change. When password validation rules change, you modify ChangeUserPassword. When credit card processing changes, you modify AddUserCreditCard. These are independent changes.
2. Parallel Development#
Different team members can work on different verbs without conflicts. One developer works on UpdateUserProfile, another on AddUserCreditCard. No merge conflicts.
3. Testing#
Each verb class is small and testable. Setting up the test is straightforward—just the context needed for that one operation. No needing to understand twenty other methods.
4. Discoverability#
Want to know what actions a user can perform? Look at the commands. Want to know how profile updates work? Look at UpdateUserProfile. The code structure mirrors the business actions.
5. Domain Alignment#
Business thinks in verbs: “Customer adds a credit card,” “User updates preferences.” Code structure matches business thinking.
6. Scalability#
As the system grows, verb-based organization scales. You add new verbs for new actions. Noun-based organization makes existing classes grow larger and more complex.
Queries Complement Commands#
Verbs extend beyond just mutations. Read operations are verbs too:
// Commands - One way to modify user state
class UpdateUserProfile {
execute(command: UpdateUserProfileCommand): void { }
}
// Queries - Many ways to view user data
class GetUserCreditCardList {
execute(userId: string): GetUserCardListingResponse[] {
// Returns list of cards with basic info
return [];
}
}
class GetUserCreditCardForPayment {
execute(userId: string, cardId: string): GetPaymentMethodDetailsResponse {
// Returns card details formatted for payment processing
return {};
}
}
class GetUserCreditCardForAdmin {
execute(userId: string, cardId: string): GetCreditCardForAdminResponse {
// Returns full card details with audit history
return {};
}
}
Notice that there’s one way to add a credit card but multiple ways to retrieve it. Commands are singular because there’s typically one way to change state. Queries are plural because there are many ways to view data depending on context.
Code Organization Structure#
With verb-based organization, your file structure reflects actions:
src/
users/
commands/
UpdateUserProfile.ts
AddUserCreditCard.ts
ChangeUserPassword.ts
queries/
GetUserProfile.ts
GetUserCreditCards.ts
ListUsers.ts
domain/
User.ts
tests/
UpdateUserProfile.test.ts
AddUserCreditCard.test.ts
Commands and queries are at the top level. The domain model is minimal. Tests correspond to commands.
Common Misconception: Command Query Responsibility Segregation (CQRS)#
Verb-based organization naturally leads to CQRS:
- Commands modify state (verbs that change things)
- Queries read state (verbs that retrieve things)
Separating commands from queries aligns with how the system actually works. Modifications are constrained and validated. Reads are optimized independently.
You don’t need a full CQRS framework to benefit from verb thinking. Even basic separation of commands and queries improves architecture.
Moving From Nouns to Verbs#
Migration from noun-based to verb-based organization doesn’t require rewriting everything:
Step 1: Identify New Operations as Verb Classes#
When adding new functionality, create a verb class instead of adding a method to an existing noun class.
// OLD: Would add to User class
// user.verifyEmail(email);
// NEW: Create verb class
class VerifyUserEmail {
execute(command: VerifyUserEmailCommand): void { }
}
Step 2: Gradually Extract Existing Methods#
As existing methods need modification, extract them into verb classes:
// OLD User class becomes smaller as methods are extracted
class User {
// Fewer methods as they move to verb classes
}
Step 3: Leave the Domain Model#
Keep a minimal domain model for shared data:
class User {
private id: string;
private email: string;
private preferences: UserPreferences;
// Only data and basic getters, no behavior
}
Real-World Example: Account Deletion#
Consider deleting an account. Noun-based approach:
class User {
deleteAccount(): void {
// Validate account can be deleted
// Cancel active subscriptions
// Refund pending charges
// Archive user data
// Notify user
// Delete from database
}
}
The User class grows with this responsibility. When requirements change (new validation, different notification), the class changes again.
Verb-based approach:
class DeleteUserAccount {
constructor(
private subscriptionService: SubscriptionService,
private refundService: RefundService,
private archiveService: ArchiveService,
private notificationService: NotificationService,
private userRepository: UserRepository,
) {}
async execute(command: DeleteUserAccountCommand): Promise<void> {
// Validate
// Cancel subscriptions
// Refund
// Archive
// Notify
// Delete
}
}
This class has one reason to change: account deletion logic changes. Each responsibility is injected and can be tested independently.
File Structure: Directory-Based Organization#
accounts/
_source/
queries/
commands/
domain/
infrastructure/
__tests__/
Each noun domain (accounts, orders, products) is a folder. Within each folder:
queries/— Read operationscommands/— Modification operationsdomain/— Core domain modelinfrastructure/— Data access, external services__tests__/— Tests for the domain
This structure makes navigation obvious and reflects business structure.
Benefits Accumulate#
The benefits of verb-based organization compound:
- Code is smaller → Easier to understand
- Easier to understand → Easier to change
- Easier to change → More rapid feature delivery
- Easier to test → More confidence in changes
- More confidence → More willingness to improve code
When Nouns Make Sense#
Noun-based organization isn’t always wrong:
- Very simple objects with few operations
- Objects that naturally group operations (Math, StringUtils)
- Legacy systems where refactoring would be expensive
But for business domain models with many operations, verbs create better architecture.
Conclusion#
The difference between noun-based and verb-based organization is profound:
Nouns bundle operations by data type. Verbs organize by business action.
When you need to add new functionality, which approach is easier?
With nouns, you modify an existing class that might be large and complex. With verbs, you create a new, focused class.
Which approach scales better as systems grow?
With nouns, classes grow larger. With verbs, you add new classes.
Which approach aligns better with how the business thinks?
With nouns, you force business language into object structure. With verbs, code structure mirrors business actions.
Consider organizing your next system around verbs. Model the business by representing actions explicitly. You’ll likely find it creates architecture that’s easier to build, test, and maintain.