Files
pfm/GEMINI.md

98 KiB
Raw Permalink Blame History

PostFundManagement (PFM) Platform — Engineering & Architecture Guide (GEMINI.md)

Document Context: This document serves as the primary system specification, technical architectural guide, and development roadmap for the Post-Fund Management, Performance & Impact Management Platform (PFM). It is directly derived from the PFM System Requirements Specification (SRS) v2.0 (authored by S. Phiri, 13 August 2026) and reflects the actual technical implementation and project layout in this repository.

Role for Gemini / Antigravity: Use this file as your authoritative blueprint when extending, debugging, testing, or implementing any features, endpoints, domain models, or UI components across the PFM solution.


1. Executive Summary & Product Perspective

The Post-Fund Management (PFM) Platform is an enterprise-grade multi-tenant software system designed for organisations that administer and govern Innovation, Development, NGO/NPO, Donor, Grant, Blended-Finance, and Impact-Funding portfolios.

1.1 Scope Boundary

  • Upstream Boundary (Out of Scope): Call for applications, proposal intake, eligibility screening, technical/financial review, due diligence, and approval committee deliberation. Upstream systems hand off approved awards into PFM via REST APIs, webhooks, or batch imports.
  • Controlled Lifecycle (In Scope): The PFM platform lifecycle strictly begins at APPROVED FUNDING AWARD. Recording an approved award triggers the automated Contract/Agreement generation workflow. From that entry point, PFM governs: \text{Award Intake} \longrightarrow \text{Contracting} \longrightarrow \text{Onboarding} \longrightarrow \text{Implementation / Milestones} \longrightarrow \text{Disbursement Control} \longrightarrow \text{M\&E / Risk / Compliance} \longrightarrow \text{Close-Out} \longrightarrow \text{Post-Fund Monitoring} \longrightarrow \text{Portfolio Impact Reporting}
   ┌─────────────────────────────────────────────────────────┐
   │ EXTERNAL / UPSTREAM (Out of Scope)                      │
   │ Call for Apps ─> Application ─> Assessment ─> Approval │
   └────────────────────────────┬────────────────────────────┘
                                │ (Handover via API / Import)
════════════════════════════════▼══════════════════════════════════ SYSTEM SCOPE BEGINS
   ┌─────────────────────────────────────────────────────────┐
   │ PFM CONTROLLED LIFECYCLE (In Scope)                     │
   │                                                         │
   │  [M01] Award Intake & Registration                      │
   │     │                                                   │
   │     ▼                                                   │
   │  [M02] Contract & Agreement Management                  │
   │     │                                                   │
   │     ▼                                                   │
   │  [M03] Recipient Onboarding (Banking & KYC Gate)        │
   │     │                                                   │
   │     ├──────────────────────────┐                        │
   │     ▼                          ▼                        │
   │  [M04] Project Management   [M05] Budget & Finance      │
   │     │                          │                        │
   │     ▼                          ▼                        │
   │  [M07] Milestones & Evidence ──> [M06] Disbursement     │
   │     │                                                   │
   │     ├─────────────────────────────────────┐             │
   │     ▼                                     ▼             │
   │  [M08] M&E / MEAL Indicators           [M09] Risk Mgmt  │
   │  [M10] Compliance & [M11] ESG          [M16] Issues     │
   │  [M12] Innovation / TRL                [M17] Site Visits│
   │  [M13] Beneficiaries (POPIA)           [M15] Change Ctrl│
   │     │                                     │             │
   │     └──────────────────┬──────────────────┘             │
   │                        ▼                                │
   │  [M18] Formal Project Close-Out                         │
   │     │                                                   │
   │     ▼                                                   │
   │  [M19] Post-Fund Monitoring (6 to 60 Months)            │
   │     │                                                   │
   │     ▼                                                   │
   │  [M20 / M22] Executive Portfolio Dashboards & Reports   │
   └─────────────────────────────────────────────────────────┘

2. Tech Stack Across Projects in the Repository

The solution is architected using Clean Architecture and Domain-Driven Design (DDD) on .NET 10.0 (C# 13) for the backend, paired with modern React 19 / TypeScript / Vite frontend applications.

PostFundManagement/
├── PostFundManagement.Domain/          # Core entities, enums, value objects, domain logic
├── PostFundManagement.Infrastructure/  # EF Core 10, PostgreSQL (Npgsql), Migrations, Data Protection
├── PostFundManagement.Application/     # CQRS Handlers, Mediator, DTOs, Validations, S3, Email, Quartz
├── PostFundManagement.Api/             # Minimal APIs / Carter endpoints, Auth, Swagger/Scalar, Versioning
├── PostFundManagement.Tests/           # Unit & Integration test suite (xUnit, Coverlet)
├── PostFundManagement.ConsoleUI/       # Internal Staff & Admin Web Portal (React 19 + Vite 6 + Tailwind 4)
├── PostFundManagement.PartnerUI/       # External Grantee / Partner Portal (React 19 + Vite 8 + Tailwind 4)
└── docker-compose.yml                  # Container orchestration for API and Frontends

2.1 Backend Projects (.NET 10.0 / C# 13)

Project Target Framework Core Libraries & Packages Architectural Purpose
PostFundManagement.Domain net10.0 Microsoft.EntityFrameworkCore 10.0.12, Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3, Humanizer 3.0.10, Hashids.net 1.7.0, OpenTelemetry 1.18.0, Quartz 4.1.0, FluentResults 4.0.0, Mediator.Abstractions 3.0.2, AWSSDK.S3 4.0.103.3, MailKit/MimeKit 4.18.0 Contains domain entities, aggregate roots, value objects, domain enums, constant definitions, and interfaces. Clean of presentation concerns.
PostFundManagement.Infrastructure net10.0 Microsoft.EntityFrameworkCore 10.0.12, Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3, Duende.AccessTokenManagement 4.2.0, EF Core Tools & Design Houses PostFundManagementDbContext, EF Core fluent configurations, entity-to-table mappings, PostgreSQL schema definitions, multitenancy filters, and migrations.
PostFundManagement.Application net10.0 Mediator.SourceGenerator / Mediator.Abstractions 3.0.2, FluentResults 4.0.0, Microsoft.Extensions.Caching.Hybrid 10.10.0, Microsoft.Extensions.Caching.StackExchangeRedis 10.0.12, Refit 15.2.0, Duende.AccessTokenManagement.OpenIdConnect 4.2.0, Quartz 4.1.0, AWSSDK.S3 Implements CQRS command/query handlers, domain validators, S3 object storage clients (Garage S3), background jobs (Quartz), notification dispatchers, and business logic.
PostFundManagement.Api net10.0 Microsoft.AspNetCore.OpenApi 10.0.12, Scalar.AspNetCore 2.17.4, Asp.Versioning.Http 10.2.3, IdentityModel.AspNetCore 4.3.0, Microsoft.AspNetCore.Authentication.JwtBearer 10.0.12, AspNetCore.HealthChecks.NpgSql 9.0.0, AspNetCore.HealthChecks.UI Minimal API endpoint definitions divided by business slices (Grants, Projects, Execution, Governance, Evidence, Communications, Users). Handles routing, auth policies, error problem details, and OpenAPI metadata.
PostFundManagement.Tests net10.0 xunit 2.9.3, xunit.runner.visualstudio 3.1.4, Microsoft.NET.Test.Sdk 17.14.1, coverlet.collector 6.0.4 Unit and integration test coverage for domain rules, CQRS handlers, financial calculation models, and gate condition validations.

2.2 Frontend Projects (React 19 / TypeScript)

PostFundManagement.ConsoleUI (Internal Staff / Portfolio Administration Portal)

  • Package Manager: Yarn (yarn.lock is authoritative; npm must not be used)
  • Runtime & Bundler: React 19.0.1, TypeScript 5.8.2, Vite 6.2.3
  • Styling: Tailwind CSS v4.1.14 (@tailwindcss/vite, @tailwindcss/postcss)
  • Routing: React Router DOM v7.18.3
  • State & Server Querying: @tanstack/react-query v5.102.8, Axios 1.20.0
  • Forms & Validation: react-hook-form 7.87.0, zod 3.25.76, @hookform/resolvers
  • Authentication: oidc-client-ts 3.5.0 (Duende IdentityServer / STS PKCE authentication)
  • Animation & Icons: motion (Framer Motion) 12.23.24, lucide-react 0.546.0
  • Exports & Utilities: jspdf 4.2.1, jspdf-autotable 5.0.8, xlsx 0.18.5, hashids 2.3.0
  • AI Capabilities: @google/genai 2.4.0 (for intelligent portfolio insights and drafting)
  • Target Users: Funding Administrators, Programme Managers, Finance Officers, M&E Officers, Risk Managers, System Administrators, Executive Leadership.

PostFundManagement.PartnerUI (External Recipient / Grantee Portal)

  • Package Manager: Yarn (yarn.lock is authoritative; npm must not be used)
  • Runtime & Bundler: React 19.0.1, TypeScript 7.0.2, Vite 8.3.0
  • Styling & Effects: Tailwind CSS v4.3.3, motion 12.23.24, ogl 1.0.11 (WebGL visual elements like MoltenMetal, BorderGlow, SpecularButton)
  • Routing: React Router DOM v7.18.4
  • State & Server Querying: @tanstack/react-query v5.104.0, Axios 1.20.0
  • Forms & Validation: react-hook-form 7.89.0, zod 4.6.5
  • Authentication: oidc-client-ts 3.5.0
  • Exports & Utilities: jspdf, xlsx, hashids
  • Target Users: Grantee / Partner Project Directors, Financial Controllers, Field Officers submitting evidence and disbursement drawdown requests.

2.3 Infrastructure, Storage & Security Topology

  • Database: PostgreSQL 14+ (Separate connection strings for primary transactional store pfm and scheduler store pfm-scheduler).
  • Object / Evidence Store: S3-compatible storage (e.g., Garage S3 / AWS S3) with pre-signed upload URLs and CDN acceleration.
  • Cache: Redis (StackExchange.Redis + ASP.NET Core OutputCaching 2-minute base policy on Minimal API GET endpoints + HybridCache). Paired with TanStack React Query in ConsoleUI and Axios mutation versioning (_v parameter) to guarantee instant reads and zero stale data after mutations.
  • Authentication / STS: Duende IdentityServer / Skoruba Administration (https://sts.security.khongisa.co.za).
  • Observability: OpenTelemetry exporters streaming traces, metrics, and logs to Aspire Dashboard / OTLP collectors.

3. Master Business Rules (BR-001 to BR-010)

The system enforces 10 cardinal business rules across all modules and APIs:

Rule ID Rule Name Specification & Enforcement Logic MoSCoW
BR-001 Approval is Entry Point No contract, project, or budget may be created without a verified, approved funding award reference. Must Have
BR-002 Contract Initiated Automatically Capturing and activating an approved award record automatically transitions into and initializes the Contract/Agreement workflow. Must Have
BR-003 No Uncontrolled Disbursement No payment or disbursement may be approved or released unless all applicable conditions precedent and milestone evidence validations are formally satisfied. Must Have
BR-004 Evidence-Based Performance Reported performance on milestones and indicators must be substantiated by mandatory uploaded evidence documents/files before approval. Must Have
BR-005 Single Source of Truth A data entity (recipient org, award amount, bank details, baseline) is captured once and referenced immutably across the full lifecycle. Must Have
BR-006 Configurable Funding Instruments Funding rules, milestones, payment schedules, KPIs, reporting frequencies, and risk thresholds must not be hardcoded; they vary by funding instrument type. Should Have
BR-007 Full Auditability Every material decision, status transition, payment approval, budget adjustment, and evidence submission writes an immutable, user-attributed log entry. Must Have
BR-008 Results Hierarchy Performance indicators strictly align to the causal chain: \text{Input} \longrightarrow \text{Activity} \longrightarrow \text{Output} \longrightarrow \text{Outcome} \longrightarrow \text{Impact}. Must Have
BR-009 Indicator Integrity Every measurable metric follows the evaluation sequence: \text{Baseline} \longrightarrow \text{Target} \longrightarrow \text{Actual} \longrightarrow \text{Variance} \longrightarrow \text{Supporting Evidence}. Must Have
BR-010 Automated Portfolio Roll-Up Activity and project data automatically roll up to Programme \longrightarrow Portfolio \longrightarrow Institution \longrightarrow National/Donor reporting tiers without manual consolidation. Must Have

4. System Architecture & Data Models

4.1 Core Domain Entities & Relationships

erDiagram
    Organisation ||--o{ Award : "receives"
    FundingProgramme ||--o{ Award : "governs"
    Award ||--|| Contract : "initiates"
    Award ||--|| Project : "funds"
    Project ||--o{ Budget : "allocates"
    Project ||--o{ Milestone : "tracks"
    Project ||--o{ Risk : "identifies"
    Project ||--o{ Indicator : "measures"
    Project ||--o{ BeneficiaryGroup : "reaches"
    Milestone ||--o{ Disbursement : "gates"
    Milestone ||--o{ Evidence : "requires"
    Indicator ||--o{ Evidence : "substantiated_by"
    FundingProgramme ||--o{ PortfolioSnapshot : "aggregates"

4.2 Key System Enums & Taxonomies

  • IndustrySector: ICT, Agriculture, Mining, Manufacturing, Energy, Healthcare, Education, Financial Services, Tourism, Biotechnology.
  • ProjectType: Research & Development (R&D), Technology Commercialisation, Infrastructure, Capacity Building, Social Impact, Community Development.
  • EvidenceType: Invoice, Bank Statement, Progress Report, Geotagged Photograph, Attendance Register, Research Output, Prototype Demonstration, Certification, Patent Filing, Audited Financial Statement, Beneficiary Schedule.
  • FundingInstrument:
    1. Grant: Award \to Milestone \to Disbursement \to Reporting.
    2. Development Finance: Investment \to Drawdown \to Conditions \to Repayment / Performance.
    3. Impact Investment: Investment \to KPI \to Impact \to Financial/Social Return.
    4. Blended Finance: Multi-tranche (Grant + Debt + Equity + Technical Assistance).
    5. NGO Programme: Grant \to Activities \to Beneficiaries \to Outcomes \to Impact.
    6. Innovation Funding: Grant \to TRL Progression \to Prototype \to Validation \to Commercialisation.

5. Functional Requirements Breakdown (Modules M01 – M25)

The SRS v2.0 establishes 25 functional modules with 304 prioritized requirements and Given/When/Then acceptance criteria:

Module M01 — Award Intake & Funding Registration

  • Purpose: Capture and validate the official award record following external approval committee sanction.
  • Priority: 17 Must Have
  • Key Requirements & Acceptance Criteria:
    • FR-M01-001 to 007: System shall capture mandatory fields: External approval reference number, funding amount (> 0), funding period (start & end dates), programme, instrument, recipient organisation, and project title.
    • FR-M01-016 (Duplicate Prevention): Given an award registration in progress, when the external reference number matches an existing record, then the system blocks creation and presents the existing award.
    • FR-M01-017 (Contract Initiation Trigger): When an award is successfully registered, the system automatically creates the corresponding Contract record in DRAFT status (BR-002).
  • Module Acceptance Gates: Award cannot be activated without external reference, recipient in master data, amount > 0, and valid date boundaries.

Module M02 — Contract & Agreement Management

  • Purpose: Convert approved awards into legally binding, monitorable contracts with version control.
  • Workflow: DRAFT \longrightarrow UNDER REVIEW \longrightarrow EXECUTED \longrightarrow AMENDED (versioned) \longrightarrow EXPIRED / TERMINATED.
  • Priority: 22 Must Have
  • Key Requirements & Acceptance Criteria:
    • FR-M02-001 to 012: Support contract templates, configurable legal clauses, signing parties, effective & expiry dates, contractual obligations, payment conditions, and digital/scanned signatures.
    • FR-M02-020: The executed agreement is locked as the authoritative legal version. Subsequent amendments (FR-M02-021) generate incremented versions (v1.1, v2.0) with full diff logs.
  • Module Acceptance Gates: A contract cannot transition to EXECUTED unless approved award exists, authorised parties are identified, agreement version is set, required signatures are attached, and effective date exists.

Module M03 — Recipient Onboarding

  • Purpose: Verify recipient organization credentials, key personnel, KYC compliance, and banking details before funds can be released.
  • Priority: 13 Must Have
  • Key Requirements & Acceptance Criteria:
    • FR-M03-001 to 006: Establish organization profile, key personnel, verified banking details, tax/statutory compliance documents, project team assignments, and configurable onboarding checklists.
    • FR-M03-012 (First Disbursement Gate): Onboarding verification is a mandatory prerequisite for the first disbursement. If banking details are unverified or compliance docs are missing, disbursement approvals are strictly blocked (AT-03).
    • FR-M03-013: Recipient profiles and verified banking details can be reused across subsequent awards (BR-005).

Module M04 — Project & Programme Management

  • Purpose: Plan, schedule, and track implementation at the work breakdown structure (WBS), activity, and task levels.
  • Workflow: SET-UP \longrightarrow READY FOR IMPLEMENTATION \longrightarrow ACTIVE \longrightarrow AT RISK \longrightarrow SUSPENDED \longrightarrow COMPLETED \longrightarrow CLOSED \longrightarrow POST-FUND \longrightarrow ARCHIVED.
  • Priority: 21 Must Have
  • Key Requirements & Acceptance Criteria:
    • FR-M04-001 to 014: Work breakdown structure, activities, tasks, responsible assignees, activity dependencies, deliverable links, and timelines.
    • FR-M04-015: Automatically monitor planned vs actual completion. Highlight delayed activities (FR-M04-017).
    • FR-M04-018: Project progress percentage is calculated automatically from verified milestone completions.
    • FR-M04-021: Project status and metrics automatically roll up to programme level (BR-010).

Module M05 — Budget & Financial Management

  • Purpose: Maintain the approved project budget, commitments, actual expenditure, variance, and leveraged co-funding.
  • Priority: 18 Must Have, 1 Should Have
  • Core Financial Calculations: \text{Budget Utilisation (\%)} = \frac{\text{Actual Expenditure}}{\text{Approved Budget}} \times 100 \text{Variance} = \text{Approved Budget} - (\text{Actual Expenditure} + \text{Committed Expenditure}) \text{Leverage Ratio} = \frac{\text{Additional External Capital Leveraged}}{\text{Total PFM Funding Award}}
  • Key Requirements & Acceptance Criteria:
    • FR-M05-001 to 013: Maintain multi-year budgets, budget categories, commitments, forecasts, and expenditures.
    • FR-M05-011 / AT-04: If cumulative commitments plus expenditure exceed an approved budget line, the system blocks the transaction and demands an approved budget reallocation/revision.
    • FR-M05-017 & 018: Track third-party co-funding and private sector co-investment leverage.

Module M06 — Disbursement Management

  • Purpose: Control the release of grant tranches against verified milestones and satisfied conditions precedent.
  • Priority: 14 Must Have, 1 Should Have
  • Key Requirements & Acceptance Criteria:
    • FR-M06-001 to 007: Multi-tranche disbursement schedules, milestone linkage, conditions precedent validation, payment request capture, multi-stage approval workflow, payment reference recording, and bank reconciliation.
    • FR-M06-008 & 012 / AT-05: Payment release requires: (1) Linked milestone is in VERIFIED status, (2) Conditions precedent satisfied, (3) Recipient onboarding complete. Duplicate payment approval attempts on the same tranche are blocked.

Module M07 — Milestone & Deliverable Management

  • Purpose: Track project milestones and deliverable submissions through rigorous evidence-based verification.
  • Workflow: PLANNED \longrightarrow IN PROGRESS \longrightarrow SUBMITTED \longrightarrow UNDER REVIEW \longrightarrow (REWORK REQUIRED | VERIFIED) \longrightarrow PAID \longrightarrow CLOSED.
  • Priority: 10 Must Have
  • Key Requirements & Acceptance Criteria:
    • FR-M07-001 to 005: Define contractual milestones, associate deliverables, define mandatory evidence types, monitor due dates, and allow grantees to submit evidence.
    • FR-M07-006 & 007: Authorised internal reviewers verify or reject submissions (with mandatory rework reasoning).
    • FR-M07-009: Overdue milestones are automatically flagged and escalated to Programme Managers.

Module M08 — Monitoring, Evaluation, Accountability & Learning (M&E / MEAL)

  • Purpose: Track the results chain from input to impact against baseline and target indicators.
  • Priority: 20 Must Have, 2 Should Have
  • Indicator RAG Status Thresholds:
    • 🟢 Green (On Track): \ge 90\% of target
    • 🟡 Amber (At Risk): 70\% - 89\% of target
    • 🔴 Red (Off Track): < 70\% of target
  • Key Requirements & Acceptance Criteria:
    • FR-M08-001 & 002: Support Theory of Change (ToC) and Logical Framework (Logframe) models.
    • FR-M08-003 to 018: Baseline, target, actual recording with mandatory evidence attachment (FR-M08-016 / AT-06).
    • FR-M08-021: Clearly segregate output indicators (operational delivery) from outcome indicators (behavioural/economic changes).

Module M09 — Risk Management

  • Purpose: Identify, assess, mitigate, and escalate risks across projects and portfolios.
  • Risk Categories: Strategic, Financial, Operational, Technical, Commercial, Governance, Regulatory, Reputational, Cybersecurity, Fraud, ESG, Sustainability.
  • Priority: 8 Must Have, 2 Should Have
  • Key Requirements & Acceptance Criteria:
    • FR-M09-001 to 005: Project risk registers, risk owners, Likelihood (1-5) and Impact (1-5) scoring, mitigation actions, and automated Residual Risk calculation.
    • FR-M09-006 / AT-07: High-rated risks with overdue mitigations automatically escalate to the Risk Dashboard (D05) and Programme Manager alerts.
    • FR-M09-008: Dynamic portfolio-wide risk heatmaps.

Module M10 — Compliance Management

  • Purpose: Manage statutory, contractual, financial, tax, POPIA/data protection, and safeguarding obligations.
  • Priority: 8 Should Have
  • Key Requirements: Obligation tracking, automatic due date reminders, overdue escalation (AT-08), and compliance exception registers.

Module M11 — ESG & Sustainability

  • Purpose: Track Environmental, Social, and Governance metrics alongside post-grant organizational viability.
  • Priority: 8 Should Have
  • Key Requirements: Environmental indicators (carbon offset, waste, energy), Social indicators (diversity, community development), Governance ratings, and project sustainability assessments.

Module M12 — Innovation & Technology (NSI Differentiator)

  • Purpose: Specialized tracking for South African National System of Innovation (NSI) and technology grants.
  • Priority: 9 Should Have, 4 Could Have
  • Key Requirements:
    • Innovation Passport creation (FR-M12-001).
    • Technology Readiness Level (TRL 1–9) progression tracking (Starting TRL vs Current TRL).
    • Tracking prototypes, IP assets/patents (FR-M12-006), validation tests, technology stalls (FR-M12-012), commercialisation status, licensing agreements, and follow-on venture capital.

Module M13 — Beneficiary & Stakeholder Management

  • Purpose: Register and report on direct/indirect beneficiaries for social and community development grants.
  • Priority: 9 Could Have
  • Key Requirements: Demographic categorization (gender, youth, disability, geography), outcome linkages, duplicate detection, and POPIA-compliant field-level data protection and anonymization (FR-M13-007).

Module M14 — Evidence & Document Management

  • Purpose: Secure repository for all supporting files, verifications, audit sheets, and legal documents.
  • Priority: 9 Must Have
  • Key Requirements:
    • File uploads against milestones, indicators, and change requests.
    • Metadata tracking: file versioning, hash verification, MIME type restrictions, and maximum size limits.
    • Storage integration via pre-signed S3 URLs.
    • Immutable audit trail for all document uploads, reviews, approvals, and deletions (FR-M14-007).

Module M15 — Change Control & Variation Management

  • Purpose: Governed process for adjusting project scope, budget reallocations, milestones, or timeline extensions.
  • Priority: 8 Should Have
  • Key Requirements:
    • Formal Change Requests with impact assessments and justifications (FR-M15-001 to 005).
    • Approval workflow. When approved, baseline updates are recorded while preserving original approved baselines for audit history (FR-M15-008 / AT-09).

Module M16 — Issue, Exception & Escalation

  • Purpose: Log, triage, assign, and resolve operational blockers during project execution.
  • Priority: 7 Should Have
  • Key Requirements: Issue ticketing, severity grading, owner assignment, automated escalation, linking issues to risks and milestones, and in-app requests for PM commentary.

Module M17 — Site / Field Monitoring

  • Purpose: In-field inspection and audit visits with mobile-friendly offline support.
  • Priority: 7 Should Have, 1 Could Have
  • Key Requirements: Visit scheduling, digital standardized checklists, offline observation caching and sync (NFR-010), geotagged photo capture, and automated site visit report generation.

Module M18 — Close-Out Management

  • Purpose: Formal project decommission upon settling all contractual, financial, and reporting obligations.
  • Priority: 9 Should Have
  • Key Requirements: Final technical/financial report verification, asset reconciliation, lessons learned capture, formal management closure approval, and automated handover into Post-Fund Monitoring (FR-M18-009 / AT-10).

Module M19 — Post-Fund Monitoring (Fundamental Value Driver)

  • Purpose: Track long-term economic sustainability, organizational survival, and lasting impact after grant funding has concluded.
  • Standard Follow-Up Intervals: 6 months, 12 months, 24 months, 36 months, and 60 months.
  • Priority: 10 Should Have, 2 Could Have
  • Key Metrics Tracked: Enterprise survival status, recurring revenue, net new jobs sustained, additional investment/capital raised, ongoing technology adoption, and commercial scale.

Module M20 — Portfolio Management

  • Purpose: Executive cross-project aggregation and strategic portfolio analytics.
  • Priority: 15 Must Have
  • Key Visualizations & Analytics: Funding by sector, geography (provinces/municipalities), organisation type, TRL stage, and funding programme. Real-time lists of high-risk and underperforming projects.

Module M21 — Knowledge & Learning

  • Purpose: Institutional memory capturing what works and what fails to improve future programme design.
  • Priority: 8 Could Have
  • Key Requirements: Lessons learned catalog, failure taxonomy classification, case study generation, and evaluation recommendations.

Module M22 — Reporting & Business Intelligence

  • Purpose: High-fidelity dashboards and role-tailored reporting exports (PDF, Excel).
  • Priority: 12 Must Have, 1 Should Have
  • Key Deliverable: Executive Portfolio Dashboard (D01) rendering 6 headline KPIs:
    1. Total Portfolio Value (ZAR)
    2. Active Awards Count
    3. Total Disbursed Capital (ZAR)
    4. Overall % On Track
    5. Count of Projects At Risk
    6. Total Beneficiaries Reached
    • Accompanying panels: Funding by Sector donut chart, Portfolio Risk Heatmap, Funding-to-Impact trendline, and an actionable "Projects Requiring Attention" table.

Module M23 — Administration & Configuration

  • Purpose: System setup, multi-tenant provisioning, role definitions, and taxonomy governance.
  • Priority: 3 Should Have, 8 Could Have
  • Key Requirements: Tenant isolation, user onboarding, role-permission matrix configuration (Create, Read, Update, Approve, Verify, Export - CRUAVX), and funding instrument parameterization.

Module M24 — Integration & API Management

  • Purpose: Secure interfaces for upstream intake, payment gateways, and external registries.
  • Priority: 2 Should Have, 3 Could Have, 3 Won't Have (Deep ERP, GIS map layer, and external research DBs deferred).
  • Key Requirements: REST APIs, inbound webhook handlers, API transaction logging, and retry resilience (Polly).

Module M25 — Audit & Assurance

  • Purpose: Full traceability and compliance verification for internal and external auditors.
  • Priority: 9 Should Have
  • Key Requirements: Queryable immutable audit log (AT-12), history of who approved every payment, contract version history, and full audit trails tracing any portfolio metric back to source evidence.

6. Non-Functional Requirements (NFR-001 – NFR-010)

ID Category Requirement & Acceptance Target MoSCoW
NFR-001 Performance (Pages) Standard transactional pages render within \le 3 seconds at the 95th percentile under normal load. Must Have
NFR-002 Performance (Dashboards) Standard dashboards render in \le 5 seconds; heavy reports process asynchronously with user notifications. Must Have
NFR-003 Availability Production uptime \ge 99.5\%, with higher availability targets for disbursement/payment services. Must Have
NFR-004 Security & Access Multi-factor authentication (MFA), TLS 1.3 in transit, AES-256 at rest, RBAC/ABAC enforcement, secure API tokens. Must Have
NFR-005 Scalability Capable of supporting thousands of organisations, tens of thousands of projects, and millions of evidence records. Should Have
NFR-006 Disaster Recovery Recovery Point Objective (\text{RPO}) \le 1 hour; Recovery Time Objective (\text{RTO}) \le 4 hours. Should Have
NFR-007 Multi-Tenancy Isolation Logical tenant isolation enforced across all database queries. Data from Tenant A never leaks to Tenant B. Must Have
NFR-008 Audit Trail Integrity Tamper-evident, timestamped, append-only audit trail for all state changes and financial approvals. Must Have
NFR-009 Configurability Business rules, payment triggers, and KPI libraries configurable by admins without code changes or redeployments. Should Have
NFR-010 Mobile Offline Tolerance Site visit and field data collection works seamlessly without connectivity, synchronizing once back online. Should Have

7. Cross-Module Acceptance Test Plan (AT-01 – AT-14)

These 14 end-to-end integration test scenarios must be verified before production readiness:

  1. AT-01 (Award to Contract): Valid award creates award record, rejects duplicate reference numbers, and auto-initiates Contract workflow. Attempts to disburse against an award without an executed contract are blocked.
  2. AT-02 (Contract Execution): Executing a contract with all signatures locks it as authoritative and activates downstream budget and milestone modules. Missing signatures prevent transition to EXECUTED.
  3. AT-03 (Onboarding Gate): Attempting to process a first disbursement when recipient banking details are unverified blocks the payment until onboarding is complete.
  4. AT-04 (Budget Control): Attempting to commit expenditure that exceeds the approved budget line is blocked until an approved budget revision exists.
  5. AT-05 (Milestone-Linked Disbursement): Payment requests are only eligible for approval when the linked milestone is in VERIFIED status. Duplicate approvals on the same tranche are blocked.
  6. AT-06 (Evidence & Indicators): Submitting an indicator actual value without required supporting evidence is blocked. Recording actual values automatically computes variance and RAG status.
  7. AT-07 (Risk Escalation): A High-rated risk with an overdue mitigation date is automatically escalated to the Risk Dashboard and PM alert queue.
  8. AT-08 (Compliance Tracking): A compliance obligation passing its due date without submission automatically marks overdue and triggers escalation.
  9. AT-09 (Change Control): A budget reallocation exceeding thresholds updates the approved baseline only after formal approval, preserving original baselines.
  10. AT-10 (Close-Out & Handover): Project closure is blocked if unresolved risks or missing reports remain. Upon closure confirmation, the project is enrolled in Post-Fund Monitoring (6/12/24/36/60-month schedule).
  11. AT-11 (Portfolio Roll-Up): Indicator actuals verified at project level immediately aggregate into programme and portfolio totals.
  12. AT-12 (Audit Trail Integrity): Any approval, payment, or budget edit creates an immutable, user-attributed audit entry that cannot be altered even by admins.
  13. AT-13 (Role-Based Access): Users without payment-approval roles attempting to execute an approval are denied and the attempt is logged.
  14. AT-14 (Multi-Tenancy Isolation): Users querying awards, projects, or budgets only receive data matching their authenticated TenantId.

8. Current Implementation Status & Gap Analysis

8.1 Completed Components (Existing in Codebase)

  • Domain Layer:
    • Core entities defined: Award, Contract, Project, Programme, Portfolio, Budget, Milestone, Activity, Indicator, Outcome, Beneficiary, Disbursement, Risk, Issue, ComplianceItem, SiteVisit, ChangeRequest, Evidence, AuditLog, Tenant, User, Rule, Instrument.
    • Enums for sectors, project types, evidence types, and notification channels.
  • Infrastructure Layer:
    • PostgreSQL EF Core DbContext with model configurations and initial migrations.
    • AWS S3 SDK integration for document uploads.
  • API Endpoints:
    • Endpoints grouped by slices: Grants (CreateAward, CreateContract, ExecuteContract, CreateBudget), Projects (CreateProject, CreateMilestone, CreateActivity, UpdateProjectStatus), Execution (CreateDisbursement, ApproveDisbursement, CreateRisk), Evidence (CreateEvidence, CreateIndicator), Governance (CreateTenant, CreateOrganisation).
  • UI Skeletons:
    • ConsoleUI: Routing and layout for Dashboard, Projects, Grants, Beneficiaries, Governance, Monitoring, Settings, SiteVisits.
    • PartnerUI: Grantees landing, basic project card display, and evidence upload modal shell.

8.2 Remaining Work & Implementation Priorities

To bring the project to full compliance with SRS v2.0, the following implementation tasks are prioritized:

┌─────────────────────────────────────────────────────────────────────────────┐
│ RELEASE 1: MVP CORE THREAD (Priority 1)                                     │
├─────────────────────────────────────────────────────────────────────────────┤
│ 1. Complete M01 Award Intake validation (duplicate ref check, auto-contract) │
│ 2. Enforce M02 Contract locks & signature verification gates               │
│ 3. Build M03 Recipient Onboarding gate blocking 1st disbursement (AT-03)    │
│ 4. Implement M05 Budget overrun prevention (AT-04) & formula calculations  │
│ 5. Implement M06 Disbursement conditions precedent & milestone gating(AT-05)│
│ 6. Complete M07/M14 Milestone evidence review queue & verification logic   │
│ 7. Complete M08 Indicator RAG threshold engine (Green/Amber/Red) (AT-06)   │
│ 8. Connect M20/M22 Executive Portfolio Dashboard D01 with live KPIs        │
└─────────────────────────────────────────────────────────────────────────────┘
                                      │
                                      ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ RELEASE 2: GOVERNANCE & CONTROL (Priority 2)                                │
├─────────────────────────────────────────────────────────────────────────────┤
│ 1. M09 Risk likelihood/impact matrix, residual risk, auto-escalation (AT-07)│
│ 2. M10 Compliance tracking & automated due date escalation (AT-08)          │
│ 3. M15 Change request lifecycle & baseline history preservation (AT-09)     │
│ 4. M16 Operational issue tracker with PM commentary requests                │
│ 5. M17 Mobile/offline site visit inspection checklists (NFR-010)            │
│ 6. M18 Formal project close-out checklist gate (AT-10)                      │
│ 7. M19 Post-Fund Monitoring schedule generator (6/12/24/36/60 mo intervals) │
│ 8. M25 Audit log viewer with end-to-end evidence traceability (AT-12)       │
└─────────────────────────────────────────────────────────────────────────────┘
                                      │
                                      ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ RELEASE 3: DIFFERENTIATORS & SPECIALIZED DOMAINS (Priority 3)               │
├─────────────────────────────────────────────────────────────────────────────┤
│ 1. M12 Innovation Passport & TRL (1–9) tracker (prototypes, patents)       │
│ 2. M13 Beneficiary outcomes with POPIA-compliant personal data masking     │
│ 3. M11 ESG indicators and post-funding sustainability scores                │
│ 4. M21 Knowledge repository, failure categorization & case studies          │
│ 5. M23 Dynamic funding instrument configurator (Grant, DFI, Blended, NSI)  │
└─────────────────────────────────────────────────────────────────────────────┘

9. Developer Guidelines & Coding Conventions

When adding or modifying code in this codebase, strictly follow these standards:

  1. Architecture & CQRS:
    • API endpoints in PostFundManagement.Api/Endpoints/ must remain lightweight. They parse HTTP requests, dispatch commands/queries via IMediator, and map Result<T> from FluentResults into appropriate HTTP responses (Results.Ok, Results.BadRequest, Results.Problem).
    • Business logic belongs in PostFundManagement.Application command and query handlers.
    • Entities in PostFundManagement.Domain protect their invariants. Avoid public setters where domain methods encapsulate state transitions (e.g. contract.Execute(...), disbursement.Approve(...)).
  2. Multi-Tenancy & Data Isolation:
    • Every query must respect the ambient TenantId. Never execute queries or mutations without verifying tenant boundaries (NFR-007, AT-14).
  3. Auditability (BR-007, AT-12):
    • Any change modifying financial amounts, approvals, contractual states, risk ratings, or project statuses must generate an AuditLog entry detailing UserId, Timestamp, Action, PreviousValue, and NewValue.
  4. Data Protection (POPIA):
    • Recipient banking details (AccountNumber, BranchCode) and beneficiary personal information must be encrypted or masked at rest and restricted via field-level authorization.
  5. Frontend State & API Sync:
    • Use TanStack Query hooks for server caching, optimistic updates, and cache invalidation.
    • Forms must use react-hook-form with zod schemas enforcing mandatory fields at input time.
  6. Frontend Package Management (Yarn Mandatory):
    • Always use yarn instead of npm across all frontend projects (PostFundManagement.ConsoleUI and PostFundManagement.PartnerUI).
    • Never run npm install, npm run, or create/modify package-lock.json.
    • Standard commands:
      • yarn install or yarn to install dependencies.
      • yarn add <pkg> / yarn add -D <pkg> to add dependencies.
      • yarn build to trigger production builds.
      • yarn dev to launch local development servers.
      • yarn test for executing frontend tests.

10. Role and Claim Architecture (RBAC & Permissions)

The platform implements a fine-grained, claim-based authorization model across both the backend APIs and frontend web applications. This model is partially implemented in PostFundManagement.ConsoleUI/src/auth/rbac.ts and governs screen access, action buttons, status changes, and endpoint execution.

10.1 Role Claims Inventory

The system defines 15 distinct role claims corresponding to operational domains and feature slices:

Claim Key Label Scope & Description
dashboard Executive Dashboard Strategic macro metrics, portfolio KPIs, and ISO 31000 health views
governance Governance & Structure Holding entities, investment portfolios, programmes, and statutory frameworks
projects_milestones Projects & Milestones Work breakdown structure (WBS), scheduled milestones, activities, and progress
grant_financials Grant & Financials Funding awards, budget line allocations, tranches, and disbursements
evidence_risks M&E, Evidence & Risks Evidence verification queue, indicator actuals, and ISO 31000 risk registers
beneficiaries Beneficiaries Target demographics, POPIA compliance tracking, and direct/indirect outcomes
change_management Change Management Contractual change requests, budget reallocations, timeline variations, and issues
site_visits Site Visits Physical field inspection reports, checklist findings, and geotagged evidence
settings_invites Settings Invites Onboarding invitations, applicant verification links, and notifications
settings_users Settings Users User account provisioning, role assignments, claims, and lockout management
settings_auditlog Settings Audit Log Immutable system-wide activity ledger and audit trail entries
settings_notifications Settings Notifications SMTP engines, notification dispatch logs, and delivery channels
tenant_self_service Tenant Self-Service Tenant-level profile parameters, local branding, and sub-user management
tenant_management Tenant Management System-level multi-tenant directory, partition allocation, and cross-tenant isolation
unrestricted Unrestricted Access System-wide root administration; bypasses granular claim checks

10.2 Claim Action Values & Permitted Capabilities

Each claim assigned to a user or role can be granted specific permission levels. The permitted action assignments are:

  • view: Can view records based on role (read-only access to entities, grids, and details).
  • add: Can add/create records or submit feedback; can change workflow statuses other than approve. (Includes view capability).
  • update: Can use approve status changes in dropdowns and action bars. (Includes view and add capabilities).
  • export: Can click export buttons (e.g. Spreadsheet/Excel .xlsx, PDF reports). (Includes view capability).
  • all: Can perform all operations (view, add, update, export). Full capability within the claim domain.

10.3 Default Role Claim Map

The following baseline mappings define the claims assigned to system roles:

Role Assigned Claims Permitted Scope Summary
Admin unrestricted Full system-wide control across all modules, configuration, and tenant partitions.
Executive dashboard, governance, projects_milestones, grant_financials, evidence_risks, tenant_self_service Strategic oversight, macro KPIs, portfolio health, financials, and high-level project monitoring.
Supervisor dashboard, projects_milestones, grant_financials, evidence_risks, beneficiaries, change_management, site_visits, settings_invites, settings_users Operational review, verification of milestones/evidence, site inspections, user/invite administration, and change review.
Manager dashboard, projects_milestones, grant_financials, evidence_risks, beneficiaries, change_management, site_visits, settings_invites, settings_users, settings_auditlog, settings_notifications, tenant_self_service Comprehensive operational management, including audit logs, notifications configuration, self-service tenancy, and full project/grant controls.
Contributor projects_milestones, grant_financials, evidence_risks, beneficiaries, change_management, site_visits, settings_invites Working-level execution: creating activities, submitting milestone evidence, capturing risks, drafting change requests, and logging site observations.

10.4 Restriction Invariant & Enforcement Rules

  • Explicit Restriction Rule: If a role claim is not present, the user is fully restricted from performing any actions associated with that claim.
  • Partner Role Isolation: The external partner (grantee/recipient) role is strictly confined to PostFundManagement.PartnerUI. Any attempt by a partner user to authenticate or access PostFundManagement.ConsoleUI routes is completely blocked.
  • Granular Action Checking in UI:
    • Pages and routes are protected via <ClaimProtectedRoute claim="..." action="..." />.
    • Components query capabilities using const { canView, canAdd, canApprove, canExport } = useRbac().
    • Approval buttons and status transitions requiring update are disabled or hidden if the user's claim assignment does not permit update or all.
    • Export buttons (.xlsx, .pdf) are disabled or hidden if the user's claim assignment does not permit export or all.
  • Backend API Enforcement:
    • Endpoints inspect JWT claims (role_claim:<claim_name> or array claims) to enforce authorization before executing command/query handlers.
    • Violations immediately return 403 Forbidden and log an audit security event (NFR-004, AT-13).

10.5 Maker-Checker Segregation of Duties Pattern

In addition to RBAC role and claim constraints, the platform enforces the Maker-Checker Pattern across all operational lifecycle modules:

  • Separation of Duty Rule: The user who creates or drafts a record (the "Maker") is strictly barred from verifying or approving that record (the "Checker"), regardless of their role or administrative privileges.
  • Attribution: Every domain entity immutably captures the creator's user identifier in createdBy (Guid/Subject ID resolved from context.GetUserId()).
  • Evaluation Formula: \text{Can Approve} = \text{Has 'update' claim permission} \land (\text{Authenticated User ID} \neq \text{Record createdBy})
  • Client UI Enforcement:
    • Components use const { canApproveRecord, isMaker, getApprovalRestrictionReason } = useRbac().
    • Status transition dropdowns disable the Approved / Active option when !canApproveRecord(claim, record.createdBy) with contextual indicator (Maker-Checker: Cannot approve own record).
    • Fast-track approval buttons (budget lines, disbursement tranches, evidence submissions, change requests) are hidden or marked with a Maker-Checker warning badge when inspected by the creator.

11. Functional API Surface & Endpoint Catalog

The backend exposes a fully functional RESTful API hosted at https://api.ifri.khongisa.co.za/ (OpenAPI v1 specification available at /openapi/v1.json). All resource routes follow the versioned prefix /v1/api/.

11.1 Governance Endpoints (24 Endpoints)

Governs institutional hierarchies, multi-tenant boundaries, investment portfolios, statutory programmes, and portfolio dashboard metrics.

  • Executive Dashboard & Valuation:
    • GET /v1/api/governance/dashboard (dashboard): Returns executive macro metrics, ISO 31000 portfolio health status, and top-level KPI aggregations.
    • GET /v1/api/governance/portfolios/{portfolioId}/value (getporfoliovalue): Aggregates total approved budget and disbursed values across all projects in a portfolio.
    • GET /v1/api/governance/programmes/{programmeId}/value (getprogrammevalue): Calculates total project budget values under a specific programme.
  • Funding Instruments (BR-006):
    • POST /v1/api/governance/instruments (createinstrument): Registers a new funding instrument (Grant, DFI, Impact Investment, Blended Finance, NGO, Innovation).
    • PUT /v1/api/governance/instruments (updateinstrument): Modifies instrument parameters and rules.
    • GET /v1/api/governance/instruments/{programmeId} (getinstruments): Lists all funding instruments configured for a programme.
  • Organisations & Recipient Master Data:
    • POST /v1/api/governance/organisations (createorganisation): Creates a new recipient or partner organisation profile.
    • GET /v1/api/governance/organisations (getorganisations): Paginated directory of registered organisations.
    • GET /v1/api/governance/organisations/{organisationId} (getorganisation): Retrieves organisation details, verification state, and banking references.
    • PUT /v1/api/governance/organisations (updateorganisation): Updates organisation master data.
  • Portfolios & Programmes:
    • POST /v1/api/governance/organisations/portfolio (createportfolio): Creates an investment portfolio linked to an organisation.
    • GET /v1/api/governance/organisations/{organisationId}/portfolio (getorganisationportfolios): Lists portfolios for an organisation.
    • POST /v1/api/governance/organisations/portfolios (getportfolios): Retrieves all active investment portfolios.
    • GET /v1/api/governance/portfolios/{portfolioId} (getportfolio): Retrieves portfolio details and linked programmes.
    • PUT /v1/api/governance/organisations/portfolio (updateportfolio): Updates portfolio properties.
    • GET /v1/api/governance/portfolios/{portfolioId}/programmes (getportfolioprogrammes): Retrieves programmes belonging to a portfolio.
    • GET /v1/api/governance/programmes/{portfolioId} (getprogrammes): Lists all programmes linked to a portfolio.
  • Multi-Tenancy Partitioning (NFR-007, AT-14):
    • POST /v1/api/governance/tenants (createtenant): Registers a new system tenant (e.g. government department, DFI).
    • GET /v1/api/governance/tenants (gettenantsretrieval): Lists all system tenants.
    • GET /v1/api/governance/tenants/{tenantId} (gettenants): Retrieves a single tenant profile.
    • PUT /v1/api/governance/tenants (updatetenant): Modifies tenant parameters and subscription status.
    • GET /v1/api/governance/tenants/{tenantIdHash}/organisations (gettenantorganisationsretrieval): Returns all organisations owned and isolated within a specific tenant partition.
  • Budgets:
    • PUT /v1/api/grants/budgets (updatebudget): Updates budget allocations and approvals.

11.2 Grants, Awards & Contracting Endpoints (14 Endpoints)

Enforces the mandatory controlled lifecycle starting at approved award (BR-001, BR-002).

  • Award Intake (M01):
    • POST /v1/api/grants/awards (createaward): Registers an approved funding award with external reference, amount, period, recipient, and objectives. Automatically triggers initial draft contract creation (BR-002, AT-01).
    • GET /v1/api/grants/awards (getawards): Lists all registered awards.
    • GET /v1/api/grants/awards/{awardId} (getaward): Retrieves detailed award metadata and downstream project links.
  • Budget Management (M05):
    • POST /v1/api/grants/budgets (createbudget): Allocates approved budget lines against an award.
    • GET /v1/api/grants/budgets (getbudgets): Lists all project and award budgets.
    • GET /v1/api/grants/budgets/{budgetId} (getbudget): Retrieves specific budget line allocations, commitments, and actual expenditures.
  • Contract & Agreement Governance (M02):
    • POST /v1/api/grants/contracts (createcontract): Creates a contract record with configurable clauses and obligations.
    • GET /v1/api/grants/contracts (getcontracts): Lists contracts across the portfolio.
    • GET /v1/api/grants/contracts/{contractId} (getcontract): Retrieves contract metadata, versioning, and execution status.
    • PUT /v1/api/grants/contracts (updatecontract): Updates draft contract terms prior to execution.
    • POST /v1/api/grants/contracts/{contractId} (uploadcontract): Securely uploads signed agreement artifacts to S3 object storage.
    • GET /v1/api/grants/contracts/{contractId}/securelink (getcontracturl): Generates a time-limited pre-signed download URL (default 15 minutes) for the executed contract document.
    • POST /v1/api/grants/contracts/execute (executecontract): Formally executes agreement with digital signatures, locking version 1.0 as the authoritative legal baseline (AT-02).
  • Evidence URLs:
    • GET /v1/api/evidence/{evidenceId}/securelink (getevidenceurl): Generates pre-signed temporary S3 download link for verified evidence files.

11.3 Projects & Operational Delivery Endpoints (43 Endpoints)

Controls the work breakdown structure (WBS), milestones, field monitoring, and project governance (M04, M07, M10, M13, M15, M16, M17).

  • Project Management (M04):
    • POST /v1/api/projects (createproject): Creates a new project under an approved award and programme.
    • GET /v1/api/projects (getprojects): Lists all active and historical projects.
    • GET /v1/api/projects/{projectId} (getproject): Detailed view of project WBS, timeline, status, and financials.
    • PUT /v1/api/projects (updateproject): Updates project details.
    • PUT /v1/api/projects/status (updateprojectstatus): Updates project status across lifecycle states (ACTIVE, AT RISK, COMPLETED, CLOSED).
    • POST /v1/api/projects/programme (createprogramme), PUT /v1/api/projects/programme (updateprogramme), GET /v1/api/projects/programme/{programmeId} (getprogramme), GET /v1/api/projects/programme/{page}/{pageSize} (getprogrammesprojects), GET /v1/api/projects/{programmeId}/programme (getprogrammeprojects).
  • Activities & WBS (M04):
    • POST /v1/api/projects/activities (createactivity): Adds an activity linked to a project milestone.
    • GET /v1/api/projects/activities/{activityId} (getactivity): Retrieves specific activity details.
    • GET /v1/api/projects/{projectId}/activities (getactivities): Lists all WBS activities for a project.
  • Milestone & Deliverables (M07):
    • GET /v1/api/projects/{projectId}/milestones (getprojectmilestones): Lists contractual milestones for a project.
    • GET /v1/api/projects/milestones/{milestoneId} (getmilestone): Retrieves milestone requirements, due dates, and evidence attachments.
    • GET /v1/api/projects/milestones/{page}/{pageSize} (getmilestones): Paginated milestone repository.
    • PUT /v1/api/projects/milestone (updatemilestone): Updates milestone dates, statuses, and verification decisions.
  • Beneficiaries & Social Outcomes (M13):
    • POST /v1/api/projects/beneficiaries (createbeneficiary): Registers beneficiary group or target demographic.
    • GET /v1/api/projects/beneficiaries/{beneficiaryId} (getbeneficiaries): Retrieves beneficiary details.
    • GET /v1/api/projects/{projectId}/beneficiaries (getbeneficiariesretrieval): Lists beneficiaries for a project.
    • PUT /v1/api/projects/beneficiaries (updatebeneficiary): Updates beneficiary records.
    • POST /v1/api/projects/beneficiaris/outcomes (createoutcome): Links an achieved outcome to a beneficiary group.
    • GET /v1/api/projects/outcomes/{outcomeId} (getoutcome): Retrieves outcome details.
    • GET /v1/api/projects/outcomes/{beneficiatyId} (getoutcomes): Retrieves outcomes for a beneficiary.
    • PUT /v1/api/projects/outcomes (updateoutcome): Updates outcome status.
  • Compliance Management (M10):
    • POST /v1/api/projects/complianceitems (createcomplianceitem): Logs a statutory, financial, or contractual compliance obligation.
    • GET /v1/api/projects/complianceitems/{complianceItemId} (getcomplianceitem): Retrieves compliance item status.
    • GET /v1/api/projects/complianceitems/{projectId} (getcomplianceitems), GET /v1/api/projects/{projectId}/complianceitems: Lists project compliance tasks.
    • PUT /v1/api/projects/complianceitems (updatecomplianceitem): Updates compliance satisfaction and attached evidence.
  • Risk & Issue Governance (M09, M16):
    • GET /v1/api/projects/{projectId}/risks (getprojectrisks): Lists risks registered against a project.
    • POST /v1/api/projects/issues (createissue): Logs an operational issue or escalation blocker.
    • GET /v1/api/projects/issues/{issueId} (getissue): Retrieves issue details and resolution history.
    • GET /v1/api/projects/{projectId}/issues (getissues): Lists all issues for a project.
    • PUT /v1/api/projects/issues (updateissue): Updates issue severity, resolution, or escalation owner.
  • Change Control & Variations (M15, AT-09):
    • POST /v1/api/projects/changerequests (createchangerequest): Submits a formal change request (scope, budget, timeline).
    • GET /v1/api/projects/changerequests/{changeRequestId} (getchangerequest): Retrieves change impact evaluation.
    • GET /v1/api/projects/{projectId}/changerequests (getchangerequests): Lists project change requests.
    • PUT /v1/api/projects/changerequests (updatechangerequest): Records review, approval, or rejection of change request.
  • Site Visits & Field Inspections (M17):
    • POST /v1/api/projects/sitevisits (createsitevisit): Records scheduled or completed field monitoring visit.
    • GET /v1/api/projects/sitevisits/{siteVisitId} (getsitevisit): Retrieves site visit checklist, inspector observations, and findings.
    • GET /v1/api/projects/{projectId}/sitevisits (getsitevisits): Lists site visits conducted on a project.
    • PUT /v1/api/projects/sitevisits (updatesitevisit): Updates inspection findings and corrective actions.
  • Indicators Linkage (M08):
    • GET /v1/api/projects/indicators/project/{projectId} (getprojectindicators): Lists all M&E indicators tracked for a project.

11.4 Evidence & M&E Endpoints (11 Endpoints)

Handles evidence verification and quantitative results measurement (M08, M14).

  • Supporting Evidence Repository (M14):
    • PUT /v1/api/evidence/file (createevidence): Multipart/stream file upload directly to S3 storage, registering SHA-256 hashes, MIME types, and entity linkages.
    • PUT /v1/api/evidence (updateevidence): Updates evidence metadata, verification status, and review notes.
    • GET /v1/api/evidence/{evidenceId} (getevidence): Retrieves evidence record details and download references.
    • GET /v1/api/evidence/files/{page}/{pageSize} (getevidencefiles): Paged catalogue of all evidence assets across the system.
    • GET /v1/api/evidence/milestone/{milestoneId} (getmilestoneevidence): Retrieves all supporting files attached to a specific milestone.
    • GET /v1/api/evidence/{projectId} (getprojectevidence): Retrieves all evidence files registered for a project.
  • M&E Indicators (M08):
    • POST /v1/api/evidence/indicators (createindicator): Defines an indicator with baseline, target, unit of measure, and results level.
    • GET /v1/api/evidence/indicators/{page}/{pageSize} (getindicators): Lists all indicators across programmes.
    • GET /v1/api/evidence/indicators/{indicatorId} (getindicator): Detailed view of an indicator's history.
    • POST /v1/api/evidence/indicators/{indicatorId}/actuals/{actualAmount} (recordindicatoramount): Records verified actual results; automatically evaluates variance and RAG status (Green \ge 90\%, Amber 70\text{--}89\%, Red < 70\%).
  • Milestone Creation:
    • POST /v1/api/evidence/milestones (createmilestonerequest): Creates milestone requirements linked to deliverables and evidence rules.

11.5 Execution & Financial Control Endpoints (13 Endpoints)

Strict disbursement release controls and risk matrices (M06, M09).

  • Disbursement Tranche Releases (M06, BR-003, AT-05):
    • POST /v1/api/execution/disbursements (createdisbursement): Creates a planned disbursement tranche linked to a milestone.
    • GET /v1/api/execution/disbursements (getdisbursements): Lists all disbursement schedules across awards.
    • GET /v1/api/execution/disbursements/{disbursementId} (getdisbursement): Retrieves tranche amount, bank details, and conditions precedent status.
    • PUT /v1/api/execution/disbursements (updatedisbursement): Modifies payment schedule details.
    • POST /v1/api/execution/disbursements/{disbursementId}/approve (approvedisbursement): Authorises tranche release. Strictly requires verified milestone evidence and completed onboarding; blocks duplicate payment approvals.
  • Risk Register (M09, AT-07):
    • POST /v1/api/execution/risks (createrisk): Registers a project risk with likelihood, impact, and mitigation plan.
    • GET /v1/api/execution/risks (getrisks): Lists risks across the portfolio.
    • GET /v1/api/execution/risks/{riskId} (getrisk): Detailed risk profile including residual risk score.
    • PUT /v1/api/execution/risks (updaterisk): Updates risk status, mitigation progress, or escalates high risks.
  • Programme Rules & Constraints (M04, M23):
    • POST /v1/api/execution/rules (createrule): Configures an operational rule or threshold for a programme.
    • PUT /v1/api/execution/rules (updaterule): Amends a rule with auto-incrementing version control.
    • GET /v1/api/execution/rules/{ruleId} (getrule): Retrieves rule definition.
    • POST /v1/api/execution/programmes/rules (getrules): Lists all active rules for a programme.

11.6 Communications & Notifications Endpoints (6 Endpoints)

  • Onboarding & Invitations (M03):
    • POST /v1/api/communications/invites (sendinvite): Dispatches onboarding invitation via Email, SMS, or WhatsApp.
    • GET /v1/api/communications/invites/{page}/{pageSize} (getinvites): Lists sent invitations.
    • GET /v1/api/communications/invites/{applicantUserId} (getapplicantinvites): Lists invitations sent to an applicant.
    • GET /v1/api/communications/invites/applicants/{applicantUserId} (getapplicantinvite): Retrieves single invitation details.
    • GET /v1/api/communications/applicants/{applicantUserId}/all (getiapplicantdata): Aggregates all onboarding communications and status for an applicant.
  • Notification Logging:
    • GET /v1/api/communications/notifications/{organisationId} (getnotifications): Retrieves notification dispatch and delivery logs.

11.7 User Administration & Identity Endpoints (15 Endpoints)

Directly integrated with the hosted Skoruba Duende security system.

  • User Directory & Synchronization:
    • POST /v1/api/users/{page}/{pageSize} (getusers): Paged, searchable directory of system users with filtering by status and email.
    • GET /v1/api/users/{userId} (getuser): Retrieves user metadata, assigned roles, and tenant associations.
    • GET /v1/api/users/{userId}/status/{status} (updateuserstatus): Updates user activity status (Active, Inactive, Suspended).
    • POST /v1/api/integration/sync (syncuser): Triggers background synchronization of users and organisations between STS and local DB.
  • User Provisioning & Session Control:
    • POST /v1/api/users/{tenantId}/{idNumber} (createuser): Provisions a new user account in both Skoruba IdentityServer and the local database.
    • PUT /v1/api/users/{userId}/session/lockout/{disable} (setuserlockout): Immediately locks or unlocks user account access in the identity store.
    • PUT /v1/api/users/{userId}/session/logout (loguserout): Invalidates active user security tokens and ends active sessions.
  • Role & Claim Assignments (RBAC):
    • GET /v1/api/users/{userId}/roles (getuserroles): Retrieves roles assigned to a user.
    • PATCH /v1/api/users/{userId}/roles/{roleId} (adduserrole): Assigns a system role to a user.
    • DELETE /v1/api/users/{userId}/roles/{roleId} (deleteuserrole): Revokes a system role from a user.
    • GET /v1/api/users/system/roles (getsystemroles): Discovers all roles configured in the security authority.
    • GET /v1/api/users/system/roles/{roleId}/claims (getroleclaims): Discovers claims associated with a role.
    • GET /v1/api/users/{userId}/claims (getuserclaims): Retrieves direct claims assigned to a user.
    • PATCH /v1/api/users/user/claims (adduserclaim): Attaches an explicit claim value to a user.
    • DELETE /v1/api/users/{targetUserId}/claims/{claimId} (deleteuserclaim): Removes a claim from a user.

12. Standardized API Response Handling & ProblemDetails Contract

12.1 ProblemDetails Error Protocol

When an endpoint experiences a validation failure, business rule violation, or internal error, it returns a standard RFC 7807 / ASP.NET Core ProblemDetails object:

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "Error",
  "status": 400,
  "detail": "Approved funding amount must be greater than zero,Funding start date must precede end date"
}

12.2 Critical Client-Side Parsing Rule

In PostFundManagement.Application/Extensions/General.cs, error lists from FluentResults are joined into the Detail property using a comma separator:

public static ProblemDetails ToProblems(this IReadOnlyList<IError> errors, int statusCode = StatusCodes.Status400BadRequest) => new()
{
    Detail = string.Join(',', errors.Select(e => e.Message)),
    Status = statusCode,
    Title = "Error",
};

Important

Crucial Rule for Frontends (PartnerUI and ConsoleUI): The detail property contains the actual human-readable error messages. Frontends must never display raw generic error titles. Instead, frontends must:

  1. Inspect error.response.data.detail.
  2. Split the string by , (comma) and trim whitespace from each item.
  3. Display each item as an individual bullet point or toast message.

This behavior is standardized in PostFundManagement.PartnerUI/src/services/api/errorHelper.ts (extractApiErrors and formatApiErrorMessage) and must be maintained across all form handlers.


13. Hosted Skoruba Duende Security System Integration

The platform relies on a dedicated, hosted Skoruba Duende IdentityServer deployment for authentication, machine-to-machine trust, and identity governance.

13.1 Infrastructure & Authority Configuration

  • STS Authority Endpoint: https://sts.security.khongisa.co.za
  • Skoruba Administration API: https://api.security.khongisa.co.za/ (Connection string SkorubaApi)
  • API Audience: pfm (configured under SecuritySettings:Audience)

13.2 API Security Architecture

  1. Interactive JWT Token Validation:
    • Configured via AddApiSecurity in PostFundManagement.Application/Extensions/Security.cs.
    • Validates tokens against https://sts.security.khongisa.co.za using asymmetric key validation.
    • options.MapInboundClaims = false: Preserves original claim types (role, role_claim:*, sub, email) without automatic legacy mapping.
    • Enforces Constants.MainScope (pfm scope) on all protected endpoints.
  2. Machine-to-Machine Administration (Client Credentials):
    • Configured via AddSkorubaApiService using ClientCredentialsTokenHandler.
    • Automatically requests and caches client credentials tokens from https://sts.security.khongisa.co.za/connect/token.
    • Communicates with https://api.security.khongisa.co.za/ via Refit / typed HttpClient to manage users, roles, and claims programmatically.
    • Outbound calls are guarded by Polly exponential retry policies (options.Retry.MaxRetryAttempts = 3).
  3. Session Revocation & Lockout:
    • PUT /v1/api/users/{userId}/session/logout and /lockout/{disable} invoke ISkorubaUserService to trigger immediate session invalidation in Duende STS, ensuring instant token revocation.

14. PartnerUI Completion Roadmap

The client has confirmed that the backend implementation conforms with the SRS v2.0 requirements. In order to complete PostFundManagement.PartnerUI (the external grantee/recipient portal), the following UI features must be finalized:

  1. Recipient Onboarding Flow (Module M03):
    • Interactive wizard for grantee onboarding: organisation profile verification, key personnel registration, and banking details submission.
    • Tax clearance and statutory document uploads with real-time status feedback.
  2. Milestone Evidence Submission Wizard (Modules M07 / M14):
    • Interactive deliverable tracker matching wireframe Figure 10 in the SRS.
    • File upload modal with pre-validation (allowed extensions: PDF, JPG, DOCX, XLSX; max size: 25MB).
    • Reviewer status feedback display (Submitted \to Under Review \to Verified or Rework Required with reviewer commentary).
  3. Disbursement Tranche Drawdown Requests (Module M06):
    • Partner drawdown dashboard displaying eligible, submitted, and disbursed funding tranches.
    • Drawdown request form with automatic conditions precedent checklist verification.
  4. M&E Indicator Actuals Reporting (Module M08):
    • Form enabling partners to report periodic actuals against contractual indicator targets.
    • Mandatory evidence attachment validation prior to submitting actual metric values.
  5. Post-Fund Monitoring Reporting (Module M19):
    • Longitudinal survey forms triggering at 6, 12, 24, 36, and 60 months post project close-out.
    • Tracking enterprise survival, annual turnover, net jobs maintained, and ongoing commercialisation.
  6. Consistent Error & Notification Handling:
    • Integrate extractApiErrors() and formatApiErrorMessage() from errorHelper.ts across all PartnerUI mutation forms and modals.

15. Hashids Architecture & Frontend Conformance Insights

The platform uses Hashids to obfuscate internal sequential database primary keys (int64 / long and int32), mitigating entity enumeration, sequential parameter tampering, and unauthorized data scraping.

15.1 Backend Hashids Topology & Error Behavior

  • Implementation: Managed by HashService (PostFundManagement.Application/Services/Shared/HashService.cs) using the Hashids.net (v1.7.0) library.
  • Shared Configuration (HasherSettings):
    • Salt: Sourced from configuration (HasherSettings:Salt or HASHER_SALT), defaulting to: "TGlnaHQgQ2hhcm1zLCBNaWRyYW5kQm9va3MgbWFrZSBsb3RzIG9mIG1vbmV5IGFuZCBhcmUgYSBzdWNjZXNzZnVsIHZpcmFsIHN0b3J5IGluIFNvdXRoIEFmcmljYQ=="
    • Min Hash Length: HasherSettings:MinHashLength or HASHER_MIN_HASH_LENGTH (default: 11).
  • Core Service Methods:
    • HashEncodeLongId(long id) / HashEncodeIntId(int id): Encodes integers into obfuscated HashID strings (e.g. 1 \to kY5aB9m...).
    • DecodeLongIdHash(string hash) / DecodeIntIdHash(string hash): Decodes HashID strings back into database IDs.
    • HashEncodeHex(string hex) / DecodeHexHash(string hex): Encodes and decodes arbitrary alphanumeric strings (such as South African 13-digit National ID numbers and passport numbers) via hexadecimal conversions.
  • Security & Failure Contract (IdDecodeError \to 403 Forbidden):
    • When an endpoint receives an invalid, corrupted, or tampered HashID, DecodeLongIdHash or DecodeHexHash fails and returns an IdDecodeError.
    • Across Minimal API endpoints (e.g. GetProjectEndpoint, GetTenantOrganisationsEndpoint, CreateUserEndpoint), IdDecodeError is mapped explicitly:
      var r when r.HasError<IdDecodeError>() => Results.Forbid()
      
    • As a result, passing an unhashed raw numeric ID (e.g. /v1/api/projects/1) or an invalid hash immediately yields HTTP 403 Forbidden, preventing route scanning and guessing attacks.

15.2 Path Parameters vs. Request Body JSON Payloads (The Core Rule)

A critical distinction governs the communication between frontends and the API:

┌────────────────────────────────────────────────────────────────────────────────────────┐
│ URL Path Parameters: HASHID STRINGS ONLY                                               │
│ E.g. GET /v1/api/projects/{projectId} ──> expects "kY5aB9m..." (HashID)               │
│ E.g. GET /v1/api/governance/tenants/{tenantIdHash}/organisations ──> expects HashID   │
│ E.g. POST /v1/api/users/{tenantId}/{idNumber} ──> expects HashID + Hex HashID          │
└────────────────────────────────────────────────────────────────────────────────────────┘
                                            vs.
┌────────────────────────────────────────────────────────────────────────────────────────┐
│ Request Body JSON Payloads: NATIVE NUMERIC INTEGERS ONLY                               │
│ E.g. POST /v1/api/projects ──> { "programmeId": 12, "name": "..." } (int64 integer)   │
│ E.g. PUT /v1/api/projects/status ──> { "projectId": 45, "status": 2 } (int64 integer) │
│ OpenAPI Schema: { "type": "integer", "format": "int64", "pattern": "^-?(0|[1-9]\\d*)$"} │
└────────────────────────────────────────────────────────────────────────────────────────┘

Warning

Payload Failure Trap: If an alphanumeric HashID string is passed inside a JSON request body where an integer is expected, ASP.NET Core deserialization and OpenAPI validation will reject the request with HTTP 400 Bad Request. Conversely, if a raw integer is passed in a URL route parameter where a HashID is expected, DecodeLongIdHash will fail to decode, returning HTTP 403 Forbidden.

15.3 How ConsoleUI Successfully Conforms

PostFundManagement.ConsoleUI achieves seamless, defect-free conformance through an isolated, two-pronged abstraction layer:

1. Instant Client-Side Deterministic Hasher (src/utils/hashids.ts & src/services/toolsService.ts)

  • ConsoleUI bundles the JavaScript hashids library initialized with the exact same salt and minHashLength as the backend.
  • All encoding and decoding occurs synchronously and client-side with zero network round-trips.
  • isHashId(str) guards against accidental double-encoding if an identifier has already been hashed.
  • Specialized encoders:
    • encodeEntityId(id) / toolsService.resolveProjectId(id): Encodes database numeric IDs for URL route parameters.
    • encodeIdNumberHex(idNumber): Encodes South African national ID numbers (13 digits), passports, and alphanumeric keys into hex-encoded Hashids for the /api/users/{tenantId}/{idNumber} provisioning endpoint.

2. Request Body Sanitization via toIntegerId() in src/services/api.ts

  • In api.ts, outgoing API calls systematically decouple route parameters from JSON payload bodies:
    • Route Parameters: Routed via resolveProjectId(projectId) or encodeEntityId(id) to produce valid HashIDs.
    • Payload Bodies: Routed via toIntegerId(payload.id) (toolsService.decodeToNumberSync(id)).
    export const toIntegerId = (id: number | string | null | undefined): number => {
      return toolsService.decodeToNumberSync(id);
    };
    
    • toIntegerId detects whether the passed ID is a HashID, a numeric string, or a number, and safely decodes it into a canonical integer number before Axios serializes the JSON request body.

3. Component Decoupling

  • UI components (ProjectsPage.tsx, GrantsPage.tsx, SettingsPage.tsx, CreateUserModal.tsx) do not perform manual hash transformations or math.
  • They simply pass identifiers into api.ts methods, which transparently route HashIDs to URLs and integer numbers to request bodies.

15.4 Conformance Guidelines for PartnerUI Completion

When implementing remaining features in PostFundManagement.PartnerUI, follow the proven ConsoleUI pattern:

  1. Route Parameters: Use hashParam(id) (already established in PartnerUI/src/services/api/endpoints.ts) for all URL paths.
  2. Payload Bodies: Always decode incoming IDs to integer numbers when constructing POST/PUT request bodies for Milestones, Evidences, Drawdown requests, and Indicator actuals.
  3. Hex ID Encoding: When capturing user national IDs or identification numbers, apply encodeIdNumberHex() before invoking user endpoints.
  4. 403 Forbidden Detection: Treat HTTP 403 on parameterized GET routes as a potential IdDecodeError caused by an invalid or unhashed route parameter.

16. .NET Project Architecture, Coding Style & Interceptor-Based Governance

The backend is built with .NET 10.0 (C# 13) using Domain-Driven Design (DDD), Clean Architecture, and Aspect-Oriented Programming (AOP).

16.1 Layered Architecture & Project Responsibilities

PostFundManagement.Api
  ├── Endpoints/             # Minimal API slices implementing IEndpoint
  ├── Filters/               # Endpoint filters (ActiveUserFilter, UserRoleFilter)
  └── Program.cs             # Middleware pipeline, DI composition, OpenAPI transformer
        │
        ▼ (References)
PostFundManagement.Application
  ├── Services/              # Domain services grouped by slice (Projects, Grants, etc.)
  │    ├── [Slice]Service.cs        # Actual business logic
  │    └── [Slice]CacheService.cs   # HybridCache decorator
  ├── Interceptors/          # Castle DynamicProxy AOP (LoggingInterceptor, AuditInterceptor)
  ├── Extensions/            # Service registration & DI wiring (Services.cs, Security.cs)
  └── Abstractions/          # Service interfaces (IProjectService, IGrantsService)
        │
        ├─────────────────────────────┐
        ▼                             ▼
PostFundManagement.Infrastructure   PostFundManagement.Domain
  ├── Database/                       ├── Entities/        # Aggregate roots & entities
  │    ├── ApplicationDbContext.cs    ├── Enums/           # System taxonomies
  │    └── Configurations/            ├── Errors/          # Domain errors (IError)
  ├── Security/                       ├── Attributes/      # [Log], [Audit], [ApiVersionTarget]
  └── Migrations/                     └── Abstractions/    # IEntity, IAggregateRoot, IService

16.2 Architectural Balance: Clean Architecture, Vertical Slices, SOLID & DRY

The PFM backend strikes a deliberate, pragmatic balance between Clean Architecture, Vertical Slice Architecture, SOLID principles, and the DRY (Don't Repeat Yourself) principle:

  1. Clean Architecture with Vertical Slices:

    • Domain Layer: Completely isolated aggregate roots, entities, taxonomies (Enums), domain errors (IError), and abstractions. It has no dependencies on database technologies or presentation frameworks.
    • Infrastructure Layer: Encapsulates EF Core configurations, PostgreSQL mappings, and data protection.
    • Application & Api Layers by Vertical Slice: Features are organized cleanly into domain feature slices (Projects, Grants, Execution, Governance, Evidence, Communications, Users). Each slice groups its commands/queries, records (Records.cs), core service ([Slice]Service), hybrid cache decorator ([Slice]CacheService), and Minimal API endpoints ([Action]Endpoint.cs), preserving strong cohesion without code bloat.
  2. Strict Adherence to SOLID Design Principles:

    • Single Responsibility Principle (SRP):
      • [Slice]Service: Implements pure business logic, database transactions, and entity mapping.
      • [Slice]CacheService: Implements read caching and mutation eviction via HybridCache.
      • LoggingInterceptor: Exclusively manages structured logging upon failure.
      • AuditInterceptor: Exclusively manages regulatory audit logging upon success (BR-007).
      • [Action]Endpoint: Exclusively handles route binding, OpenAPI documentation, and HTTP mapping.
    • Open/Closed Principle (OCP): New cross-cutting behaviors (auditing, metrics, tracing) and endpoints are added via decorators, interceptors, and IEndpoint implementations without altering existing service logic.
    • Liskov Substitution Principle (LSP): [Slice]CacheService transparently decorates and substitutes [Slice]Service across all consumers via I[Slice]Service.
    • Interface Segregation Principle (ISP): Lean, dedicated service interfaces per domain slice rather than bloated monolithic service contracts.
    • Dependency Inversion Principle (DIP): Endpoints and services depend exclusively on abstractions (IDbContextFactory, ITenantContextService, HashService, service interfaces) rather than concrete implementations.
  3. DRY Principle via AOP & Functional Utilities:

    • Zero Cross-Cutting Boilerplate: Developers never write repetitive logging or auditing code in service methods. Attributes [Log] and [Audit] handle these concerns declaratively via Castle DynamicProxy.
    • Unified Error & Problem Details: Domain error records (ValidationError, NotFoundError, UpdateError, CreateError, IdDecodeError) and extension methods (ToProblems(), ToEndpointName()) prevent repeated error payload formatting.
    • Shared HashID & Tenant Context: Centralized HashService and ITenantContextService guarantee single-point security and tenant isolation enforcement.

16.3 C# 13 & .NET 10 Coding Style: Functional Results, Formatting & Syntax Rules

Important

Authoritative Coding Style Rules (Never Regress or Undo This Approach): Antigravity / Gemini must strictly adhere to the established C# functional idioms, layout, and syntax conventions across all projects. When authoring, refactoring, or extending backend code:

  • DO NOT introduce imperative try-catch blocks for control flow or routine domain logic.
  • DO NOT introduce nested if-else return branches.
  • DO NOT use braces {} on single-line if statements — braces are reserved exclusively for blocks with more than one line of code.
  • Prefer FluentResults over ternary operators (Result.FailIf(...).ToResult(...) over condition ? Result.Fail(...) : Result.Ok(...)).
  • Prefer expression-bodied members (=>) and switch expressions over statement bodies and traditional switch statements.
  • Never split method parameters one-per-line in method or constructor definitions.
  • Group related variable declarations and function calls without empty blank lines between them.
  • Never leave unused using namespaces in .cs files.
  • Pragmatically avoid magic strings using constants, enums, or domain errors without obsessive over-engineering.

1. Extensive Avoidance of try-catch Blocks

  • Imperative try-catch blocks are strictly avoided across all application services, endpoints, and domain logic.
  • Methods wrap their entire execution functionally inside Result.Try:
    public async Task<Result<Project>> GetProjectAsync(string projectIdHash, CancellationToken cancellationToken = default) =>
        await Result.Try((Func<Task<Result<Project>>>)(async () =>
        {
            // Execution logic without any try-catch...
        }));
    
  • Unexpected runtime exceptions are automatically caught by Result.Try and converted into a Result.Fail with exception metadata, allowing LoggingInterceptor to record errors without leaking raw exceptions or crashing threads.

2. Minimal Branching, Guard Clauses & If-Statement Brace Discipline

  • No Braces for Single-Line if: Braces {} are strictly omitted when the if body consists of a single statement/line:
    if (!await context.Users.AnyAsync(u => u.Id == userId, cancellationToken))
        return Result.Fail(NotFoundError.New);
    
    if (decodeResult.IsFailed)
        return Result.Fail<Contract>(IdDecodeError.Create().CausedBy(decodeResult.Errors));
    
  • Braces Only for Multi-Line Blocks: Braces {} are used only when the if block encompasses more than one line of code:
    if (string.IsNullOrWhiteSpace(cached))
    {
        var result = await service.GetActivitiesAsync(projectIdHash, cancellationToken);
        if (result.IsFailed) return result;
    
        await cache.SetStringAsync(cacheKey, result.Value.ToJson(), cacheOptions, cancellationToken);
        return result.Value;
    }
    

3. FluentResults Monadic Chaining over Ternary Operators

  • Whenever processing or transforming a response, always choose FluentResults methods over ternary operators (? :):
    // Preferred (Fluent monadic style):
    return Result.FailIf(project is null, NotFoundError.Create("Project not found"))
        .ToResult(project?.Map()!);
    
    return Result.OkIf(changedTotal > 0, UpdateError.Create("No changes detected, update cancelled."));
    
    // Avoid (Ternary branching):
    // return project is null ? Result.Fail<Project>(...) : Result.Ok(project.Map());
    

4. Expression-Bodied Members & Switch Expressions

  • Methods, mappers, getters, and endpoint registrations strongly prefer expression bodies (=>):
    public static Activity Map(this Entities.Activity entity) => new()
    {
        Id = entity.Id,
        Title = entity.Title,
        // ...
    };
    
  • Minimal API endpoints map Result<T> directly to HTTP status codes via C# pattern-matching switch expressions:
    await service.UpdateProjectStatusAsync(request, context.GetUserId(), cancellationToken) switch
    {
        { IsSuccess: true } => Results.Ok(),
        var r when r.HasError<IdDecodeError>() => Results.Forbid(),
        var r when r.HasError<NotFoundError>() => Results.NotFound(r.Errors.ToProblems()),
        var r when r.HasError<ValidationError>() => Results.BadRequest(r.Errors.ToProblems()),
        var r when r.HasError<UpdateError>() => Results.BadRequest(r.Errors.ToProblems()),
        var r => Results.InternalServerError(r.Errors.ToProblems())
    }
    

5. Compact Method & Constructor Definitions

  • Method signatures are kept compact and readable on one line or cleanly wrapped; parameters are never split into one line per variable:
    // Preferred:
    public async Task<Result> UpdateProjectStatusAsync(UpdateProjectStatusRequest request, Guid userId, CancellationToken cancellationToken = default) =>
    
    public sealed class ProjectService(IDbContextFactory<ApplicationDbContext> contextFactory, HashService hashService,
        ITenantContextService tenantContext) : IProjectService
    
    // Avoid:
    // public async Task<Result> UpdateProjectStatusAsync(
    //     UpdateProjectStatusRequest request,
    //     Guid userId,
    //     CancellationToken cancellationToken = default)
    

6. Bracket Placement (Allman Style)

  • Opening braces { are placed on a new line for type declarations, multi-line blocks, lambda bodies in Result.Try, and object initializers:
    public sealed class ProjectService(...) : IProjectService
    {
        public async Task<Result> MethodAsync(...) =>
            await Result.Try((Func<Task<Result>>)(async () =>
            {
                // ...
            }));
    }
    
  • Related variable declarations are grouped together without blank lines between them.
  • Related sequential invocations are kept adjacent without extraneous empty lines:
    var name = request.Name.Humanize(LetterCasing.Title).Trim();
    var description = request.Description.Humanize(LetterCasing.Sentence).Trim();
    
    var cacheKey = HashService.ToMd5Hash($"project-{id}").Value;
    var cached = await cache.GetStringAsync(cacheKey, cancellationToken);
    

8. Clean Namespaces & Pragmatic String Handling

  • Zero Unused Namespaces: Every .cs file maintains clean, minimal using directives without stale or unused imports. File-scoped namespaces (namespace PostFundManagement...;) are standard.
  • Pragmatic Magic String Avoidance: Avoid magic strings for business-critical values (use constants in Constants.cs, EndpointTags, domain enums, or typed domain errors), but remain pragmatic (e.g. human-readable error messages and route patterns need not be over-abstracted into single-use constants).

9. Functional Programming Orientation

  • While C# is multi-paradigm, the codebase leans intentionally into functional programming concepts:
    • Immutability via positional records (public sealed record UpdateProjectStatusRequest(...)).
    • Pure extension mapping functions (entity.Map()).
    • Declarative LINQ queries and pipeline data transformations.
    • Monadic error handling (Result<T>, Result.Try, CausedBy).

10. High-Concurrency DbContext Management

  • Services inject IDbContextFactory<ApplicationDbContext> rather than scoped ApplicationDbContext.
  • Creates short-lived contexts using await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);.
  • Avoids thread concurrency issues in parallel mediator requests and background Quartz jobs.

11. Query & Mutation Optimizations

  • Queries use .AsNoTracking() to eliminate EF Core tracking overhead.
  • Targeted updates leverage .ExecuteUpdateAsync(...) for in-database bulk updates without loading full entities into memory.

12. Tiered Hybrid Caching

  • Implements Microsoft.Extensions.Caching.Hybrid alongside StackExchange Redis.
  • Local L1 memory cache: 30-minute expiration.
  • Distributed L2 Redis cache: 2-minute expiration (InstanceName = "PFM-API_").

16.4 Logging Strategy Using LoggingInterceptor & [Log]

The application uses Aspect-Oriented Programming (AOP) via Castle DynamicProxy and Castle.Core.AsyncInterceptor to decouple logging from core domain logic.

How LoggingInterceptor Works

  • Source: PostFundManagement.Application/Interceptors/LoggingInterceptor.cs.
  • Trigger: Decorated with [Log] attribute on service interface methods.
  • Execution Flow:
    1. Intercepts both synchronous (InterceptSynchronous) and asynchronous (InterceptAsynchronous<TResult>) calls.
    2. Awaits the target method execution to retrieve the resultObj.
    3. Checks if resultObj is IResultBase result && result.IsFailed.
    4. If failed:
      • Discovers target method information.
      • Sanitizes and serializes invocation arguments via BuildPayloadSummary(parameters, args):
        • Form files are summarized: [File] Name='...', Size=... bytes.
        • Streams are summarized: [Stream] Length=... bytes.
        • Cancellation tokens are excluded.
        • DTOs/Requests are serialized to JSON.
      • Extracts error reasons and error type:
        var errorType = result.Reasons?.FirstOrDefault()?.GetType().Name.Replace("Error", string.Empty);
        var errorsString = string.Join("; ", errorMessages);
        
      • Emits a structured log via ILogger:
        logger.LogError("Method {MethodName} failed. Errors: [{ErrorType}] {Errors}. Parameters: {@Parameters}",
            method.Name, errorType, errorsString, parameters);
        
    5. The original resultObj is returned to the caller without alteration.

16.5 Audit Strategy Using AuditInterceptor, [Audit] & AuditService

The audit trail is a strict regulatory requirement (BR-007, NFR-008, AT-12). Rather than writing manual audit code in every handler, the platform automates audit logging via AuditInterceptor.

How AuditInterceptor Works

  • Source: PostFundManagement.Application/Interceptors/AuditInterceptor.cs.
  • Trigger: Decorated with [Audit] attribute on service interface methods.
  • Execution Flow:
    1. Intercepts asynchronous execution: InterceptAsynchronous<TResult>(IInvocation invocation).
    2. Inspects resultObj: Auditing executes only when result.IsSuccess is true.
    3. Automatically resolves audit attributes from runtime reflection:
      • Action: Strips Async suffix from method name (CreateProjectAsync \to CreateProject).
      • EntityName: Inferred from return value generic type (Result<Project> \to Project) or request argument name (CreateProjectRequest \to Project).
      • EntityId: Inspects the returned object's Value.Id property or checks invocation arguments for an Id/id property or primitive key.
      • Changes: Serializes parameters into a formatted payload summary (BuildPayloadSummary).
      • PerformedBy: Extracts the authenticated user's Guid from IHttpContextAccessor.HttpContext.User claims (sub or NameIdentifier).
    4. Asynchronously invokes AuditService.LogActionAsync(...):
      await auditService.LogActionAsync(
          entityId: entityId,
          entityName: entityName,
          action: action,
          changes: changes,
          performedBy: performedBy).ConfigureAwait(false);
      
    5. Persists an immutable entry to context.AuditLogs with UTC timestamp and ambient TenantId.
    6. Resilience: The entire audit block is wrapped in a non-throwing catch block so audit database write issues never fail a successful business transaction.

16.6 AOP Service Registration Pipeline

All application services are registered through a unified decorator-and-proxy composition in PostFundManagement.Application/Extensions/ServiceExtensionTools.cs and Services.cs:

services.AddLoggedAuditedCachedServiceScoped<IProjectService, ProjectService, ProjectCacheService>();
services.AddLoggedAuditedCachedServiceScoped<IGrantsService, GrantsService, GrantsCacheService>();
services.AddLoggedAuditedCachedServiceScoped<IExecutionService, ExecutionService, ExecutionCacheService>();
services.AddLoggedAuditedCachedServiceScoped<IEvidenceService, EvidenceService, EvidenceCacheService>();
services.AddLoggedAuditedCachedServiceScoped<IGovernanceService, GovernanceService, GovernanceCacheService>();
services.AddLoggedAuditedCachedServiceScoped<ICommunicationsService, CommunicationsService, CommunicationsCacheService>();
services.AddLoggedAuditedCachedServiceScoped<IUserService, UserService, UserCacheService>();

Execution Pipeline Order

Caller (e.g. Minimal API Endpoint)
   │
   ▼
1. Castle DynamicProxy Interface Proxy
   │
   ├─► [LoggingInterceptor]: Intercepts call; logs structured error if Result.IsFailed
   │
   ├─► [AuditInterceptor]: Intercepts call; records immutable AuditLog if Result.IsSuccess
   │
   ▼
2. [Slice]CacheService (Cache Decorator)
   │ Checks HybridCache (L1 Memory / L2 Redis); returns cached response on read hits
   │
   ▼ (Cache Miss / Mutation)
3. [Slice]Service (Actual Implementation)
   │ Executes business invariants, EF Core queries, and state persistence
   │
   ▼
Database (PostgreSQL / Npgsql)

This composition ensures:

  • Zero Boilerplate: Domain services do not write logging or auditing code.
  • Separation of Concerns: Caching, logging, auditing, and business rules remain in isolated, testable classes.
  • Guaranteed Auditability: Any method tagged with [Audit] automatically satisfies BR-007 and AT-12.