Skip to content
Zetmel Safraz Razik

Oxygen — Architecting an Education CRM Before the Modern JavaScript Stack

A full-stack education CRM and institutional operations platform architected during the early JavaScript SPA era — with a custom frontend platform, reusable widgets, metadata-driven administration and a modular Symfony backend.

Historical project Full-stack architecture Custom frontend platform

A full-stack education CRM and institutional operations platform architected during the early JavaScript SPA era. As Co-founder and CTO, I designed the client and server architecture, created a custom frontend platform, reusable business widgets, metadata-driven administration tools and modular Symfony server bundles.

Role: Co-founder, CTO & Principal Architect

Archived website: oxygen.adrotec.com

Durandal · Knockout · BreezeJS · Symfony · Doctrine · RequireJS · D3 · Cylinder

At a glance

  • Full-stack platform — modular SPA client and domain-oriented Symfony server
  • Custom frontend platform — Cylinder, a framework built above Durandal providing authentication, routing, data services, translation, widgets and more
  • Metadata-driven administration — Magic CRUD generated grid and form experiences from server entity metadata
  • Reusable widget library — dozens of platform and domain-specific widgets shared across business modules
  • Business domains — enquiries, marketing, enrolments, courses, payments, messaging, staff, reporting and business intelligence
  • International footprint — documented deployments across Sri Lanka, India, Qatar, Australia and Papua New Guinea (per archived product website)
  • Repository scale — over 1,500 combined commits across client and server repositories

The product challenge

Oxygen was designed for colleges, training institutes, universities and multi-branch education businesses. It connected the complete student lifecycle — from initial enquiry and marketing engagement through application, enrolment, payments, communication, ongoing student management, reporting and executive business intelligence.

Business complexity

The platform needed to support:

  • Student enquiries and automated follow-ups
  • Marketing campaigns and conversion tracking
  • Applications, enrolments, courses, programmes, batches and subjects
  • Branch and multi-institutional management
  • Payments, discounts, revenue monitoring and payment recovery
  • Cashier operations and financial reconciliation
  • Staff management, roles and granular permissions
  • Email, SMS and broadcast messaging
  • Operational dashboards and executive business intelligence
  • PDF and spreadsheet reporting

Each of these areas had distinct workflows, data models and user roles. They also shared data — an enquiry became an application, an application became an enrolment, an enrolment generated payment obligations. The platform had to support each stage independently while preserving continuity across the whole lifecycle.

Technical complexity

Oxygen was developed during a fragmented period of large-scale JavaScript application development. Modern frontend frameworks now provide integrated solutions for components, routing, data fetching, state management, authentication, build tooling and developer experience. At the time, each of these concerns had to be evaluated, integrated and maintained separately.

The client stack included Durandal, Knockout, BreezeJS, RequireJS, AMD modules, Gulp, Babel, Traceur, Bower, npm, Bootstrap, Sass, D3, C3, Moment.js and Knockout Validation — all assembled manually into a coherent application.

The architectural challenge was not simply to build individual screens. It was to create a platform through which a team could continue delivering a growing collection of connected business capabilities without rebuilding infrastructure for every feature.

Architecture

The system was organised into several clear layers:

Users and role-based dashboards

Oxygen modular single-page application

Cylinder frontend platform

BreezeJS metadata and entity layer

Custom Web API integration

Symfony domain bundles

Doctrine domain model

Relational database

Client side

The single-page application was built with Durandal and Knockout, organised into AMD modules loaded by RequireJS. BreezeJS managed client-side entities, change tracking, query caching and metadata synchronisation with the server.

Above this stack I built Cylinder — a custom frontend platform that provided shared application infrastructure:

  • Application bootstrap and module loading
  • Routing and navigation
  • Authentication and authorisation
  • HTTP communication and request encoding
  • Data services and entity management
  • Client-side validation and local storage
  • Translation and internationalisation
  • Dialogs, toast notifications and progress indicators
  • Pagination, charts and reusable widgets

Cylinder enabled the product team to build business modules on top of shared architectural conventions rather than reimplementing application infrastructure for every feature.

Server side

The server was a Symfony and Doctrine application organised into domain-oriented bundles. Business areas such as institutes, enquiries, staff and messaging were separated from shared infrastructure such as users, security, profiles, contacts, file management, configuration, email, SMS and API support.

A custom Breeze server integration (predating my later breeze.server.php library) connected the Doctrine domain model to the BreezeJS client, providing metadata generation, query translation and transactional save processing.

My role

I served as Co-founder and CTO of Adrotec, the company behind Oxygen. My technical contributions included:

  • Full-stack architecture — defining the overall system structure, technology selection and client-server integration strategy
  • Frontend platform design — architecting the modular SPA and building the Cylinder frontend platform
  • Reusable component infrastructure — creating the widget system, shared data services and application shell
  • Server architecture — designing the Symfony bundle structure, domain boundaries and shared infrastructure
  • Client-server integration — establishing the BreezeJS metadata bridge between the frontend entities and the Doctrine domain model
  • Metadata-driven tooling — designing Magic CRUD to generate administration interfaces from server metadata
  • Reporting infrastructure — building the charting widget system, PDF and spreadsheet generation services
  • Technical leadership — guiding the engineering team, establishing conventions and reviewing architecture

I did not write every line of code in the product. I architected the platform, built the core infrastructure, created the reusable components and provided technical direction to the team.

Engineering highlights

Cylinder Frontend Platform

Cylinder was a custom frontend application platform built above Durandal. It combined what we would now recognise as an application shell, a shared component runtime, a data-access abstraction layer, an authentication framework, a module system and a developer productivity toolkit.

Capabilities included application bootstrap, module loading, routing, authentication, authorisation, HTTP communication, data services, entity management, validation, local storage, translation, dialogs, toast notifications, progress indicators, pagination, charts and a library of reusable widgets.

In modern terminology, Cylinder functioned as the platform layer that sat between the framework (Durandal) and the business modules, providing consistent infrastructure that every feature could rely on.

Metadata-Driven Magic CRUD

Magic CRUD examined the client’s Breeze metadata store at runtime, discovered available server entity types, and generated administrative routes, models, grid columns, form fields and validation behaviour from the entity definitions.

The objective was to reduce repetitive CRUD development and maintain consistency between the server domain model and its management interfaces — an early form of metadata-driven administration implemented inside a product platform rather than as a standalone tool.

Modular Symfony Server

The server followed a domain-oriented modular-monolith architecture using Symfony bundles. Business capabilities — institutes, enquiries, staff, messaging — were separated from shared infrastructure — users, security, profiles, contacts, files, configuration, email, SMS and API services.

A modular monolith was the right choice because the business domains were deeply interconnected, many workflows crossed institutional areas, a single deployment simplified operations for clients, and Symfony bundles provided meaningful internal boundaries without distributed-system complexity.

Business Intelligence Widgets

Oxygen treated reporting as a first-class product capability rather than an afterthought. The platform included marketing dashboards, accounting summaries, enquiry reports, branch revenue monitoring, student engagement panels, executive dashboards, status gauges, tabular reports and interactive D3 and C3 visualisations.

Server-side reporting services and PDF and spreadsheet generation complemented the interactive dashboards, serving marketers, administrators, cashiers, finance teams, managers and executives from the same platform.

Building before the modern JavaScript stack

The technology choices reflect the constraints and ecosystem of the early-to-mid 2010s.

Frontend foundation

Durandal, Knockout, BreezeJS, RequireJS — a modular SPA with client-side entities, change tracking and AMD module loading, all assembled manually before integrated frameworks existed.

Build and tooling

Gulp, Babel, Traceur, Bower, npm, Sass — transpilation, package management and build orchestration wired together by hand instead of provided by a single tool.

Visualisation

D3, C3 — custom charting and dashboard visualisations rendered from operational data.

Backend

Symfony, Doctrine, PHP — a modular server with domain-oriented bundles and a rich ORM layer.

Infrastructure

Custom Web API, Breeze integration, PDF and spreadsheet generation — the integration, serialisation and reporting layers that connected the frontend and backend.

Many responsibilities now handled by a single framework — routing, data fetching, state management, build optimisation and developer tooling — had to be designed, integrated and maintained independently.

Architectural trade-offs

Cylinder provided real productivity benefits for the team. Business modules could rely on shared infrastructure for authentication, data access, validation, translation and common UI patterns. New features were built faster because the platform handled the repetitive concerns.

The trade-off was maintenance. A custom platform layer required ongoing investment to keep pace with changes in the underlying frameworks, browser APIs and developer tooling. The Breeze metadata integration created strong coupling between the client entity model and the Doctrine schema — valuable for productivity but costly to change.

The build pipeline — Gulp, Babel, Traceur, Bower, npm — reflected the transitional state of frontend tooling at the time. Each tool solved a real problem, but the combination required active maintenance.

These trade-offs were appropriate for the product’s stage and era. The platform accelerated feature development across a large domain, and the tight integration between client and server was a deliberate choice that prioritised team productivity over architectural flexibility. The repositories should be treated as historical references, not modern starter templates.

Lasting lessons

Oxygen established architectural principles that still influence my work today.

  • Build platforms, not isolated features. The most durable contribution was Cylinder — not any single business module, but the infrastructure that made building modules consistent.
  • Create meaningful domain boundaries. The Symfony bundle structure kept the codebase navigable as the product grew across many business areas.
  • Automate repetitive engineering work. Magic CRUD was an early recognition that generating code from metadata is often more reliable than writing it by hand.
  • Treat reusable UI as product infrastructure. Widgets are not an afterthought — they are the mechanism through which a product maintains consistency at scale.
  • Align client and server architecture deliberately. The BreezeJS metadata integration was expensive to build and maintain. It was worth the cost because it gave the team a unified data model across the full stack.
  • Modern AI accelerates architecture experience; it does not replace it. The problems Oxygen solved — domain modelling, platform design, integration strategy, team leadership — are the same problems that senior engineers solve today, regardless of the tools.

These links are historical archives.

Let's talk

Have a project in mind?

Whether it's a new build or something that needs a fresh perspective — I'd love to hear about it.