Best Practices
Follow these recommendations for building reliable, maintainable nopCommerce plugins.
Code Organization
Follow Naming Conventions
Nop.Plugin.{Type}.{Name}
├── Domain/ # Entities
├── Services/ # Business logic
├── Controllers/ # MVC controllers
├── Models/ # View models
├── Views/ # Razor views
├── Infrastructure/ # Startup, routes
└── Data/ # Database mappingsUse Interfaces
csharp
// ✅ Good - depend on abstraction
public class MyController
{
private readonly IMyService _myService;
}
// ❌ Bad - depend on implementation
public class MyController
{
private readonly MyService _myService;
}Use Region Blocks
Organize class members using #region blocks for better readability and maintainability:
csharp
public class MyService : IMyService
{
#region Fields
private readonly IRepository<MyEntity> _repository;
private readonly IStaticCacheManager _cacheManager;
#endregion
#region Ctor
public MyService(
IRepository<MyEntity> repository,
IStaticCacheManager cacheManager)
{
_repository = repository;
_cacheManager = cacheManager;
}
#endregion
#region Methods
public async Task<IList<MyDto>> GetAllAsync()
{
// Implementation
}
#endregion
#region Utilities
private MyDto MapToDto(MyEntity entity) => new()
{
Id = entity.Id
};
#endregion
}Standard region order:
#region Fields- Private readonly fields#region Ctor- Constructor(s)#region Properties- Public properties (if any)#region Methods- Public methods#region Utilities- Private helper methods
Performance
Use Caching
csharp
return await _cacheManager.GetAsync(cacheKey, async () =>
{
return await _repository.GetAllAsync(query => query);
});Use Paging
csharp
// ✅ Good - paged results
var items = await _repository.GetAllPagedAsync(q => q, pageIndex, pageSize);
// ❌ Bad - load everything
var items = await _repository.GetAllAsync(q => q);Use Async/Await
csharp
// ✅ Good
public async Task<IList<Item>> GetItemsAsync()
{
return await _repository.GetAllAsync(q => q);
}Error Handling
csharp
public async Task ProcessAsync()
{
try
{
await DoWorkAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "Processing failed");
throw; // Re-throw for proper error handling
}
}Testing
- Write unit tests for services
- Test with different store configurations
- Verify install/uninstall works cleanly
- Test with multiple languages
Security
- Validate all user input
- Use
[AuthorizeAdmin]on admin controllers - Sanitize output to prevent XSS
- Use antiforgery tokens on forms
Checklist
- [ ] Uses dependency injection properly
- [ ] Has proper error handling and logging
- [ ] Cleans up on uninstall
- [ ] Has localization resources
- [ ] Uses caching where appropriate
- [ ] Follows naming conventions
- [ ] Has configuration documentation