Building Seven CMS: Architecture Decisions
Why I wrote a CMS from scratch instead of extending WordPress. Module system, security hardening, and Vue.js frontend.
Building Seven CMS: Architecture Decisions
When I decided to build a CMS, everyone asked: "Why not fork WordPress?"
Here's why I didn't.
The WordPress Problem
WordPress's codebase dates to 2003. It's accumulated 20 years of backwards compatibility hacks, global state, and security patches on top of security patches. Extending it means inheriting all of that.
I wanted something I could reason about completely — where every line of code exists for a reason.
Architecture Decisions
Modular by Default
Each feature is an independent module: Blog, Pages, Media, E-commerce, Users. Modules register themselves at boot. You can install or remove a module without touching the core.
// Module registration
ModuleRegistry::register('blog', BlogModule::class);
Security First
Every security decision was explicit:
- CSP nonce on every inline script — no wildcard
unsafe-inline - CSRF tokens on all state-changing forms
- Rate limiting on auth endpoints (Redis-backed)
- SVG sanitization on upload — SVGs can contain scripts
- Trusted proxy validation — proper IP resolution behind Nginx
Vue.js Admin Panel
The admin is a Vue 3 SPA communicating with the PHP REST API. Clean separation: PHP handles data, Vue handles UI.
Build with Vite, served as static assets — the PHP app doesn't need to know about frontend tooling.
What I'd Do Differently
Add a proper event system from the start. Right now inter-module communication is direct method calls. An event bus would make modules more independent.
Open source release is planned once the documentation is complete.