Moncton CRM — Settlement Agency Case Management Platform
A bilingual case management platform for Canadian settlement agencies, replacing spreadsheets and paper files with one system covering the full newcomer client journey from inquiry to IRCC reporting.
Contents
Executive Summary
Moncton CRM is a secure, bilingual case management platform for Canadian settlement agencies supporting refugees and newcomers. Work has covered full-stack ownership of the product: a Next.js frontend talking to a NestJS/Prisma/PostgreSQL backend exclusively through Server Actions, role- and department-scoped access control for five staff roles (plus agency-defined custom roles), a unified client activity timeline across audit logs, referrals, and intake, and IRCC/iCARE federal reporting. The platform runs on AWS ECS Fargate behind an Application Load Balancer, with Microsoft SSO for staff authentication.
Business Context
Constraints
Government reporting requirements (IRCC/iCARE)
The platform has to capture the specific fields Canada's IRCC newcomer-services program requires and generate compliant XML for federal submission — the data model is shaped by that reporting requirement, not just by what's convenient for casework.
Role- and department-scoped visibility
Front-Desk and Staff see only their own clients, Leads see their team, Managers see their department, Admins see the organization — and agencies can define custom roles on top of the built-in five, so access control couldn't be hardcoded to a fixed role list.
Bilingual by requirement
Agencies operate in English and French. Every core workflow needs a real French equivalent maintained alongside the English one, not a translation layer bolted on afterward.
PIPEDA-sensitive client data
Client records include immigration status, family composition, and other PII protected under Canadian privacy law, which meant audit trails and encrypted storage were requirements from the start, not hardening added later.
System Blueprint
The Next.js frontend reaches the NestJS API only through Server Actions, the API enforces role- and department-scoped access via Guards and permission decorators before any Prisma query runs, and background work like appointment reminders is queued through Redis/BullMQ instead of running inline on the request path.
Frontend
Next.js App Router application covering inquiry, intake, case, referral, and appointment workflows for staff, with a bilingual English/French UI.
Server Actions
Mediates every data read and write from the frontend, so there is no data-fetching endpoint directly callable from the browser and auth tokens stay server-side.
API
NestJS REST API authenticating staff via JWT and Microsoft SSO, enforcing role- and department-scoped authorization through Guards and permission decorators, validating every request through DTO pipelines.
Data layer
Relational models for users, roles, organizations, departments, clients, inquiries, intake records, referrals, appointments, documents, and audit logs.
Background jobs
Redis-backed BullMQ queues handle appointment reminders and notifications asynchronously, off the request path.
Deployment
Containerized frontend and backend services on AWS ECS Fargate behind an Application Load Balancer, with configuration injected from AWS Systems Manager Parameter Store.
Architecture
Server Actions as the only data boundary
Every read and write — inquiry, intake, case, referral, appointment — goes through a Next.js Server Action rather than a REST route the browser calls directly, keeping authentication tokens and session handling server-side across a data model that includes PIPEDA-protected client PII.
Role- and department-scoped authorization at the API
NestJS Guards and permission decorators enforce visibility by role and department before a query runs, so a Front-Desk or Staff request can't return records outside its assigned scope regardless of what the frontend asks for — the same boundary that fetches data is the boundary that scopes it.
Unified client activity timeline
Audit logs, referral events, intake updates, and inquiry changes are combined into one paginated, chronological timeline per client, with actor resolution and organization scoping, instead of staff reconstructing a client's history from separate logs.
Bilingual UI via structured localization
English and French are maintained through structured translation files and reusable localization patterns across core modules, so a new feature ships in both languages rather than treating French as a follow-up pass.
Deployment topology
Frontend and backend run as separate containerized services on AWS ECS Fargate behind an Application Load Balancer, with environment configuration injected via AWS Systems Manager Parameter Store at task startup.
Engineering Decisions
Server Actions vs. a client-facing REST API
- Expose a REST API the frontend calls directly.
- Route all data access through Next.js Server Actions, invoked only from the application's own server-rendered flows.
Inline role checks vs. centralized scoping at the API boundary
- Check role/department scope inline in each controller or service method.
- Centralize role- and department-scoping in NestJS Guards and permission decorators applied at the route level.
Synchronous vs. queued appointment reminders
- Send reminders and notifications synchronously within the handling request.
- Queue them through Redis-backed BullMQ and process asynchronously.
Production Stories
Standing up the dev environment the night before a client demo
Lessons Learned
Centralizing role- and department-scoping at the API boundary, instead of inline per controller, means new endpoints inherit correct access control by default.
A single config change (a listening port) can require updates in several independent places — task definition, target group, health checks, security group — and each one left stale looks like a separate bug.
Writing up a debugging trace as documentation the team can reuse is worth more than the fix itself; the fix only helps once, the documentation helps every time the pattern recurs.
Related Notes
- Six Misconfigurations, One Night: Bringing Up a Dev Environment Before a Client Demo
Moncton CRM's dev environment went down at 11pm, the night before its first client demo, with the engineer who owns cloud configuration unavailable. Six independent misconfigurations were hiding behind each other — fixing one only revealed the next.