Skip to main content

Annex A: Reference Implementation Examples

Fastify and NestJS Patterns Across Current Verticals

Companion to: RaR-IT SaaS Platform Architecture Policy, Sections 5–6.1

:::note Purpose This annex illustrates, with real component names drawn from the current AMAAR, Clinivio, EduSuite, Transport, and Tourism backlogs, how the two sanctioned framework patterns actually look in code — the Fastify default (Section 5) and the NestJS scoped allowance (Section 6.1) — plus one pattern that is not permitted without Architecture Review Board sign-off, shown so the risk is concrete rather than abstract.

Every example shares one non-negotiable property: Entitlements' module discovery reads the same framework-agnostic manifest contract regardless of which pattern a module's internals use. :::

A.1 Fastify Pattern — Simple CRUD Module

AMAAR: Properties

The default and most common case — no dependency-injection container is needed because there is nothing to inject.

verticals/amaar/modules/properties/manifest.ts
import { FeatureManifest } from '@rarit-kernel/entitlements';

export const manifest: FeatureManifest = {
key: 'properties',
displayName: { en: 'Properties', ar: 'العقارات' },
requiredPlan: ['starter', 'growth', 'enterprise'],
routes: ['/properties/*'],
};
verticals/amaar/modules/properties/index.ts
import fp from 'fastify-plugin';
import { z } from 'zod';
import { eq } from 'drizzle-orm';
import { requireEntitlement } from '@rarit-kernel/entitlements';
import { properties } from '../../db/schema';

const CreatePropertySchema = z.object({
title: z.string().min(3),
type: z.enum(['apartment', 'villa', 'office', 'land']),
price: z.number().positive(),
city: z.string(),
bedrooms: z.number().int().optional(),
});

export default fp(async (fastify) => {
// Entitlements check applies to every route in this plugin.
fastify.addHook('preHandler', requireEntitlement('properties'));

fastify.get('/properties', async (req) => {
const { tenantId } = req.tenant;
return fastify.db.query.properties.findMany({
where: eq(properties.tenantId, tenantId),
});
});

fastify.post('/properties', { schema: { body: CreatePropertySchema } },
async (req, reply) => {
const { tenantId, userId } = req.tenant;
const [property] = await fastify.db.insert(properties)
.values({ ...req.body, tenantId, createdBy: userId })
.returning();
return reply.code(201).send(property);
});
}, { name: 'properties' });

A.2 Fastify Pattern — Thin Consumer of a Shared Engine

Transport: Commission & Settlement

A module whose entire job is to read and write against a fixed-contract shared engine. No vertical-side business logic complex enough to justify a DI container.

verticals/transport/modules/commission/manifest.ts
export const manifest: FeatureManifest = {
key: 'commission-settlement',
displayName: { en: 'Commission & Settlement', ar: 'العمولات والتسوية' },
requiredPlan: ['growth', 'enterprise'],
routes: ['/commission/*'],
};
verticals/transport/modules/commission/index.ts
import fp from 'fastify-plugin';
import { requireEntitlement } from '@rarit-kernel/entitlements';
import { getLedgerForTenant, recordSettlement } from '@rarit-kernel/payment';

export default fp(async (fastify) => {
fastify.addHook('preHandler', requireEntitlement('commission-settlement'));

// Ledger entries (due / pending / approved / rejected) come straight
// from the Payment engine's shared ledger primitive -- Transport does
// not maintain its own copy of settlement state.
fastify.get('/commission/ledger/:packetShopId', async (req) => {
return getLedgerForTenant(req.tenant.tenantId, {
scope: 'packet_shop',
scopeId: req.params.packetShopId,
});
});

fastify.post('/commission/settle/:entryId', async (req, reply) => {
const settled = await recordSettlement(req.tenant.tenantId, req.params.entryId, {
approvedBy: req.tenant.userId,
});
return reply.send(settled);
});
}, { name: 'commission-settlement' });

A.3 Fastify Pattern — Simple CRUD Module (Second Vertical)

Tourism: CRM & Customer Management

A second CRUD example from a different vertical, to make clear this pattern is the norm, not an AMAAR-specific convention.

verticals/tourism/modules/crm/index.ts
import fp from 'fastify-plugin';
import { requireEntitlement } from '@rarit-kernel/entitlements';

export default fp(async (fastify) => {
fastify.addHook('preHandler', requireEntitlement('crm'));

fastify.get('/customers/:id', async (req) => {
return fastify.db.query.customers.findFirst({
where: (c, { eq, and }) => and(
eq(c.id, req.params.id),
eq(c.tenantId, req.tenant.tenantId),
),
with: { bookings: true },
});
});

fastify.patch('/customers/:id/loyalty-tier', async (req, reply) => {
const customer = await fastify.db.query.customers.findFirst({
where: (c, { eq }) => eq(c.id, req.params.id),
});
const tier = customer.lifetimeSpend >= 10000 ? 'preferred' : 'standard';
await fastify.db.update(customers).set({ tier }).where(eq(customers.id, req.params.id));
return reply.send({ tier });
});
}, { name: 'crm' });

A.4 NestJS Pattern — Shared Providers Across Many Templates

Clinivio: Clinical Templates Engine (Section 6.1 scoped allowance)

This is the case Section 6.1 was written for: ten specialty clinical templates, several sharing common building blocks — a body/anatomy diagram widget, a structured-value validator, a template-registry lookup. Fastify still owns the HTTP route, the manifest, and the entitlement check; Nest is invisible from Entitlements' point of view.

:::caution Scope note This module is currently Phase 2 under the lean-MVP launch scope (deferred alongside Hospitals/Centers/Dental). Included here as the clearest real example of the pattern, not as a claim it ships at initial launch. :::

verticals/clinivio/modules/clinical-templates/nest/clinical-templates.module.ts
import { Injectable, Module } from '@nestjs/common';

export interface ClinicalTemplateDefinition {
specialtyKey: string;
schema: ZodSchema;
usesBodyDiagram: boolean;
}

@Injectable()
export class TemplateRegistryService {
private templates = new Map<string, ClinicalTemplateDefinition>();

register(template: ClinicalTemplateDefinition) {
this.templates.set(template.specialtyKey, template);
}
get(specialtyKey: string) {
return this.templates.get(specialtyKey);
}
}

@Injectable()
export class BodyDiagramWidgetService {
// Shared by Dermatology (skin map), Orthopedics (pain map),
// Physiotherapy (assessment diagram), and Beauty (treatment-area marking).
renderDiagram(regionMap: BodyRegionMap, markings: Marking[]) {
/* shared rendering + coordinate-validation logic lives here once */
}
}

@Injectable()
export class ClinicalValidationService {
constructor(private readonly registry: TemplateRegistryService) {}

validate(specialtyKey: string, payload: unknown) {
const template = this.registry.get(specialtyKey);
if (!template) throw new UnknownSpecialtyError(specialtyKey);
return template.schema.parse(payload);
}
}

@Module({
providers: [
TemplateRegistryService,
BodyDiagramWidgetService,
ClinicalValidationService,
DermatologyTemplateProvider,
OrthopedicsTemplateProvider,
PhysiotherapyTemplateProvider,
// ... remaining specialty template providers
],
exports: [ClinicalValidationService, BodyDiagramWidgetService],
})
export class ClinicalTemplatesModule {}
verticals/clinivio/modules/clinical-templates/index.ts — Fastify owns HTTP, manifest, and entitlement check
import fp from 'fastify-plugin';
import { NestFactory } from '@nestjs/core';
import { requireEntitlement } from '@rarit-kernel/entitlements';
import { ClinicalTemplatesModule, ClinicalValidationService } from './nest/clinical-templates.module';

export const manifest = {
key: 'clinical-templates',
displayName: { en: 'Specialty Clinical Templates', ar: 'القوالب السريرية التخصصية' },
requiredPlan: ['enterprise'],
routes: ['/clinical-templates/*'],
};

export default fp(async (fastify) => {
// Nest used ONLY as a DI application context -- no HTTP adapter,
// no Nest guards/interceptors, no Nest-owned routing. This is the
// required integration pattern under Policy Section 6.1.
const nestCtx = await NestFactory.createApplicationContext(ClinicalTemplatesModule);
const validation = nestCtx.get(ClinicalValidationService);

fastify.addHook('preHandler', requireEntitlement('clinical-templates'));

fastify.post('/clinical-templates/:specialty/visit-note', async (req, reply) => {
const parsed = validation.validate(req.params.specialty, req.body);
const [note] = await fastify.db.insert(visitNotes)
.values({ ...parsed, tenantId: req.tenant.tenantId })
.returning();
return reply.code(201).send(note);
});
}, { name: 'clinical-templates' });

A.5 NestJS Pattern — Pluggable Strategy Objects

AMAAR: Financial Forecasting

A second real Nest-scoped-allowance case: multiple interchangeable forecasting algorithms implementing the same interface, selected at request time — the textbook strategy pattern.

verticals/amaar/modules/financial-forecasting/nest/financial-forecasting.module.ts
import { Inject, Injectable, Module } from '@nestjs/common';

export interface ForecastStrategy {
readonly key: string;
forecast(history: RevenuePoint[], horizonMonths: number): ForecastResult;
}

const FORECAST_STRATEGIES = Symbol('FORECAST_STRATEGIES');

@Injectable()
class LinearTrendStrategy implements ForecastStrategy {
readonly key = 'linear';
forecast(history, horizonMonths) { /* simple linear regression */ }
}

@Injectable()
class SeasonalAdjustedStrategy implements ForecastStrategy {
readonly key = 'seasonal';
forecast(history, horizonMonths) { /* seasonal decomposition */ }
}

@Injectable()
export class ForecastingService {
constructor(@Inject(FORECAST_STRATEGIES) private readonly strategies: ForecastStrategy[]) {}

run(key: string, history: RevenuePoint[], horizonMonths: number) {
const strategy = this.strategies.find((s) => s.key === key) ?? this.strategies[0];
return strategy.forecast(history, horizonMonths);
}
}

@Module({
providers: [
LinearTrendStrategy,
SeasonalAdjustedStrategy,
{
provide: FORECAST_STRATEGIES,
useFactory: (linear: LinearTrendStrategy, seasonal: SeasonalAdjustedStrategy) => [linear, seasonal],
inject: [LinearTrendStrategy, SeasonalAdjustedStrategy],
},
ForecastingService,
],
exports: [ForecastingService],
})
export class FinancialForecastingModule {}
verticals/amaar/modules/financial-forecasting/index.ts
import fp from 'fastify-plugin';
import { NestFactory } from '@nestjs/core';
import { requireEntitlement } from '@rarit-kernel/entitlements';
import { FinancialForecastingModule, ForecastingService } from './nest/financial-forecasting.module';

export const manifest = {
key: 'financial-forecasting',
displayName: { en: 'Financial Forecasting', ar: 'التوقعات المالية' },
requiredPlan: ['enterprise'],
routes: ['/forecasting/*'],
};

export default fp(async (fastify) => {
const nestCtx = await NestFactory.createApplicationContext(FinancialForecastingModule);
const forecasting = nestCtx.get(ForecastingService);

fastify.addHook('preHandler', requireEntitlement('financial-forecasting'));

fastify.get('/forecasting/revenue', async (req) => {
const history = await fastify.db.query.revenueHistory.findMany({
where: (r, { eq }) => eq(r.tenantId, req.tenant.tenantId),
});
const strategyKey = req.query.strategy ?? 'linear';
const horizon = Number(req.query.months ?? 6);
return forecasting.run(strategyKey, history, horizon);
});
}, { name: 'financial-forecasting' });

A.6 Anti-Pattern — Nest Owning Its Own HTTP Pipeline

:::danger NOT PERMITTED WITHOUT ARCHITECTURE REVIEW BOARD SIGN-OFF Shown here only to make the risk in Policy Section 6.1 concrete — do not copy this as a starting template. :::

The problem: once Nest owns its own Fastify-adapter HTTP instance, requests to it never pass through the parent process's entitlement-check hook.

verticals/tourism/modules/ai-intelligence/nest-app.ts — illustrative, not an approved pattern
import { NestFactory } from '@nestjs/core';
import { FastifyAdapter } from '@nestjs/platform-fastify';

// Nest now owns its own HTTP pipeline and lifecycle.
const nestApp = await NestFactory.create(AiIntelligenceModule, new FastifyAdapter());
await nestApp.init();

// Mounting it under the parent process does NOT route these requests
// through the parent's requireEntitlement() preHandler hook:
fastify.register(async (instance) => {
instance.all('/ai-intelligence/*', (req, reply) =>
nestApp.getHttpAdapter().getInstance().routing(req.raw, reply.raw)
);
});
// -> A tenant without the 'ai-intelligence' entitlement can still
// reach this route. Fixing it requires either a duplicated Nest
// guard re-implementing the same check, or a proxy hop through
// Fastify's own hook first -- both are the added complexity and
// dual-enforcement-path risk Section 6.1 flags before this pattern
// may be used.

A.7 Summary Table — Pattern Choice by Module

VerticalModulePattern UsedWhy
AMAARProperties✅ Fastify (direct)Ordinary CRUD; no internal collaborators to inject.
TransportCommission & Settlement✅ Fastify (direct)Thin consumer of the fixed-contract Payment engine; no vertical-side complexity.
TourismCRM & Customer Management✅ Fastify (direct)Ordinary CRUD across a different vertical — confirms the pattern is the norm.
ClinivioClinical Templates Engine🟡 NestJS (DI context)Shared providers reused across 10 specialty templates — genuine DI use case.
AMAARFinancial Forecasting🟡 NestJS (DI context)Multiple interchangeable forecast strategies selected at request time — textbook strategy pattern.
Tourism (illustrative)AI Intelligence🔴 Not permitted — Nest-owned HTTPShown in A.6 only to demonstrate why this pattern requires Architecture Review Board sign-off.
note

This annex is illustrative and will be extended as new real modules are built. It does not itself carry policy force independent of the main document — where this annex and the policy body appear to conflict, the policy body (Sections 1–10) governs.