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.
Links
These links are historical archives.
- Archived product website — historical product page and screenshots