Content Module Backend Documentation
Data model, validation architecture, and cache behavior for IT Mart CMS pages.
Content Module - Backend Documentation
Scope and Boundaries
The Content module owns:
- CMS page metadata:
pageKey, title, optionalseoId - CMS section payload CRUD
- page-to-section compatibility checks
- page-aware payload validation
- public read projection with enabled sections only
- Redis read caching and write-triggered cache invalidation
The Content module does not own listing data for products, categories, brands, reviews, care packages, FAQs, or articles. Those remain in their existing feature modules and are composed by the frontend.
Module Composition
ContentModule composes:
ContentAdminModuleContentCustomerModule
ContentSharedModule provides and exports ContentService.
Route ownership:
- Admin routes live under
admin/content/pages - Public routes live under
content/pages - Mobile routes are composed by
MobileModuleundermobile/content/pages
Database Model
No schema migration is needed for the IT Mart key update. Both key columns are varchar values, not database enums.
content_page
| Column | Purpose |
|---|---|
id | serial primary key |
page_key | unique CMS page key |
title | admin-facing page title |
seo_id | optional FK to seo.id, ON DELETE SET NULL |
created_at, updated_at | timestamps |
content_section
| Column | Purpose |
|---|---|
id | serial primary key |
page_id | FK to content_page.id, ON DELETE CASCADE |
section_key | CMS section key |
position | layout order |
is_enabled | public visibility flag |
payload_json | validated JSON payload |
created_at, updated_at | timestamps |
Important constraints:
content_page.page_keyis unique.(content_section.page_id, content_section.section_key)is unique.- Sections sort by
position ASC, thenid ASC.
Page Registry
Valid IT Mart page keys:
homeaboutfaqprivacy_policyterms_of_serviceglobal_footerdigital_productsrepairitmartcareitmartcare_claimitmartcare_detailcategoriesbrandsarticles
Retired copied keys are not valid API params anymore:
products_thangkasproducts_singing_bowlsproducts_statuesproducts_jewellery
Section Registry
CONTENT_SECTIONS_BY_PAGE is the authority for compatibility. Invalid pairs fail before database access with 400 CONTENT_SECTION_KEY_INVALID.
The registry is page-aware because shared section keys can have different contracts:
about.herorequires label, heading, subtitle, description, and a main image.digital_products.hero,repair.hero,categories.hero, andbrands.herorequire heading and description only.itmartcare.herorequires logo, description, and shield image.articles.herorequires heading only.
This avoids weakening validation to a single universal hero shape.
Validation Architecture
Validation happens in two stages:
- Param DTO validation:
pageKeymust be inCONTENT_PAGE_KEYSsectionKeymust be inCONTENT_SECTION_KEYS
- Service/registry validation:
- selected section must be allowed for the selected page
payloadJsonmust match the page-specific Zod schema
Shared validators:
- Tiptap doc fields require
type: "doc"and non-emptycontent[]. - URL fields allow HTTP(S) URLs or site-relative paths beginning with
/. - Payload schemas are strict and reject unknown fields.
- Repeater arrays have bounded min/max sizes.
Privacy policy, terms of service, and FAQ retain their live-compatible payload contracts.
Service Behavior
ContentService owns all business behavior:
| Method | Behavior |
|---|---|
getPageForAdmin | cache-aside read; auto-creates missing page |
getPageForPublic | cache-aside read; missing page returns CONTENT_NOT_FOUND |
updatePageMeta | validates non-null seoId, updates page, purges caches, triggers revalidation |
upsertSection | validates pair and payload, upserts by (pageId, sectionKey), purges caches, triggers revalidation |
deleteSection | deletes existing section, purges caches, triggers revalidation |
Controllers stay thin and only unwrap params/body, call the service, and return ResponseDto.
Cache Invalidation
Read cache prefixes:
content:page:public:content:page:admin:
Write invalidation happens in two layers:
ContentService.invalidatePageCache(pageKey)directly invalidates the admin and public Redis cache keys for that page.CacheInvalidationService.triggerForContentWrite(...)invalidates the same content Redis patterns through the centralized purge flow and triggers Next revalidation tags.
New IT Mart shared tags include:
| Page key | Shared tag |
|---|---|
home | page:home |
about | page:about |
faq | page:faq |
privacy_policy | page:privacy-policy |
terms_of_service | page:terms-of-service |
global_footer | layout:footer |
digital_products | page:digital-products |
repair | page:repair |
itmartcare | page:itmartcare |
itmartcare_claim | page:itmartcare:claim |
itmartcare_detail | page:itmartcare:detail |
categories | page:categories |
brands | page:brands |
articles | page:articles |
Cache failures are fail-open. The module logs warnings and does not roll back successful content writes because Redis/Next purge is recoverable through manual purge or retry.
Error Handling
| Scenario | HTTP | errorCode |
|---|---|---|
| Missing public page | 404 | CONTENT_NOT_FOUND |
| Missing page or section on delete | 404 | CONTENT_NOT_FOUND |
| Invalid page/section pair | 400 | CONTENT_SECTION_KEY_INVALID |
| Invalid section payload | 400 | CONTENT_SECTION_PAYLOAD_INVALID |
| Unknown non-null SEO id | 400 | CONTENT_SEO_NOT_FOUND |
Test Coverage
The module has focused coverage for:
- invalid page/section pairs
- invalid page-aware payloads
- representative IT Mart section payloads
- public/admin cache key generation
- write-triggered invalidation of public and admin caches
- centralized content revalidation tags and Redis purge patterns
- admin and public controller forwarding
Operational Notes
- Existing database rows with retired copied keys are not deleted by this code change.
- Those rows are no longer reachable through validated API params.
- If data cleanup is needed, handle it as a separate explicit data migration or SQL maintenance task.
- The admin API still auto-creates newly requested valid pages with default IT Mart titles.