Skip to content

Create a new module from Scratch

How to create a new module from scratch using Visual Studio? Follow the Dummy module example.

1. Create solution and projects in correct structure

  1. Create empty DummyModule solution: https://learn.microsoft.com/en-us/visualstudio/get-started/tutorial-projects-solutions?view=vs-2022##create-a-solution
  2. Add src and tests Solution Folders (using Visual Studio)
  3. In src add project using "Class library (.NET Core)" template:
  4. Project name: DummyModule.Core
  5. Location: ...\DummyModule\src folder (create the missing src folder).
  6. Add DummyModule.Data project in the same way, ensure that it located under src folder.
  7. Add DummyModule.Web using "ASP.NET Core Web Application" Empty template, ensure that it located under src folder.
  8. Delete the auto-generated Class1.cs from all projects.
  9. In tests folder add a project using "xUnit Test Project (.NET Core)" template:
  10. Project name: DummyModule.Tests
  11. Location: ...\DummyModule\tests folder (create the missing tests folder).
  12. Set "Target framework" to ".NET Core 3.1" for all 4 projects.
  13. References to projects:
  14. DummyModule.Data: add reference to DummyModule.Core project
  15. DummyModule.Web: add references to DummyModule.Core, DummyModule.Data projects
  16. DummyModule.Tests: add references to DummyModule.Core, DummyModule.Data, DummyModule.Web projects
  17. References to NuGet packages:
    1. DummyModule.Core: add reference to the latest version VirtoCommerce.Platform.Core package.
    2. DummyModule.Data: add reference to the latest version VirtoCommerce.Platform.Data package.
  18. (Outdated) References to NuGet packages in VirtoCommerce.Platform.Web:
    1. (Double click in Visual Studio to) open DummyModule.Web.csproj file for editing;
    2. Add new ItemGroup:
      <ItemGroup>
          <PackageReference Include="Microsoft.AspNetCore.App" />
      </ItemGroup>
      
  19. Compile the solution (there should be no warnings).

2. Fill DummyModule.Core project

  1. Add class ModuleConstants.cs for module constants.
  2. Inside ModuleConstants add sub-classes Security and Permissions like this:
            public static class Security
            {
                public static class Permissions
                {
                    public const string Access = "dummy:access";
                    public const string Read =   "dummy:read";
                    public const string Create = "dummy:create";
                    public const string Update = "dummy:update";
                    public const string Delete = "dummy:delete";
    
                    public static string[] AllPermissions = { Access, Read, Create, Update, Delete };
                }
            }
    
  3. Add sub-classes Settings and General containing settings' definitions of type SettingDescriptor, e.g., for other constants.
            public static SettingDescriptor DummyInteger = new SettingDescriptor
            {
                Name = "Dummy.Integer",
                GroupName = "Dummy|General",
                ValueType = SettingValueType.Integer,
                DefaultValue = 50
            };
    

Tip

Check sample ModuleConstants.cs for complete code.

  1. If there is any domain models in your module, add Models folder. All domain models should be defined in Models folder.
  2. If there is any need for search services, add Search sub-folder. Add all SearchCriteria and SearchResult classes there.
  3. Services folder: in order to use the created models, create interfaces of the required services. Typically, interfaces containing CRUD and search operations are defined.
  4. Events folder: add model-related changing/changed events. Derive from the base GenericChangedEntryEvent class.
  5. Notifications folder: define new types of Notifications, that your module would expose. Each class should inherit from EmailNotification/SmsNotification or own class, based on Notification.

3. Fill DummyModule.Data project

  1. If there is any (domain) data to be persisted, add Models folder. All persistency related models should be defined in Models folder. Typically, each of the classes under this folder would have a corresponding class in DummyModule.Core/Models. The class should:
  2. define the DB table structure and restrictions;
  3. be able to convert from/to the corresponding Core model class.
  4. Add Repositories folder. Under it:
    1. Add class DummyDbContext, deriving from DbContextWithTriggers. Check and copy class contents from sample DummyDbContext.cs.
    2. Add class DesignTimeDbContextFactory. Check and copy class contents from sample DesignTimeDbContextFactory.cs. Double-check that connection to your development SQL server is correct.
    3. Add data repository abstraction (interface), deriving from IRepository for the defined persistency model.
    4. Add the repository implementation class for the previously defined interface and deriving from DbContextRepositoryBase<DummyDbContext>.
  5. Generate code-first migration:
  6. Execute "Set as Startup Project" on CustomerReviews.Data project in Solution Explorer
  7. Open NuGet Package Manager Console
  8. Select "src\DummyModule.Data" as "Default project"
  9. Run command:
        Add-Migration Initial -Verbose
    

A new EF migration gets generated. Read more details on extending an existing module's model: How to extend the DB model of VC module.

  1. Caching folder: add it, if data caching should be used. This folder is for the cache region classes. Typically, each model should have its own region. Derive CacheRegion from generic CancellableCacheRegion<T> class e.g., public class StoreCacheRegion : CancellableCacheRegion<StoreCacheRegion>.
  2. Services folder: add implementations of the interfaces that were defined in the .Core project.
  3. ExportImport folder: add class for data export/import. It should be called from Module.cs and contain implementation for module data export and import.
  4. Handlers folder: add handlers for the domain events, which were defined under .Core/Events.

4. Fill DummyModule.Web project

Typical structure of .Web project is:

  • Controllers/Api - API controllers;
  • Localizations - translation resources for UI;
  • Scripts - AngularJS module for extending Platform Manager UI;
  • Module.cs - the entry point for module managed code;
  • module.manifest - the main file of a module, the module definition.

Project items to add:

  1. module.manifest (xml file)
  2. Identifier - a module identifier, each modules' identifier should be unique:
    <id>VirtoCommerce.Dummy</id>
    
  3. Versioning - actual version and (optional) prerelease version tag of a module:
    <version>1.0.0</version>
    <version-tag>v1</version-tag>
    
  4. Required minimal version of VC Platform:
    <platformVersion>3.0.0</platformVersion>
    
  5. Module dependencies - list of modules (with versions), functions of which will be used in the new module:
    <dependencies>
        <dependency id="VirtoCommerce.Core" version="3.0.0" />
    </dependencies>
    
  6. Title and description - name and description of a new module that will be displayed in the Platform Manager and Swagger UI:
    <title>Dummy module</title>
    <description>Sample module for training purposes.</description>
    
  7. Authors - list of the developers, who wrote the module:
    <authors>
        <author>Andrei Kostyrin</author>
        <author>Egidijus Mazeika</author>
    </authors>
    
  8. Owners - list of the module owners:
    <owners>
        <owner>Virto Commerce</owner>
    </owners>
    
  9. Module groups - optional, logical grouping of the modules:
    <groups>
        <group>samples</group>
    </groups>
    
  10. ProjectUrl - optional URL to get more details on the module:
    <projectUrl>https://github.com/VirtoCommerce/vc-samples</projectUrl>
    
  11. IconUrl - optional icon for the module to display in Platform's module management UI:
    <iconUrl></iconUrl>
    
  12. ReleaseNotes, copyright, tags - optional, additional information for the module:
    <releaseNotes>First version.</releaseNotes>
    <copyright>Copyright &copy; 2011-2020 Virto Commerce. All rights reserved</copyright>
    <tags>sample</tags>
    
  13. AssemblyFile and ModuleType - information for the module's managed code library loading into VC Platform:

    <assemblyFile>DummyModule.Web.dll</assemblyFile>
    <moduleType>DummyModule.Web.Module, DummyModule.Web</moduleType>
    

  14. JsonConverters folder: add it, if polymorphic data should be accepted by the API methods and deserialized correctly. This folder should contain converter(s) for populating the JSON values onto the target object.

  15. Module.cs class: the main entry point for module managed code, containing database initialization, registration of new repositories, services, model types and overrides:

    1. Implement VirtoCommerce.Platform.Core.Modularity.IModule interface like this:
      public class Module : IModule
      {
          public ManifestModuleInfo ModuleInfo { get; set; }
      
          public void Initialize(IServiceCollection serviceCollection)
          {
              // database initialization
              var configuration = serviceCollection.BuildServiceProvider().GetRequiredService<IConfiguration>();
              var connectionString = configuration.GetConnectionString("VirtoCommerce.Dummy") ?? configuration.GetConnectionString("VirtoCommerce");
              serviceCollection.AddDbContext<DummyDbContext>(options => options.UseSqlServer(connectionString));
          }
      
          public void PostInitialize(IApplicationBuilder appBuilder)
          {
              // register settings
              var settingsRegistrar = appBuilder.ApplicationServices.GetRequiredService<ISettingsRegistrar>();
              settingsRegistrar.RegisterSettings(ModuleConstants.Settings.AllSettings, ModuleInfo.Id);
      
              // register permissions
              var permissionsProvider = appBuilder.ApplicationServices.GetRequiredService<IPermissionsRegistrar>();
              permissionsProvider.RegisterPermissions(ModuleConstants.Security.Permissions.AllPermissions.Select(x =>
                  new Permission() { GroupName = "Dummy", Name = x }).ToArray());
      
              // Ensure that any pending migrations are applied
              using (var serviceScope = appBuilder.ApplicationServices.CreateScope())
              {
                  using (var dbContext = serviceScope.ServiceProvider.GetRequiredService<DummyDbContext>())
                  {
                      dbContext.Database.EnsureCreated();
                      dbContext.Database.Migrate();
                  }
              }
          }
      
          public void Uninstall()
          {
              // do nothing in here
          }
      }
      
  16. If the module should support data export/import, implement IExportSupport and IImportSupport interfaces accordingly. Call the methods of the class(es), that were defined in .Data/ExportImport folder.

  17. Add Controllers/Api folder, if any API endpoints need to be exposed. Add API Controller(s), derived from Microsoft.AspNetCore.Mvc.Controller. Sample ASP.NET MVC endpoint:
        [HttpGet]
        [Route("getnew")]
        [Authorize(ModuleConstants.Security.Permissions.Create)]
        public ActionResult<DummyModel> GetNewDummyModel()
    

If the endpoint should have a restricted access, an Authorize attribute with the required permission should be provided. Use the ModuleConstants class, which was previously defined in Dummy.Core project.

  1. Optional Content folder: add module icon, stylesheets or any other optional content.

  2. Optional Scripts folder: add module.js and any required subfolders: blades, resources, widgets, etc. Sample module.js file:

    //Call this to register our module to main application
    var moduleName = "virtoCommerce.dummy";
    
    if (AppDependencies !== undefined) {
        AppDependencies.push(moduleName);
    }
    
    angular.module(moduleName, []);
    

  3. If there are any JavaScript or stylesheet files in the project:

    1. Copy package.json from sample package.json;
    2. Copy webpack.config.js from sample webpack.config.js;
    3. Change the namespace on line 36 in webpack.config.js to be equal to module's identifier:
      namespace: 'VirtoCommerce.Dummy'
      
    4. Open command prompt and navigate to DummyModule.Web folder:
      1. Run npm install
      2. Run npm run webpack:dev
  4. Localizations folder. Add en.VirtoCommerce.Dummy.json file:

    {
        "permissions": {
            "dummy:access": "Open Dummy menu",
            "dummy:create": "Create Dummy related data",
            "dummy:read": "View Dummy related data",
            "dummy:update": "Update Dummy related data",
            "dummy:delete": "Delete Dummy related data"
        },
        "settings": {
            "Dummy": {
                "ShortText": {
                    "title": "Dummy dictionary",
                    "description": "ShortText dummy dictionary setting"
                },
                "Integer": {
                    "title": "Dummy integer",
                    "description": "Dummy integer setting"
                },
                "DateTime": {
                    "title": "Dummy DateTime",
                    "description": "Dummy DateTime setting"
                }
            }
        }
    }
    

5. Fill DummyModule.Tests project

  1. UnitTests folder: add unit tests here.
  2. IntegrationTests folder: add integration tests here. Ensure, that each integration tests class is marked with Trait attribute:
    [Trait("Category", "IntegrationTest")]
    

6. Create module package

Please read the article.


Last update: March 23, 2023