Hi ABP Support Team,
We have an ABP Commercial project A (our main product) that is already live and running in production.
Now we have a new requirement for a subset of tenants: we want to provide CMS functionality (CMS Kit + Public Website) for those tenants.
To explore this, we created a separate ABP project B using the ABP template with CMS Kit and Public Website enabled, and we’ve already done some customizations in project B.
At this point, we want to bring this CMS capability into our main project A, and we are considering two approaches:
Option 1: Convert project B into a module and integrate it into project A
Make B a reusable module and then include it in A.
Option 2: Install CMS Kit + Public Website into project A directly, and migrate our customizations from project B
We need guidance on:
- Which option is the recommended approach for long-term maintainability and upgrade safety (especially since A is already live)?
- If option 1 is feasible, what is the recommended way to extract what we built in B (CMS + Public Website customizations) into a proper ABP module and integrate it into A (projects to reference, DependsOn setup, migrations, UI packages, etc.)?
- If option 2 is preferred, what is the safest migration path to move the B customizations into A after installing CMS Kit + Public Website (including any typical pitfalls around global features, migrations, and unified admin/public layout configuration)?
Our goal is to keep one main solution (A), enable CMS features only where needed, and avoid creating an architecture that becomes difficult to maintain across ABP upgrades.
Thanks in advance for your recommendation and the best-practice steps to implement it.
1 Answer(s)
-
0
- Which option is better?
For a long‑lived, upgrade‑friendly Commercial solution, Option 2 is the recommended path:
- Use your existing, already‑running solution A as the main solution.
- Install CMS Kit(.Pro) into A and add a Public Website project into A, then
- Manually merge / move your customizations from B into A.
This aligns with how ABP team advises adding Public Website/CMS Kit to existing apps:
you generate a new solution with Public Website, then move that project into your existing solution rather than trying to “convert” another solution into a module.From an official support answer:
“Create a new blank application (that includespublic websiteproject) with the same options with your existing application.
Move thepublic websiteproject from the newly created solution to your existing solution.”
(support question: “add public website (and CMS kit) to existing application?”)Using a separate “B as a module” works, but:
- you’ll continually fight template drift and module‑solution mismatches,
- upgrades are harder (you must track and manually merge ABP template/public-website changes into B‑module forever).
So, for maintainability and upgrade safety: Option 2.
- If you did Option 1: how to convert B into a module and integrate into A?
This is technically feasible, but more complex/fragile than Option 2.
High‑level steps if you insist on Option 1:
- Refactor B into a proper ABP Application Module
- Ensure B follows the module layering pattern:
B.DomainB.Domain.SharedB.ApplicationB.Application.ContractsB.HttpApiB.HttpApi.Host(optional if you want a separate host)B.Web(for the public website UI)
- Each should have its own
AbpModuleclass, with proper[DependsOn]attributes, e.g.:
- Ensure B follows the module layering pattern:
// B.Domain.Shared [DependsOn( typeof(AbpDddDomainSharedModule), typeof(CmsKitDomainSharedModule) // if you depend on CmsKit )] public class BDomainSharedModule : AbpModule { } // B.Domain [DependsOn( typeof(AbpDddDomainModule), typeof(BDomainSharedModule), typeof(CmsKitDomainModule) )] public class BDomainModule : AbpModule { }- Make sure B doesn’t redefine infrastructure that A already has (e.g., EF Core DbContext, identity, SaaS, etc.).
Database & migrations
- If B currently has its own DbContext & migrations:
- Decide whether you want:
- Shared DbContext with A (recommended for a monolith), or
- Separate schema / connection string.
- Decide whether you want:
- If sharing A’s DbContext:
- Move B’s entity mappings into A’s DbContext project and regenerate migrations there.
- If keeping a separate DbContext:
- Keep B’s
B.EntityFrameworkCorewith its own migrations & connection string.
- Keep B’s
- If B currently has its own DbContext & migrations:
Wire the module into A
- Reference B’s projects from A’s solution (csproj references).
- In A’s startup module(s),
DependsOnthe B modules you need, e.g.:
[DependsOn( typeof(BWebModule), typeof(CmsKitPublicWebModule) // if used )] public class AWebModule : AbpModule { }UI integration
- If B.Web is a full Public Website (layout, theme, CMS pages):
- Treat it as A’s Public Website project:
- Add a project reference from A’s solution.
- Configure routing,
app.UseEndpoints, etc., as in a standard ABP Public Website.
- Treat it as A’s Public Website project:
- Ensure one consistent theme (often LeptonX) and do not duplicate theme modules.
- If B.Web is a full Public Website (layout, theme, CMS pages):
Multi‑tenancy
- If B contains tenant‑aware components, ensure it uses ABP’s multi‑tenancy as in A (same
ICurrentTenant, tenant resolvers, etc.). - Use features / settings to control which tenants see the CMS/public website (see section 3.4 below).
- If B contains tenant‑aware components, ensure it uses ABP’s multi‑tenancy as in A (same
Because this essentially recreates what the ABP “Application Module” template already gives you, and you also must keep up with Public Website template changes, this option is usually not worth the maintenance cost unless you are building a generic module to reuse in multiple, unrelated solutions.
- Option 2 in detail: safest way to install CMS Kit + Public Website into A and migrate customizations
3.1. Add CMS Kit to existing solution A
- Install module via CLI (run in A’s solution root):
# Open-source abp add-module Volo.CmsKit --skip-db-migrations # Commercial (Pro) abp add-module Volo.CmsKit.Pro --skip-db-migrations- Enable CMS global features in
YourProjectNameDomainSharedModule(orGlobalFeatureConfigurator):
GlobalFeatureManager.Instance.Modules.CmsKit(cmsKit => { cmsKit.EnableAll(); }); GlobalFeatureManager.Instance.Modules.CmsKitPro(cmsKitPro => { cmsKitPro.EnableAll(); });Run EF Core migrations (A’s DbContext project):
- Add migration including CMS tables.
- Update DB.
This is the officially documented install path for CMS Kit into an existing solution.
3.2. Add a Public Website project into A
There is no direct “add public website” CLI command to an existing solution. Official guidance:
- Create a new temp solution C with the same options as A but with Public Website enabled:
abp new TempSolution --with-public-website [other-options-matching-A]In solution C, locate the Public Website project (e.g.
TempSolution.Public.Web).Copy that project into A’s solution:
- Add the project folder under A’s
srcdirectory. - Add it to A’s
.sln. - Fix namespaces and project names if needed.
- Add the project folder under A’s
Update references:
- Ensure A’s Public Website project references:
- A’s
Domain.Shared,Application.Contracts,HttpApi.Clientetc., as in C. Volo.CmsKit.Public.Web/Volo.CmsKit.Pro.Public.Web(they should already be referenced from the template).
- A’s
- Ensure A’s
DbMigrator(if used) knows about the CMS migrations (usually automatic if using the unified DbContext).
- Ensure A’s Public Website project references:
This step gives you a canonical, up‑to‑date Public Website layer, exactly as ABP expects, now living inside solution A.
3.3. Migrate customizations from B into A
From your experimental solution B (created with CMS Kit + Public Website):
Identify what you actually changed in B:
- New/changed pages (
Pages/Public/...), view models, components. - Theme customizations (styles, partials, layouts).
- CMS Kit usage: custom menus, widgets, blogs, etc. (often mostly data seeded via UI, not code).
- New/changed pages (
Copy over custom UI code:
- For page customizations in
B.Public.Web:- Move them into corresponding folders in
A.Public.Web:Pages/Public/...Themes/LeptonX/...overrides
- Resolve namespace / module differences (A vs B project names).
- Move them into corresponding folders in
- For CMS‑specific views (blogs, dynamic pages, widgets) you extended:
- Overwrite or extend the relevant Razor Pages in A’s Public Website project.
- For page customizations in
Copy/merge application/domain logic (if any) from B into A:
- Application services, DTOs, domain services used by the public website.
- Add these to A’s Application/Domain projects instead of B’s.
Re‑run and verify migrations if you added new entities for B that need DB tables in A.
Test all flows in A:
- Admin CMS management (blogs, pages, menus, etc.).
- Public website display of your custom pages/features.
- Multi‑tenancy behavior (see 3.4).
This is conceptually the same as “upgrading imported module source code by re‑downloading and merging” that ABP support often suggests for modules: you bring in a fresh template version and then merge your custom changes on top.
3.4. Enable CMS/Public Website only for specific tenants
With everything in a single solution A, you still want only a subset of tenants to have CMS/public web capability.
Use a combination of:
Feature management / settings per tenant
- Define a feature (e.g.
CmsKit.Enabled) or reuse existing module features. - In host UI, enable that feature only for selected tenants.
- In Public Website project and/or CMS menus, check the feature before showing pages or allowing access.
- Define a feature (e.g.
Tenant‑specific DNS + tenant resolvers
- Use domain/subdomain‑based tenant resolution so only tenants that have a configured domain actually hit the public website.
- Tenants without a mapped domain will effectively have no public website.
Permission control
- Grant CMS admin permissions only to tenants that should manage content.
This is aligned with ABP’s feature system + multi‑tenancy model and avoids duplicating application code per tenant.
4. Summary
Recommended:
- Keep A as the only main solution.
- Install CMS Kit(.Pro) into A.
- Add a Public Website project to A by generating a temp solution and moving its public‑web project.
- Merge customizations from B (code only) into A’s projects.
- Use features + tenant configuration to expose CMS/public web only for selected tenants.
Avoid:
- Treating a full solution (B) as a “module” and embedding it inside A.
It goes against ABP’s modularity patterns and complicates upgrades significantly.
- Treating a full solution (B) as a “module” and embedding it inside A.
Sources
- CMS Kit module docs (installation, global features, unified/public/admin packages)
- https://abp.io/docs/latest/modules/cms-kit
- ABP Modularity basics (module structure, DependsOn, module best practices)
- https://abp.io/docs/latest/framework/architecture/modularity/basics
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.