Skip to content

Funding Programs Domain (funding_programs_v1)

The funding_programs_v1 domain manages grant and funding programs, including application workflows, funding program definitions (such as Perseus), benefit claims, and verification processes. It handles the complete lifecycle of funding applications and program-based benefit claims with energy efficiency and sustainability focus.

This bounded context manages two primary workflows:

  1. Traditional Funding Applications: Direct grant applications with energy calculations and retrofit verification
  2. Perseus Program Claims: Scheme-based benefit claims with evidence submission and verification

The domain enforces business rules around:

  • Application lifecycle and status transitions
  • Program scheme definitions and benefits
  • Claim submission, verification, and benefit awards
  • Evidence tracking and compliance
  • Energy baseline measurements and retrofit verification
dart_packages/co2/domains/funding_programs_v1/
├── lib/
│ ├── funding_programs_v1.dart # Main exports and registration
│ └── src/
│ ├── aggregates/ # Domain aggregates
│ │ ├── funding_application_aggregate.dart
│ │ ├── funding_program_definition_aggregate.dart
│ │ ├── funding_program_claim_aggregate.dart
│ │ ├── metered_energy_baseline_aggregate.dart
│ │ ├── regional_cost_benchmark_aggregate.dart
│ │ └── retrofit_verification_aggregate.dart
│ ├── events/ # Domain events
│ │ ├── funding_program_events.dart
│ │ ├── program_definition_events.dart
│ │ └── claim_events.dart
│ ├── directives/ # Command payloads
│ │ ├── funding_program_directives.dart
│ │ ├── program_definition_directives.dart
│ │ └── claim_directives.dart
│ └── services/ # Domain services
│ ├── perseus_eligibility_policy.dart
│ ├── perseus_benefit_calculator.dart
│ ├── perseus_evidence_validator.dart
│ └── retrofit_compliance_service.dart
├── docs/
│ └── PERSEUS.md # Perseus program documentation
├── pubspec.yaml
└── test/

A funding application progresses through distinct states:

  • Submitted: Initial state when application is created
  • Under Review: Application being evaluated
  • Approved: Application has been approved with an approved amount
  • Rejected: Application has been rejected with a reason
  • Withdrawn: Application has been withdrawn by applicant

Applications include energy calculations to assess eligibility and justify funding amounts.

Perseus is a government-backed scheme for sustainability-linked finance and grant funding. It includes:

  • Program Definitions: Scheme rules, benefits, eligibility criteria, and evidence requirements
  • Benefit Claims: Organizations apply for specific benefits under a program
  • Evidence Submission: Claims require evidence (certificates, reports, calculations)
  • Verification: Third-party verification of claims and energy reductions
  • Monitoring: Post-award monitoring of benefit compliance

Manages the lifecycle of traditional funding applications with energy calculations.

class FundingApplicationAggregate extends Aggregate<FundingApplicationAggregate> {
final FundingApplicationId applicationId;
final FundingProgramId programId;
final UserId applicantId;
final ApplicationTitle projectTitle;
final double requestedAmount;
final FundingApplicationStatus status;
// Energy calculations
Map<EnergyCalculationId, EnergyCalculation> energyCalculations;
// Approval details
double? approvedAmount;
DomainText? rejectionReason;
}

Key Methods:

  • canBeApproved(): Checks if application can transition to approved
  • canBeRejected(): Checks if application can be rejected
  • allCalculationsMeetCriteria: Validates that all energy calculations meet funding criteria

Defines the rules, benefits, and requirements of a funding program like Perseus.

class FundingProgramDefinitionAggregate
extends Aggregate<FundingProgramDefinitionAggregate> {
final FundingProgramId programId;
final FundingProgramCode programCode;
final ProgramVersion version;
final List<BenefitDefinition> benefitCatalog;
final EligibilityPolicy eligibilityPolicy;
final List<EvidenceRequirement> evidenceRequirements;
final AssurancePolicy assurancePolicy;
final bool isActive;
}

Key Responsibilities:

  • Maintain program scheme definition and benefits
  • Track program versions and updates
  • Define eligibility rules and evidence requirements
  • Support program activation/deactivation

Represents an organization’s claim for a benefit under a funding program.

class FundingProgramClaimAggregate
extends Aggregate<FundingProgramClaimAggregate> {
final FundingProgramClaimId claimId;
final FundingProgramId programId;
final OrganizationId claimantId;
final BenefitType selectedBenefitType;
final ClaimStatus status;
final List<ClaimEvidence> evidenceRefs;
final VerificationOutcome? verificationOutcome;
final AwardTerms? awardTerms;
}

Claim Lifecycle:

  1. Draft: Claim being prepared
  2. Submitted: Claim submitted to program
  3. Evidence Pending: Awaiting evidence submission/verification
  4. Verified: Evidence has been verified
  5. Awarded: Benefit has been awarded
  6. Active Monitoring: Post-award monitoring phase
  7. Completed: Monitoring period completed
  8. Revoked: Claim revoked due to non-compliance

Tracks electricity consumption baselines from smart metering.

class MeteredEnergyBaselineAggregate
extends Aggregate<MeteredEnergyBaselineAggregate> {
final MeteredEnergyBaselineId baselineId;
final SiteId siteId;
final List<ElectricityReading> electricityReadings;
final List<String> validationErrors;
}

Verifies that retrofitting work has been completed to specification.

class RetrofitVerificationAggregate
extends Aggregate<RetrofitVerificationAggregate> {
final RetrofitVerificationId verificationId;
final FundingApplicationId applicationId;
final bool installedAsSpecified;
final String status; // 'pending' | 'completed' | 'failed'
final List<AttachmentId> evidenceAttachmentIds;
}

Defines cost benchmarks for retrofit measures by region and measure type.

class RegionalCostBenchmarkAggregate
extends Aggregate<RegionalCostBenchmarkAggregate> {
final RegionalCostBenchmarkId benchmarkId;
final String region;
final String measureType;
final double maxCostPerUnit;
}

Fired when an organization submits a funding application.

class FundingApplicationSubmittedEvent implements Event {
final FundingApplicationId applicationId;
final FundingProgramId programId;
final UserId applicantId;
final ApplicationTitle projectTitle;
final double requestedAmount;
final FundingProjectData fundingProjectData;
final List<AttachmentId> attachmentIds;
}

Fired when application status changes (approved, rejected, withdrawn).

class FundingApplicationStatusChangedEvent implements Event {
final FundingApplicationId applicationId;
final FundingApplicationStatus newStatus;
final double? approvedAmount;
final DomainText? rejectionReason;
}

Fired when energy efficiency calculations are completed for an application.

class EnergyCalculationCompletedEvent implements Event {
final EnergyCalculationId calculationId;
final EnergyCalculationType calculationType;
final double totalEnergyLoss;
final double totalArea;
final double averageEfficiency;
final bool meetsCriteria;
final EnergyCalculationData energyCalculationData;
}

Fired when a new funding program scheme is created.

class FundingProgramDefinedEvent implements Event {
final FundingProgramId programId;
final FundingProgramCode programCode;
final ProgramVersion version;
final List<BenefitDefinition> benefitCatalog;
final EligibilityPolicy eligibilityPolicy;
final List<EvidenceRequirement> evidenceRequirements;
final AssurancePolicy assurancePolicy;
}

Fired when a new benefit is added to an active program.

class BenefitAddedToProgramEvent implements Event {
final FundingProgramId programId;
final BenefitDefinition benefit;
}

Fired when a program is deactivated.

class ProgramDeactivatedEvent implements Event {
final FundingProgramId programId;
}

Fired when an organization submits a claim for a program benefit.

class ClaimSubmittedEvent implements Event {
final FundingProgramClaimId claimId;
final FundingProgramId programId;
final OrganizationId claimantId;
final BenefitType selectedBenefitType;
final ReductionTarget? declaredTarget;
final MonitoringPeriod? monitoringPeriod;
}

Fired when evidence is attached to support a claim.

class EvidenceAttachedToClaimEvent implements Event {
final FundingProgramClaimId claimId;
final ClaimEvidence evidence;
}

Fired when verification is requested for a claim.

class ClaimVerificationRequestedEvent implements Event {
final FundingProgramClaimId claimId;
final UserId verifierId;
}

Fired when claim verification is completed.

class ClaimVerificationCompletedEvent implements Event {
final FundingProgramClaimId claimId;
final VerificationOutcome outcome;
}

Fired when a benefit is awarded following successful verification.

class BenefitAwardedEvent implements Event {
final FundingProgramClaimId claimId;
final AwardTerms awardTerms;
}

Fired when post-award monitoring begins.

class ClaimMonitoringStartedEvent implements Event {
final FundingProgramClaimId claimId;
final MonitoringPeriod period;
}

Fired when the monitoring period ends successfully.

class MonitoringPeriodCompletedEvent implements Event {
final FundingProgramClaimId claimId;
}

Fired when a claim is revoked due to non-compliance.

class ClaimRevokedEvent implements Event {
final FundingProgramClaimId claimId;
final String reason;
}

Directives represent commands that trigger state changes in the domain.

Submit a new funding application.

class SubmitFundingApplicationPayload extends Payload {
final FundingApplicationId applicationId;
final FundingProgramId programId;
final UserId applicantId;
final ApplicationTitle projectTitle;
final ApplicationDescription projectDescription;
final double requestedAmount;
final CurrencyCode currency;
final Map<String, dynamic> projectData;
final List<AttachmentId> attachmentIds;
}

Change the status of an application (approve, reject, withdraw).

class UpdateFundingApplicationStatusPayload extends Payload {
final FundingApplicationId applicationId;
final FundingApplicationStatus newStatus;
final double? approvedAmount;
final DomainText? rejectionReason;
}

Submit completed energy efficiency calculations for an application.

class CompleteEnergyCalculationPayload extends Payload {
final EnergyCalculationId calculationId;
final FundingApplicationId applicationId;
final EnergyCalculationType calculationType;
final double totalEnergyLoss;
final double totalArea;
final double averageEfficiency;
final bool meetsCriteria;
}

Create a new funding program scheme (like Perseus).

class DefineFundingProgramPayload extends Payload {
final FundingProgramId programId;
final FundingProgramCode programCode;
final ProgramVersion version;
final List<BenefitDefinition> benefitCatalog;
final EligibilityPolicy eligibilityPolicy;
final List<EvidenceRequirement> evidenceRequirements;
final AssurancePolicy assurancePolicy;
}

Submit a claim for a program benefit.

class SubmitFundingProgramClaimPayload extends Payload {
final FundingProgramClaimId claimId;
final FundingProgramId programId;
final OrganizationId claimantId;
final BenefitType selectedBenefitType;
final ReductionTarget? declaredTarget;
}

Attach evidence supporting the claim.

class AttachEvidenceToClaimPayload extends Payload {
final FundingProgramClaimId claimId;
final ClaimEvidence evidence;
}

Request verification of a claim.

class RequestClaimVerificationPayload extends Payload {
final FundingProgramClaimId claimId;
final UserId verifierId;
}

Complete the verification of a claim.

class CompleteClaimVerificationPayload extends Payload {
final FundingProgramClaimId claimId;
final VerificationOutcome outcome;
}

Award the benefit after successful verification.

class AwardBenefitPayload extends Payload {
final FundingProgramClaimId claimId;
final AwardTerms awardTerms;
}
1. Submit Application
└─> FundingApplicationSubmittedEvent
└─> Application in "Submitted" state
2. Energy Calculations
└─> CompleteEnergyCalculationDirective
└─> EnergyCalculationCompletedEvent
└─> Calculations stored in aggregate
3. Review & Decision
└─> UpdateFundingApplicationStatusDirective
└─> FundingApplicationStatusChangedEvent
└─> Application moves to "Approved" or "Rejected"
4. Retrofit Verification (if approved)
└─> Verify work completed to specification
└─> RetrofitVerificationCompletedEvent
1. Create Program Definition
└─> DefineFundingProgramDirective
└─> FundingProgramDefinedEvent
└─> Program with benefits active
2. Submit Claim
└─> SubmitFundingProgramClaimDirective
└─> ClaimSubmittedEvent
└─> Claim in "Submitted" state
3. Attach Evidence
└─> AttachEvidenceToClaimDirective
└─> EvidenceAttachedToClaimEvent (multiple times)
└─> Claim in "Evidence Pending" state
4. Request Verification
└─> RequestClaimVerificationDirective
└─> ClaimVerificationRequestedEvent
└─> Third party verifies evidence
5. Complete Verification
└─> CompleteClaimVerificationDirective
└─> ClaimVerificationCompletedEvent
└─> Claim moves to "Verified"
6. Award Benefit
└─> AwardBenefitDirective
└─> BenefitAwardedEvent
└─> Claim in "Awarded" state
7. Post-Award Monitoring
└─> ClaimMonitoringStartedEvent
└─> Track compliance during monitoring period
└─> MonitoringPeriodCompletedEvent
└─> Claim "Completed"

The domain uses strongly-typed identifiers from the contracts package:

TypePurpose
FundingApplicationIdUnique application identifier
FundingProgramIdFunding program identifier
FundingProgramCodeProgram code (e.g., “PERSEUS-2024”)
FundingProgramClaimIdUnique claim identifier
ApplicationTitleApplication project title
ApplicationDescriptionDetailed project description
ProgramNameHuman-readable program name
ProgramVersionProgram version (e.g., “1.0.0”)
BenefitTypeType of benefit (e.g., sustainabilityLinkedLoanRateReduction)
ClaimStatusStatus of claim (draft, submitted, awarded, etc.)
FundingApplicationStatusStatus of application (submitted, approved, rejected, etc.)
EnergyCalculationIdEnergy calculation identifier
EnergyCalculationTypeType of calculation (general, ashp, boiler, etc.)
CurrencyCodeCurrency (GBP, EUR, USD)
JurisdictionCodeJurisdiction (UK, EU, etc.)

Evaluates claim eligibility against program rules.

abstract class PerseusEligibilityPolicy {
/// Check if organization meets program eligibility criteria
bool isOrganizationEligible(
OrganizationId organizationId,
FundingProgramDefinitionAggregate program,
);
/// Check if claimed benefit is available in program
bool isBenefitAvailable(
BenefitType benefit,
FundingProgramDefinitionAggregate program,
);
}

Calculates benefit amounts based on program rules and claim details.

abstract class PerseusBenefitCalculator {
/// Calculate monetary benefit for a claim
Money calculateMonetaryBenefit(
FundingProgramClaimAggregate claim,
FundingProgramDefinitionAggregate program,
);
/// Calculate loan rate reduction benefit
double calculateLoanRateReduction(
double loanAmount,
int loanTermMonths,
ReductionTarget target,
);
}

Validates that submitted evidence meets program requirements.

abstract class PerseusEvidenceValidator {
/// Validate evidence against program requirements
EvidenceValidationResult validateEvidence(
List<ClaimEvidence> evidence,
List<EvidenceRequirement> requirements,
);
/// Check completeness of evidence package
bool isEvidenceComplete(
List<ClaimEvidence> evidence,
List<EvidenceRequirement> requirements,
);
}

Validates retrofit work compliance.

abstract class RetrofitComplianceService {
/// Validate that retrofit meets building regulations
ComplianceCheckResult validateCompliance(
RetrofitVerificationAggregate verification,
FundingProjectData projectData,
);
}

All types must be registered for serialization/deserialization:

// In main application setup:
registerFundingProgramsV1();
// Or use the module interface:
final module = FundingProgramsV1();
module.registerDomainTypes();

The registerFundingProgramsV1() function registers:

  • All aggregates (creation and deserialization factories)
  • All events and their factories
  • All payloads (directives)
  • All custom types with the TypeRegistry
import 'package:funding_programs_v1/funding_programs_v1.dart';
import 'package:contracts_v1/contracts_v1.dart';
// Create and submit application
final directive = SubmitFundingApplicationPayload(
applicationId: const FundingApplicationId('app-001'),
programId: const FundingProgramId('prog-001'),
applicantId: const UserId('user-123'),
organizationId: const OrganizationId('org-456'),
projectTitle: ApplicationTitle.fromString('Solar Panel Installation'),
projectDescription: ApplicationDescription.fromString(
'Installing 10kW solar panels on warehouse roof',
),
requestedAmount: 50000.0,
currency: const CurrencyCode('GBP'),
submittedBy: const UserId('user-123'),
projectData: {
'buildingType': 'warehouse',
'location': 'Manchester',
'estimatedSavings': 30000.0,
},
);
// This would be processed by the application service
// which creates an event and stores the aggregate
// Complete energy efficiency calculation
final calcDirective = CompleteEnergyCalculationPayload(
calculationId: const EnergyCalculationId('calc-001'),
applicationId: const FundingApplicationId('app-001'),
calculationType: EnergyCalculationType.ashp,
totalEnergyLoss: 45000.0,
totalArea: 1200.0,
averageEfficiency: 0.85,
meetsCriteria: true,
);
// Event is created and aggregate is updated
// Submit claim for funding program benefit
final claimDirective = SubmitFundingProgramClaimPayload(
claimId: const FundingProgramClaimId('claim-001'),
programId: const FundingProgramId('PERSEUS-2024'),
claimantId: const OrganizationId('org-456'),
selectedBenefitType: BenefitType.sustainabilityLinkedLoanRateReduction,
declaredTarget: ReductionTarget(
targetPercentage: 20.0,
baselineYear: 2024,
targetYear: 2027,
),
);
// Claim is created and processing begins
// Attach evidence to support claim
final evidenceDirective = AttachEvidenceToClaimPayload(
claimId: const FundingProgramClaimId('claim-001'),
evidence: ClaimEvidence(
evidenceType: 'EnergyAudit',
attachmentId: const AttachmentId('attach-001'),
uploadedAt: DateTime.now(),
uploadedBy: const UserId('user-123'),
description: 'Professional energy audit report',
),
);
// Evidence is attached to claim
  • Applications may reference trackable assets (buildings, sites)
  • Energy calculations use asset baseline data
  • Retrofit verification confirms changes to asset configuration
  • Applications and claims attach supporting documents
  • Evidence submission uses attachment references
  • Verification requires attachment access
  • All identifiers and value objects come from contracts
  • Cross-domain communication uses payload types
  • Event publishing uses contract event interfaces
  1. Application Approval Rule: An application can only be approved if:

    • It is in “Under Review” status
    • All energy calculations meet criteria
    • Approved amount does not exceed requested amount
  2. Rejection Rule: Rejected applications must have a rejection reason

  3. Claim Verification Rule: Claims can only be verified if:

    • All required evidence has been submitted
    • Evidence passes validation
    • Organization meets eligibility criteria
  4. Benefit Award Rule: Benefits can only be awarded after:

    • Claim is verified
    • Verification outcome is positive
    • Monitoring period (if required) has started
  5. Retrofit Completion Rule: Completed retrofit verification requires:

    • Evidence attachments
    • Specification compliance confirmation
  6. Calculation Validity Rule: Energy calculations must meet:

    • Non-empty areas
    • Valid efficiency values (0-1 or percentage range)
    • Consistent data across all calculations

The domain throws StateError for validation failures:

// Application validation errors
StateError('Application ID cannot be unresolved')
StateError('Project title cannot be empty')
StateError('Requested amount must be positive')
StateError('Approved applications must have an approved amount')
StateError('Rejected applications must have a rejection reason')
StateError('Approved amount cannot exceed requested amount')
// Claim validation errors
StateError('Claim ID cannot be unresolved')
StateError('Program ID cannot be unresolved')
StateError('Claimant ID cannot be unresolved')

The domain includes comprehensive tests for aggregates:

test/aggregates/funding_aggregates_test.dart
// Run tests
dart test
// Tests cover creation, validation, event application

For detailed information about the Perseus program scheme, including:

  • Program objectives and scope
  • Financial product opportunities
  • User roles and responsibilities
  • Ecosystem map
  • Future innovation cases

See: /docs/PERSEUS.md

The domain has been updated to use typed FundingProjectData and EnergyCalculationData value objects instead of raw Map<String, dynamic>.

// Old (deprecated)
final projectData = event.projectData as Map<String, dynamic>;
// New (recommended)
final typedData = event.fundingProjectData;
final category = typedData.category;

Access the typedProjectData property on aggregates and events for type-safe access.

  • Applications can have multiple energy calculations; consider pagination in UI
  • Claims can have many evidence attachments; use lazy loading
  • Program definitions with large benefit catalogs should be cached
  • Monitoring periods can span months; use background jobs for completion checks