Updated documentation
This commit is contained in:
+87
-7
@@ -12,7 +12,7 @@ The PFM platform governs the lifecycle of grant, development, innovation, and im
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ POST-FUND LIFECYCLE │
|
||||
│ │
|
||||
│ [1. Award Intake] ──> [2. Contracting] ──> [3. Onboarding] │
|
||||
│ [1. Award Intake] ──> [2. Contracting] ──> [3. Onboarding] │
|
||||
│ External Approval Draft Agreement KYC & Banking Gate │
|
||||
│ Recorded (BR-001) Auto-created (BR-002) Required for Disb. (BR-003) │
|
||||
│ │ │
|
||||
@@ -88,6 +88,16 @@ The PFM platform governs the lifecycle of grant, development, innovation, and im
|
||||
4. Resolution: `Approved & Enacted` or `Rejected`.
|
||||
5. **Maker-Checker Rule**: Change request initiator cannot approve or enact the change.
|
||||
|
||||
### 2.7 Process 7: Automated Background Workflows & Scheduled Jobs (PostFundManagement.Jobs)
|
||||
* **Pre-condition**: Background jobs host (`PostFundManagement.Jobs`) active and connected to `pfm-scheduler`.
|
||||
* **Actors**: System Engine, `JobsWorker` Daemon, Domain Event Handlers.
|
||||
* **Workflow**:
|
||||
1. **Event Triggering**: API operations emit strongly typed events (`IEvent`) and enqueue them into `pfm-scheduler` via `IJobOrchestrator`.
|
||||
2. **Cron Schedule Execution**: `JobsWorker` initializes recurring schedules on daemon startup for periodic monitoring, risk assessments, and communication batches.
|
||||
3. **Job Execution**: Quartz cluster dispatches acquired triggers to `MediatorJob<TNotification>` within `PostFundManagement.Jobs` (concurrency limit = 5).
|
||||
4. **Mediated Processing**: Event payload is deserialized and published via `IMediator` to execute domain logic (project monitoring, risk threshold checks, email dispatch).
|
||||
5. **Cascading One-Time Jobs**: When a recurring job identifies an action item requiring separate asynchronous execution, it triggers a decoupled one-time job (`jobOrchestrator.SendAsync`).
|
||||
|
||||
---
|
||||
|
||||
## 3. The Maker-Checker Operational Policy
|
||||
@@ -139,20 +149,20 @@ To ensure that caching never impedes record lifecycle updates or displays stale
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor User as User / Maker
|
||||
participant UI as ConsoleUI (React Query)
|
||||
participant UI as ConsoleUI React Query
|
||||
participant Client as Axios HttpClient
|
||||
participant OutputCache as Backend OutputCache (Redis/Memory)
|
||||
participant OutputCache as Backend OutputCache Redis/Memory
|
||||
participant DB as PostgreSQL Database
|
||||
|
||||
User->>UI: Submits Record Mutation (Create / Update / Approve)
|
||||
User->>UI: Submits Record Mutation Create/Update/Approve
|
||||
UI->>Client: Dispatches POST / PUT / PATCH / DELETE
|
||||
Client->>DB: Executes Mutation in Database
|
||||
DB-->>Client: Returns 200 OK / 201 Created
|
||||
Client->>Client: Interceptor detects mutation: bumps activeMutationEpoch
|
||||
UI->>UI: onSuccess triggers queryClient.invalidateQueries()
|
||||
UI->>UI: onSuccess triggers invalidateQueries
|
||||
UI->>Client: Refetches active queries with _v = activeMutationEpoch
|
||||
Client->>OutputCache: GET /v1/api/*?_v=<newEpoch>
|
||||
Note over OutputCache: Cache Miss (new _v key never seen before)
|
||||
Client->>OutputCache: GET /v1/api/* with new _v epoch
|
||||
Note over OutputCache: Cache Miss: new _v key never seen before
|
||||
OutputCache->>DB: Queries PostgreSQL for fresh data
|
||||
DB-->>OutputCache: Returns updated entities
|
||||
OutputCache-->>UI: Caches fresh data under _v and returns to UI
|
||||
@@ -164,4 +174,74 @@ sequenceDiagram
|
||||
2. **Deterministic Invalidation**: Calling standard `queryClient.invalidateQueries({ queryKey: [...] })` or `invalidateAndBustCache(queryClient, filters)` guarantees that the subsequent refetch bypasses stale ASP.NET Core OutputCache entries.
|
||||
3. **Optimized Read Cache**: When users navigate across unchanged views or switch tabs, identical `_v` tokens are reused, allowing backend OutputCache and React Query in-memory caches to fulfill requests in < 5ms without unnecessary database hits.
|
||||
|
||||
---
|
||||
|
||||
## 6. Background Job Lifecycle & Event Execution Workflows
|
||||
|
||||
The dedicated worker host (`PostFundManagement.Jobs`) operates as a decoupled daemon executing recurring maintenance routines and asynchronous domain events dispatched from the API or internally from other jobs.
|
||||
|
||||
### 6.1 Event Ingestion & Job Persistence Flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph Producer ["PostFundManagement.Api (Event Source)"]
|
||||
A["User Action / Lifecycle Transition"] --> B["Domain Command / Mutation"]
|
||||
B --> C["Instantiate IEvent (e.g. SyncUserEvent, ProjectReminderEvent)"]
|
||||
C --> D["IJobOrchestrator.SendAsync() or ScheduleAsync()"]
|
||||
end
|
||||
|
||||
subgraph Store ["PostgreSQL Clustered Store (pfm-scheduler)"]
|
||||
D --> E[("QRTZ_JOB_DETAILS and QRTZ_TRIGGERS")]
|
||||
end
|
||||
|
||||
subgraph Consumer ["PostFundManagement.Jobs (Execution Host)"]
|
||||
E -->|"Acquire Trigger Lock"| F["Quartz Scheduler Engine (MaxConcurrency = 5)"]
|
||||
F --> G["MediatorJob[TNotification]"]
|
||||
G --> H["Deserialize JSON Payload from JobDataMap"]
|
||||
H --> I["IMediator.Publish(notification)"]
|
||||
I --> J["Domain Event Handler: Business Logic in pfm DB"]
|
||||
J -.->|"Cascading Sub-Task Needed"| K["IJobOrchestrator.SendAsync: Dispatch One-Time Job"]
|
||||
K -.-> E
|
||||
end
|
||||
```
|
||||
|
||||
### 6.2 Standard Background Processes
|
||||
|
||||
#### 1. Automated Project Monitoring & Reminders Process
|
||||
* **Trigger**: Scheduled cron job registered on `JobsWorker` startup.
|
||||
* **Operational Cycle**:
|
||||
1. Job queries active projects and non-completed milestones across tenant partitions.
|
||||
2. Compares milestone contractual due dates against current UTC time:
|
||||
- **T - 14 / T - 7 / T - 1 Days**: Emits reminder notification events to Grantee/Partner project managers.
|
||||
- **T + 0 Days (Overdue)**: Automatically flags the milestone as `Overdue` (`FR-M07-009`) and updates project RAG status to `At Risk` (`FR-M04-017`).
|
||||
3. Escalates overdue notifications to internal Programme Managers for intervention.
|
||||
|
||||
#### 2. Risk Monitoring & Automated Escalation Process
|
||||
* **Trigger**: Periodic recurring schedule.
|
||||
* **Operational Cycle**:
|
||||
1. Queries all active project risk registers and Key Risk Indicators (KRIs) (`FR-M09-001` - `011`).
|
||||
2. Evaluates current severity score ($\text{Risk Score} = \text{Likelihood} \times \text{Impact}$).
|
||||
3. If risk score breaches the configured programme tolerance threshold:
|
||||
- Automatically logs a risk escalation event.
|
||||
- Dispatches alerts to Risk Managers and Portfolio Executives.
|
||||
4. Sweeps assigned risk mitigation actions; identifies overdue mitigation plans and emits reminders to responsible assignees.
|
||||
|
||||
#### 3. Email Notification Processing Workflow (`Platform.Email`)
|
||||
* **Trigger**: High-frequency recurring polling job or deferred one-time dispatch.
|
||||
* **Operational Cycle**:
|
||||
1. Queries pending notifications from the transactional database where `Platform == NotificationPlatform.Email` and `Status == NotificationStatus.Pending` or `Queued`.
|
||||
2. Resolves tenant context using `HeadlessTenantContextService`.
|
||||
3. Formats email templates with recipient credentials and branding.
|
||||
4. Transmits messages via MailKit/MimeKit SMTP engine with rate-limiting and connection pooling.
|
||||
5. On successful delivery: Updates record status to `Sent`, stamps `DeliveredAt` timestamp, and logs dispatch telemetry (`FR-M25-001`).
|
||||
6. On failure: Increments retry counter; if retry limit exceeded, marks as `Failed` with diagnostic error trace.
|
||||
|
||||
### 6.3 Recurring-to-One-Time Job Cascading Pattern
|
||||
|
||||
When a recurring job evaluates a broad batch of records (e.g., scanning thousands of project indicators or verifying compliance expirations), it must not execute long-running operations synchronously in the recurring job thread:
|
||||
1. The recurring job identifies items requiring heavy processing (e.g. generating an executive PDF digest or calling external webhooks).
|
||||
2. For each identified item, it enqueues an isolated one-time job via `IJobOrchestrator.SendAsync(specificEvent)`.
|
||||
3. This releases the recurring job quickly, distributes execution across the 5 available worker threads in `PostFundManagement.Jobs`, and guarantees that a single failed sub-task can be retried independently without stalling the entire recurring cycle.
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user