Skip to content

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 mappings

Use 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:

  1. #region Fields - Private readonly fields
  2. #region Ctor - Constructor(s)
  3. #region Properties - Public properties (if any)
  4. #region Methods - Public methods
  5. #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

Released under the nopCommerce Public License.