-
Notifications
You must be signed in to change notification settings - Fork 0
Software Architecture
This page outlines the general architecture and design principles of the Permit Connect Navigator Service (PCNS). It is mainly intended for a technical audience, and for people who want to have a better understanding of how the system works.
- Common Object Management Service (COMS) (ref)
- BC Address Geocoder (ref]
- OpenStreet Map (ref]
- Orgbook (ref]
- ATS
- Common Hosted Email Service (ref]
- NR Permitting Exchange, Aggregation and Collection Hub (PEACH) (ref]
The PostgreSQL database is written and handled via managed, code-first migrations. We generally store tables containing activities, initiatives, enquiries, permits, projects, users, and how they relate to each other. We enforce foreign key integrity by invoking onUpdate and onDelete cascades in Postgres. This ensures that we do not have dangling references when entries are removed from the system.
The following figures depict the database schema structure as of Dec 2025.
We use a generic audit schema table to track any update and delete operations done on the database. This table is only modified by database via table triggers, and is not accessible by the PCNS application itself. This meets most general security, tracking and auditing requirements.
The backend code structure in PCNS follows a simple, layered structure adhering to best practice recommendations from Express, Node, and TypeScript coding styles, and utilizes ESLint and Prettier to enforce those recommendations. The codebase has the following discrete layers:
| Layer | Purpose |
|---|---|
| controllers | Contains controller express logic for determining what services to invoke and in what order |
| db | Contains the direct database table model definitions and typical modification queries |
| docs | Contains the OpenAPI specification |
| interfaces | Contains the TypeScript interfaces |
| middleware | Contains middleware functions for handling authentication, authorization and feature toggles |
| parsers | Contains parsers for transforming incoming or outgoing data for 3rd party APIs |
| routes | Contains defined Express routes for defining the PCNS API shape and invokes controllers |
| services | Contains logic for interacting with the database other external APIs for specific tasks |
| types | Contains the TypeScript types and domains to transform between |
| utils | Contains useful utilities and functions that are generic and may be used across the codebase |
| validators | Contains logic which examines and enforces incoming request shapes and patterns |
Each layer is designed to focus on one specific aspect of business logic. Calls between layers are designed to be deliberate, scoped, and contained. This hopefully makes it easier to tell at a glance what each piece of code is doing and what it depends on. For example, the validation layer sits between the routes and controllers. It ensures that incoming network calls are properly formatted before proceeding with execution.
---
title: Network Request Flow Diagram
---
graph LR
ST@{ shape: sm-circ, label: "Start" } --> routes
routes --> middleware
middleware --> validators
validators --> controllers
controllers --> services
services --> EN@{ shape: fr-circ, label: "End" }
PCNS middleware focuses on ensuring that the appropriate business logic filters are applied as early as possible. Concerns such as feature toggles, authentication, and authorization are handled here. Express executes middleware in the order of introduction. It will sequentially execute and then invoke the next callback as a part of its call stack. Because of this, we must ensure that the order we introduce and execute our middleware adheres to the following pattern:
- Ensure authentication and authorization is valid.
- Ensure that the contents, query, and params within the request are structurally sound.
- Ensure that the request contents contain only correct data.
- Perform any remaining middleware hooks prior to invoking the controller logic.
Controllers manage the REST interface responses, focus on the business logic implementation, and invoke the necessary services to perform a business function. Services focus on building the associated SQL query and executing it.
The types layer solely contains TypeScript types. It is not intended to contain any business logic, but rather to provide a common set of types that can be used across the codebase. The models.ts file leverages the Prisma client autogenerated types to add foreign key relations, and should generally not need to be modified. This helps ensure type safety and consistency in how data is represented. The stuff.d.ts file is for most non autogenerated types that may additionally be required.
The validators layer contains logic that examines and enforces incoming request shapes and patterns. It is responsible for validating the structure of incoming requests, ensuring that they conform to the expected format before they are processed by the controllers. This helps catch errors early in the request lifecycle and provides a clear contract for what data is expected.
Validation leverages the Joi library. This allows us to ensure that both the structure and the content of incoming requests are valid before they reach the business logic layer. The validators are designed to be reusable and composable, allowing for easy extension and modification as the API evolves.
Error handling in PCNS is designed to be consistent and informative. Any thrown errors will fall into one of two categories: known response errors, or unexpected errors. Depending on the type of error classification, we will handle the error differently.
For known response errors, we will leverage the Problem utility to generate a standardized error response compliant with RFC9457. This ensures that all known errors are handled in a consistent manner, providing clear and actionable feedback to the client. For all other unexpected errors, a simple and generic throw new Error() will cover the majority of cases, as these are typically indicative of bugs or issues that need to be addressed in the codebase. This approach allows us to differentiate between expected and unexpected errors, making it easier to debug and maintain the application.
Note
As a convention, only controllers and middleware should throw Problem errors; the rest of the codebase should not throw Problem errors, and instead only throw generic errors. This helps ensure that we only expose and handle network request concerns at the appropriate layers, while keeping the rest of the codebase focused on business logic and data manipulation.
The frontend code structure in PCNS follows a simple, layered structure adhering to best practice recommendations from Vue, and TypeScript coding styles, and utilizes ESLint and Prettier to enforce those recommendations. The codebase has the following discrete layers:
| Layer | Purpose |
|---|---|
| assets | Contains public facing images and primary CSS designs |
| components | Contains the independent and reusable UI pieces |
| composables | Contains reusable stateful logic functions leveraging Vue's Composition API |
| interfaces | Contains the TypeScript interfaces |
| lib | Contains wrappers around 3rd party package dependenciefs |
| locales | Contains localization files |
| router | Contains defined vue-router routes for defining the PCNS web application shape |
| services | Contains logic for interacting with the PCNS API or other external APIs for specific tasks |
| store | Contains global state and business logic |
| types | Contains the TypeScript types and domains to transform between |
| utils | Contains useful utilities and functions that are generic and may be used across the codebase |
| validators | Contains logic which examines and enforces form and outgoing request shapes and patterns |
| views | Contains top level components directly referenced by the router |
The following is a partial list of important package dependencies in the frontend:
| Package | Purpose |
|---|---|
| Axios | Library for making HTTP requests |
| Pinia | State management framework for Vue |
| Primevue | Vue component and template library |
| Vite | Javascript bundler, hot-module replacement capabilities |
| Vitest | Javascript unit testing framework |
| Vue Router | Client-side routing library for Vue |
| Vue Test Utils | Vue unit test utility library |
Table of Contents
-
Software Design
-
Developer Resources
-
Product Roadmap