All work

DMS Platform

A public site, CMS, and learning platform for a training business.

Year
2026
Role
Design and build
Type
Platform · Public site, CMS, LMS
Stack
Next.js 16 · .NET 10 · PostgreSQL 17 · MinIO · TipTap
Public site landing page with service paths for businesses and learners

Problem

A training business ran its public site, course catalogue, and learner records in separate places. The platform joins them: one public site, one CMS for editors, and a learning dashboard for participants.

Constraints

  • Three roles with different visibility: editor, reviewer, and administrator.
  • Rich text is stored as TipTap JSON and sanitized on save and on render.
  • Indonesian and English content from the same records.
  • Payments were out of scope for the first release; the data model had to leave room for them.

System

API
.NET 10 modular monolith with Identity, Content, Catalog, and Learning modules. ASP.NET Identity with short-lived JWTs and rotating refresh tokens; FluentValidation on every input.
Content
PostgreSQL 17 for structured content, MinIO for media with presigned URLs. Landing sections, banners, articles, and course records share one audit trail.
Clients
Next.js 16 for the public site, the admin CMS, and the learner dashboard. shadcn/ui primitives with an atomic component structure; security headers set in the Next config.

Decisions

  1. 1.

    Modular monolith, not services

    One deployable with explicit module boundaries. Content and Learning can be extracted later without changing the API contract.

    Tradeoff Module discipline is enforced by review, not the network.

  2. 2.

    Sanitize twice

    TipTap JSON is sanitized when saved and again when rendered. Stored content is never trusted.

    Tradeoff Rendering costs more; a poisoned record cannot reach a reader.

  3. 3.

    Admin is desktop-only

    The CMS assumes a 1440px workspace and collapses its rail to icons. The public site and learner dashboard are responsive.

    Tradeoff Editors cannot publish from a phone; the editing surface stays legible.

  4. 4.

    DTOs only

    No entity ever crosses the HTTP boundary. Overposting is impossible by construction.

    Tradeoff More mapping code per feature.

Screens

CMS course editor: details form beside the curriculum treeSelf-paced course catalogue with pricesArticles index on the public site

Status

MVP in progress, running on a dev deployment. Private repositories.

Evidence