Skip to main content

00 - Insurance Domain for Developers: Products, Actors & Data Models

Insurance Domain for Developers: Products, Actors and Data Models. If you are a software developer entering the insurance industry for the first time, the complexity can be overwhelming. This guide maps the domain: underwriting, claims, policy administration, and the data models that power billion-dollar systems...

What do you think about this article?

At a Glance

Insurance Domain for Developers: Products, Actors and Data Models. If you are a software developer entering the insurance industry for the first time, the complexity can be overwhelming. This guide maps the domain: underwriting, claims, policy administration, and the data models that power billion-dollar systems...

The Insurance Domain for Developers: Products, Actors, and Data Models

If you are a software developer approaching the insurance world for the first time, you will likely face an impenetrable jargon: underwriting, endorsement, FNOL, subrogation, bordereaux. Don't worry: this guide translates the entire insurance domain into concepts, data models, and architectural patterns that a developer can understand, implement, and most importantly model correctly in code.

The insurance industry globally moves over 7 trillion dollars in annual premiums (2025 data, Swiss Re Sigma) and is one of the most heavily regulated sectors in the world. Yet, much of the IT infrastructure supporting it dates back to the 1980s and 1990s: COBOL mainframes, nightly batch jobs, green-screen interfaces. The digital transformation of the sector, known as InsurTech, is redesigning all of this, and developers are at the center of the change.

This article kicks off the InsurTech Engineering series with a comprehensive overview of the domain: insurance products, value chain actors, fundamental data models, policy and claim lifecycles, and modern architectural patterns for building cloud-native insurance platforms.

What You Will Learn in This Article

  • Insurance product categories (Life, P&C, Health, Specialty) and their technical differences
  • Value chain actors: policyholder, insurer, broker, agent, reinsurer, regulator
  • The insurance value chain: distribution, underwriting, policy admin, claims, investments
  • Core data models: Policy, Claim, Premium, Coverage, Endorsement, Rider
  • The complete policy lifecycle: quote, issue, endorse, renew, cancel
  • The claims lifecycle: FNOL, assessment, settlement, recovery
  • Domain-Driven Design applied to insurance: bounded contexts and aggregates
  • Rating engine architecture and premium calculation
  • ACORD standards and API patterns for interoperability
  • The regulatory landscape: Solvency II, IFRS 17, GDPR

The Insurance Industry: An Overview for Developers

Before writing a single line of code, it is essential to understand how the insurance business works. Unlike an e-commerce where the product is tangible, in insurance the "product" is a promise: the payment of a future indemnity upon the occurrence of an uncertain event. This makes the domain inherently complex from a data modeling perspective.

"Insurance is the only product you buy hoping to never use it. This paradox defines the entire architecture of the systems that manage it."

The Fundamental Principle: Risk Pooling

The core concept is risk pooling: a large group of individuals or businesses contributes regular premiums to a common fund, from which resources are drawn to compensate the few who suffer a loss. The insurer's role is to manage this fund in an actuarially sustainable way, ensuring that collected premiums are sufficient to cover expected claims plus operating costs and a profit margin.

Insurance Product Categories

Insurance products are divided into macro-categories with profoundly different technical characteristics. Understanding these differences is crucial for correctly modeling data.

Category Examples Typical Duration Risk Nature Data Complexity
Life Term, Whole Life, Unit-Linked, Pension 10-40 years Mortality, longevity High (actuarial tables, mathematical reserves)
P&C (Property & Casualty) Auto, Home, Professional Liability, Fire 1 year (renewable) Property, liability Medium (claim frequency, average cost)
Health Medical Expense, Accident, Long-Term Care 1 year / multi-year Morbidity, disability High (ICD codes, DRG, provider networks)
Specialty Marine, Aviation, Cyber, D&O, E&O Variable Complex / catastrophe risks Very high (CAT models, accumulations)

Implication for the Developer

There is no single "universal" data model for insurance. A Life system manages mathematical reserves and mortality tables; a P&C system manages deductibles, limits, and loss adjustments; a Health system manages medical codes and provider networks. The architectural choice between a unified model and specialized models per LOB (Line of Business) is one of the most critical decisions in designing an insurance platform.

Value Chain Actors

The insurance ecosystem involves numerous actors with distinct roles. Each of them becomes an entity in our data model and often a separate bounded context in the architecture.

Key Actor Map

Actor Domain Role Key Data Entities Primary Interactions
Policyholder Purchases the policy, pays premiums Customer, Account, PaymentMethod Quote, Bind, Pay Premium, File Claim
Insured Person/object covered by the policy InsuredParty, RiskObject May differ from the Policyholder
Insurer (Carrier) Assumes the risk, pays claims Company, Portfolio, Reserve Underwrite, Issue, Settle Claims
Agent Represents the insurer, sells policies Agent, Agency, Commission Distribute, Quote, Service
Broker Represents the client, negotiates terms Broker, BrokerFirm, Placement Place Risk, Negotiate Terms
Reinsurer Insures the insurer (excess risks) Treaty, Cession, Recovery Cede, Retrocede, Settle
Adjuster Evaluates claims and determines indemnity Assessment, Estimate, Report Inspect, Assess, Recommend
Regulator Oversees the market, enforces rules Filing, Compliance, Report Approve Products, Audit, Fine

Critical Relationship: Policyholder vs Insured vs Beneficiary

One of the most underestimated complexities is the distinction between policyholder, insured, and beneficiary. In the simplest case (individual auto policy), all three are the same person. But in a corporate life policy, the policyholder is the company, the insured are the employees, and the beneficiaries are the designated family members. The data model must support this flexibility with many-to-many relationships between these entities.

The Insurance Value Chain

The insurance value chain describes the entire flow of activities from the first customer contact to claim settlement and policy renewal. Each link in the chain typically corresponds to a bounded context in the software architecture.

Value Chain - Logical Flow
Distribution    Underwriting     Policy Admin     Claims         Investment
    |               |                |              |               |
    v               v                v              v               v
+-----------+  +-----------+  +------------+  +-----------+  +-----------+
| Marketing |  | Risk      |  | Issue      |  | FNOL      |  | Asset     |
| Lead Gen  |  | Selection |  | Endorse    |  | Assess    |  | Mgmt      |
| Quoting   |  | Pricing   |  | Renew      |  | Adjust    |  | ALM       |
| Binding   |  | Approval  |  | Cancel     |  | Settle    |  | Reserves  |
+-----------+  +-----------+  +------------+  +-----------+  +-----------+
    |               |                |              |               |
    +-------+-------+--------+-------+------+-------+-------+------+
            |                |              |               |
       +---------+     +---------+    +---------+    +---------+
       |Reinsur. |     | Billing |    | Fraud   |    | Report  |
       |Cessions |     | Collect |    | Detect  |    | Regulat.|
       +---------+     +---------+    +---------+    +---------+

1. Distribution

Distribution encompasses all channels through which insurance products reach the customer: agents (captive or independent), brokers, bancassurance, comparison websites, and direct channels (web, app, call center). For the developer, this translates into multi-channel quoting APIs, integrated CRMs, and product configuration engines.

2. Underwriting (Risk Assumption)

Underwriting is the process by which the insurer evaluates the proposed risk and decides whether to accept it, under what conditions, and at what price. It is the technical heart of the insurance business and involves:

  • Risk Assessment: Evaluation of risk characteristics (age, health, location, claims history)
  • Rating: Premium calculation through the rating engine (actuarial algorithms + business rules)
  • Risk Selection: Decision to accept, decline, or modify conditions
  • Referral: Escalation to senior underwriter for risks outside automatic parameters

3. Policy Administration

The Policy Administration System (PAS) is the central system managing the entire policy lifecycle: issuance, endorsements, renewals, cancellations, and reinstatements. It is typically the most complex system in the insurance IT ecosystem.

4. Claims Management

Claims management covers the entire process from loss notification (FNOL - First Notice of Loss) to settlement and potential recovery (subrogation). It is the process that directly impacts customer satisfaction and insurer profitability.

5. Investments and Reserve Management

Insurers invest collected premiums while waiting to pay future claims. Investment management (Asset-Liability Management) and the calculation of technical reserves are regulated processes requiring sophisticated actuarial models.

Core Data Models

Let's dive into what a developer needs to model. The following are the fundamental aggregates (in the DDD sense) of any insurance platform.

Key Entities and Relationships

TypeScript - Insurance Domain Data Model
// ============================================
// Core Insurance Domain Model (TypeScript)
// ============================================

/** Insurance product type */
type LineOfBusiness = 'LIFE' | 'PROPERTY' | 'CASUALTY' | 'HEALTH' | 'MARINE' | 'CYBER';

/** Policy status in its lifecycle */
type PolicyStatus =
  | 'QUOTE'          // Indicative quote
  | 'APPLICATION'    // Formal proposal
  | 'BOUND'          // Bound (commitment made)
  | 'ISSUED'         // Issued
  | 'IN_FORCE'       // Active
  | 'SUSPENDED'      // Suspended
  | 'LAPSED'         // Lapsed (non-payment)
  | 'CANCELLED'      // Cancelled
  | 'EXPIRED'        // Expired
  | 'NON_RENEWED';   // Not renewed

/** Main aggregate: the Policy */
interface Policy {
  readonly id: string;
  readonly policyNumber: string;
  readonly version: number;             // Versioning for endorsements
  readonly lineOfBusiness: LineOfBusiness;
  readonly status: PolicyStatus;
  readonly effectiveDate: Date;
  readonly expirationDate: Date;
  readonly inceptionDate: Date;         // Original issue date
  readonly policyholder: Party;         // Policy owner
  readonly insuredParties: readonly InsuredParty[];
  readonly coverages: readonly Coverage[];
  readonly premium: PremiumBreakdown;
  readonly endorsements: readonly Endorsement[];
  readonly documents: readonly Document[];
  readonly underwritingInfo: UnderwritingInfo;
  readonly createdAt: Date;
  readonly updatedAt: Date;
}

/** Party (individual or organization) */
interface Party {
  readonly id: string;
  readonly type: 'INDIVIDUAL' | 'ORGANIZATION';
  readonly firstName?: string;
  readonly lastName?: string;
  readonly companyName?: string;
  readonly taxId: string;               // Tax identification number
  readonly dateOfBirth?: Date;
  readonly addresses: readonly Address[];
  readonly contacts: readonly ContactInfo[];
  readonly riskProfile?: RiskProfile;
}

/** Insured party with specific role */
interface InsuredParty {
  readonly party: Party;
  readonly role: 'PRIMARY' | 'ADDITIONAL' | 'NAMED_INSURED';
  readonly relationship: string;         // Relationship to policyholder
}

/** Coverage: what is protected and up to what amount */
interface Coverage {
  readonly id: string;
  readonly code: string;                 // E.g.: "TPL", "COMP", "FIRE"
  readonly name: string;
  readonly description: string;
  readonly type: 'BASE' | 'OPTIONAL' | 'MANDATORY';
  readonly limit: Money;                 // Coverage limit
  readonly deductible: Deductible;       // Deductible
  readonly premium: Money;               // Premium for this coverage
  readonly effectiveDate: Date;
  readonly expirationDate: Date;
  readonly exclusions: readonly string[];
  readonly conditions: readonly string[];
}

/** Deductible with various application modes */
interface Deductible {
  readonly type: 'FIXED' | 'PERCENTAGE' | 'WAITING_PERIOD' | 'AGGREGATE';
  readonly amount?: Money;               // For FIXED
  readonly percentage?: number;          // For PERCENTAGE
  readonly waitingDays?: number;         // For WAITING_PERIOD
  readonly aggregateLimit?: Money;       // For AGGREGATE
  readonly appliesToEach: 'CLAIM' | 'OCCURRENCE' | 'POLICY_PERIOD';
}

/** Premium breakdown */
interface PremiumBreakdown {
  readonly grossPremium: Money;          // Gross premium
  readonly netPremium: Money;            // Net premium (for the insurer)
  readonly taxes: Money;                 // Taxes
  readonly fees: Money;                  // Fees and charges
  readonly commission: Money;            // Intermediary commission
  readonly components: readonly PremiumComponent[];
  readonly paymentPlan: PaymentPlan;
}

/** Mid-term policy change */
interface Endorsement {
  readonly id: string;
  readonly endorsementNumber: number;
  readonly type: 'COVERAGE_CHANGE' | 'LIMIT_CHANGE' | 'ADD_INSURED'
              | 'REMOVE_INSURED' | 'ADDRESS_CHANGE' | 'VEHICLE_CHANGE'
              | 'GENERAL_CHANGE';
  readonly effectiveDate: Date;
  readonly description: string;
  readonly premiumAdjustment: Money;     // Premium difference (+/-)
  readonly previousVersion: number;
  readonly newVersion: number;
  readonly changes: readonly FieldChange[];
  readonly approvedBy?: string;
  readonly approvedAt?: Date;
}

/** Monetary value with currency */
interface Money {
  readonly amount: number;
  readonly currency: string;             // ISO 4217: "EUR", "USD", "GBP"
}

/** Single tracked change in an endorsement */
interface FieldChange {
  readonly field: string;
  readonly oldValue: string;
  readonly newValue: string;
}

Key Pattern: Immutability and Versioning

Notice how every interface uses readonly on all properties. In the insurance domain, traceability is a regulatory requirement: every change to a policy generates a new endorsement that increments the version. You never "modify" a policy; you create a new version. This pattern pairs perfectly with event sourcing and CQRS architectures.

The Policy Lifecycle

A policy passes through well-defined states during its existence. We model this lifecycle as a state machine, a fundamental pattern in insurance software.

TypeScript - Policy Lifecycle State Machine
// ============================================
// Policy Lifecycle State Machine
// ============================================

/** Valid transitions in the policy lifecycle */
type PolicyTransition =
  | { from: 'QUOTE';       to: 'APPLICATION';  action: 'SUBMIT_APPLICATION' }
  | { from: 'QUOTE';       to: 'CANCELLED';    action: 'DECLINE_QUOTE' }
  | { from: 'APPLICATION'; to: 'BOUND';        action: 'BIND_COVERAGE' }
  | { from: 'APPLICATION'; to: 'CANCELLED';    action: 'DECLINE_APPLICATION' }
  | { from: 'BOUND';       to: 'ISSUED';       action: 'ISSUE_POLICY' }
  | { from: 'ISSUED';      to: 'IN_FORCE';     action: 'ACTIVATE' }
  | { from: 'IN_FORCE';    to: 'IN_FORCE';     action: 'ENDORSE' }
  | { from: 'IN_FORCE';    to: 'SUSPENDED';    action: 'SUSPEND' }
  | { from: 'IN_FORCE';    to: 'CANCELLED';    action: 'CANCEL' }
  | { from: 'IN_FORCE';    to: 'EXPIRED';      action: 'EXPIRE' }
  | { from: 'IN_FORCE';    to: 'IN_FORCE';     action: 'RENEW' }
  | { from: 'IN_FORCE';    to: 'NON_RENEWED';  action: 'NON_RENEW' }
  | { from: 'SUSPENDED';   to: 'IN_FORCE';     action: 'REINSTATE' }
  | { from: 'SUSPENDED';   to: 'LAPSED';       action: 'LAPSE' }
  | { from: 'LAPSED';      to: 'IN_FORCE';     action: 'REINSTATE' }
  | { from: 'LAPSED';      to: 'CANCELLED';    action: 'CANCEL' };

/** Valid transition map for runtime validation */
const VALID_TRANSITIONS: ReadonlyMap<PolicyStatus, readonly PolicyTransition[]> = new Map([
  ['QUOTE', [
    { from: 'QUOTE', to: 'APPLICATION', action: 'SUBMIT_APPLICATION' },
    { from: 'QUOTE', to: 'CANCELLED', action: 'DECLINE_QUOTE' },
  ]],
  ['APPLICATION', [
    { from: 'APPLICATION', to: 'BOUND', action: 'BIND_COVERAGE' },
    { from: 'APPLICATION', to: 'CANCELLED', action: 'DECLINE_APPLICATION' },
  ]],
  ['IN_FORCE', [
    { from: 'IN_FORCE', to: 'IN_FORCE', action: 'ENDORSE' },
    { from: 'IN_FORCE', to: 'SUSPENDED', action: 'SUSPEND' },
    { from: 'IN_FORCE', to: 'CANCELLED', action: 'CANCEL' },
    { from: 'IN_FORCE', to: 'EXPIRED', action: 'EXPIRE' },
    { from: 'IN_FORCE', to: 'IN_FORCE', action: 'RENEW' },
    { from: 'IN_FORCE', to: 'NON_RENEWED', action: 'NON_RENEW' },
  ]],
]);

/** Pure function for state transition */
function transitionPolicy(
  policy: Policy,
  action: PolicyTransition['action'],
  context: TransitionContext
): Policy {
  const validTransitions = VALID_TRANSITIONS.get(policy.status);
  const transition = validTransitions?.find(t => t.action === action);

  if (!transition) {
    throw new InvalidTransitionError(
      `Cannot perform ${action} on policy in status ${policy.status}`
    );
  }

  // Create new immutable version of the policy
  return {
    ...policy,
    status: transition.to,
    version: policy.version + 1,
    updatedAt: new Date(),
    endorsements: action === 'ENDORSE'
      ? [...policy.endorsements, context.endorsement!]
      : policy.endorsements,
  };
}

Key Lifecycle Phases

Pre-Issuance Phase

  • Quote: Indicative estimate based on minimal data. Typical validity: 30 days
  • Application: Formal proposal with all data needed for underwriting
  • Bind: The insurer commits to providing coverage (but the policy is not yet issued)
  • Issue: Issuance of the official policy document

Post-Issuance Phase

  • Endorse: Modification of terms during the validity period (new version)
  • Renew: Renewal at expiration (may involve re-rating)
  • Suspend: Temporary suspension (e.g., non-payment)
  • Cancel: Cancellation with pro-rata premium calculation
  • Reinstate: Restoration after suspension or lapse

The Claims Lifecycle

A claim is the event that activates the insurance promise. Its lifecycle is equally structured and lends itself perfectly to state machine modeling.

TypeScript - Claims State Machine and Data Model
// ============================================
// Claims Domain Model
// ============================================

type ClaimStatus =
  | 'FNOL'              // First Notice of Loss - initial notification
  | 'REGISTERED'        // Registered in the system
  | 'UNDER_INVESTIGATION' // Under investigation / adjustment
  | 'ASSESSED'          // Assessed by adjuster
  | 'APPROVED'          // Approved for settlement
  | 'PARTIALLY_APPROVED'// Partially approved
  | 'DENIED'            // Denied
  | 'SETTLED'           // Settled
  | 'REOPENED'          // Reopened
  | 'CLOSED'            // Definitively closed
  | 'SUBROGATION';      // In recovery phase

interface Claim {
  readonly id: string;
  readonly claimNumber: string;
  readonly policyId: string;
  readonly policyNumber: string;
  readonly status: ClaimStatus;
  readonly lossDate: Date;              // Date of loss
  readonly reportDate: Date;            // Date reported
  readonly lossType: string;            // Type of damage
  readonly lossDescription: string;
  readonly lossLocation: Address;
  readonly claimant: Party;             // Who requests indemnity
  readonly reserves: readonly Reserve[];
  readonly payments: readonly ClaimPayment[];
  readonly assessments: readonly Assessment[];
  readonly documents: readonly Document[];
  readonly fraudIndicators: readonly FraudIndicator[];
  readonly totalIncurred: Money;        // Reserve + Paid
  readonly totalPaid: Money;
  readonly totalReserve: Money;
}

/** Reserve: estimate of future claim cost */
interface Reserve {
  readonly id: string;
  readonly type: 'INDEMNITY' | 'EXPENSE' | 'LEGAL';
  readonly amount: Money;
  readonly setDate: Date;
  readonly setBy: string;
  readonly reason: string;
  readonly history: readonly ReserveChange[];
}

/** Payment made on a claim */
interface ClaimPayment {
  readonly id: string;
  readonly type: 'INDEMNITY' | 'EXPENSE' | 'LEGAL' | 'SALVAGE' | 'SUBROGATION';
  readonly amount: Money;
  readonly payee: Party;
  readonly paymentDate: Date;
  readonly paymentMethod: string;
  readonly invoiceReference?: string;
  readonly approvedBy: string;
}

/** Loss adjustment assessment */
interface Assessment {
  readonly id: string;
  readonly assessor: Party;             // Adjuster
  readonly assessmentDate: Date;
  readonly damageEstimate: Money;
  readonly findings: string;
  readonly recommendation: 'APPROVE' | 'DENY' | 'FURTHER_INVESTIGATION';
  readonly photos: readonly string[];   // Photo URLs
  readonly report: Document;
}

/** Potential fraud indicator */
interface FraudIndicator {
  readonly rule: string;
  readonly score: number;               // 0-100
  readonly description: string;
  readonly triggeredAt: Date;
}

Claims Phases in Detail

Phase Activities Actors Involved Data Generated
FNOL Loss notification via phone, web, app, email Insured, Call Center ClaimNotification, InitialReserve
Triage Claim classification, priority assignment, coverage verification Claims Handler, Automated System CoverageVerification, Priority, Assignment
Investigation Documentation gathering, adjustment, circumstance verification Adjuster, Investigator, Medical Expert Assessment, Photos, ExpertReport
Adjudication Decision: full approval, partial, or denial Claims Manager, Claims Committee Decision, AdjustmentCalc
Settlement Indemnity calculation and payment Claims Handler, Payments Office Payment, SettlementAgreement
Recovery Subrogation against responsible third parties, salvage recovery Legal Department, Recovery Office SubrogationClaim, Recovery

Rating Engine Architecture

The rating engine is the component that calculates insurance premiums. It is one of the most critical and performance-sensitive systems in the InsurTech ecosystem: it must process thousands of quotes per second with actuarial precision and complete traceability.

How Premium Calculation Works

Premium calculation follows a well-defined pipeline that transforms risk factors into a final price. Let's look at a simplified example for Motor Third-Party Liability.

TypeScript - Simplified Rating Engine
// ============================================
// Rating Engine - Motor TPL Example
// ============================================

/** Risk factors for motor rating */
interface AutoRatingFactors {
  readonly driverAge: number;
  readonly driverExperience: number;      // Years of license
  readonly vehicleGroup: number;          // Rating group 1-20
  readonly vehicleAge: number;
  readonly postalCode: string;
  readonly claimsHistory: number;         // Claims in last 5 years
  readonly bonusMalusClass: number;       // No-claims class (1-18 in Italy)
  readonly annualMileage: number;
  readonly garaging: 'GARAGE' | 'STREET' | 'PARKING';
  readonly usage: 'PERSONAL' | 'COMMUTE' | 'BUSINESS';
}

/** Rating calculation result */
interface RatingResult {
  readonly baseRate: Money;
  readonly factors: readonly AppliedFactor[];
  readonly technicalPremium: Money;       // Pure risk premium
  readonly loadings: readonly Loading[];
  readonly grossPremium: Money;           // Gross premium
  readonly taxes: Money;
  readonly totalPremium: Money;           // Total amount due
  readonly ratingDate: Date;
  readonly rateTableVersion: string;
  readonly auditTrail: readonly string[];
}

interface AppliedFactor {
  readonly name: string;
  readonly inputValue: string | number;
  readonly factor: number;                // Multiplier (e.g.: 1.25 = +25%)
  readonly source: string;                // Reference table
}

interface Loading {
  readonly type: 'EXPENSE' | 'COMMISSION' | 'PROFIT' | 'CATASTROPHE' | 'REINSURANCE';
  readonly percentage: number;
  readonly amount: Money;
}

/** Rating Engine: pure function that calculates the premium */
function calculateAutoRate(
  factors: AutoRatingFactors,
  rateTables: RateTableSet
): RatingResult {
  const auditTrail: string[] = [];

  // 1. Base Rate from territorial table
  const baseRate = rateTables.territorial.lookup(factors.postalCode);
  auditTrail.push(`Base rate for ${factors.postalCode}: ${baseRate}`);

  // 2. Apply multiplicative factors
  const ageMultiplier = rateTables.ageFactors.lookup(factors.driverAge);
  const vehicleMultiplier = rateTables.vehicleGroups.lookup(factors.vehicleGroup);
  const bonusMalusMultiplier = rateTables.bonusMalus.lookup(factors.bonusMalusClass);
  const experienceMultiplier = rateTables.experience.lookup(factors.driverExperience);
  const mileageMultiplier = rateTables.mileage.lookup(factors.annualMileage);

  const appliedFactors: AppliedFactor[] = [
    { name: 'Age', inputValue: factors.driverAge, factor: ageMultiplier, source: 'AGE_TABLE_v3' },
    { name: 'Vehicle Group', inputValue: factors.vehicleGroup, factor: vehicleMultiplier, source: 'VEH_TABLE_v2' },
    { name: 'Bonus/Malus', inputValue: factors.bonusMalusClass, factor: bonusMalusMultiplier, source: 'BM_TABLE_v1' },
    { name: 'Experience', inputValue: factors.driverExperience, factor: experienceMultiplier, source: 'EXP_TABLE_v1' },
    { name: 'Mileage', inputValue: factors.annualMileage, factor: mileageMultiplier, source: 'KM_TABLE_v2' },
  ];

  // 3. Technical premium = base * product of factors
  const combinedFactor = appliedFactors.reduce((acc, f) => acc * f.factor, 1);
  const technicalPremium = baseRate * combinedFactor;
  auditTrail.push(`Technical premium: ${baseRate} * ${combinedFactor.toFixed(4)} = ${technicalPremium.toFixed(2)}`);

  // 4. Loadings (expense loading, commissions, profit)
  const loadings: Loading[] = [
    { type: 'EXPENSE', percentage: 0.15, amount: { amount: technicalPremium * 0.15, currency: 'EUR' } },
    { type: 'COMMISSION', percentage: 0.12, amount: { amount: technicalPremium * 0.12, currency: 'EUR' } },
    { type: 'PROFIT', percentage: 0.05, amount: { amount: technicalPremium * 0.05, currency: 'EUR' } },
    { type: 'CATASTROPHE', percentage: 0.02, amount: { amount: technicalPremium * 0.02, currency: 'EUR' } },
  ];

  const totalLoadingPct = loadings.reduce((acc, l) => acc + l.percentage, 0);
  const grossPremium = technicalPremium * (1 + totalLoadingPct);

  // 5. Taxes (22.25% for Motor TPL in Italy)
  const taxRate = 0.2225;
  const taxes = grossPremium * taxRate;
  const totalPremium = grossPremium + taxes;

  return {
    baseRate: { amount: baseRate, currency: 'EUR' },
    factors: appliedFactors,
    technicalPremium: { amount: technicalPremium, currency: 'EUR' },
    loadings,
    grossPremium: { amount: grossPremium, currency: 'EUR' },
    taxes: { amount: taxes, currency: 'EUR' },
    totalPremium: { amount: totalPremium, currency: 'EUR' },
    ratingDate: new Date(),
    rateTableVersion: 'MOTOR_TPL_2026_Q1',
    auditTrail,
  };
}

Modern Rating Engine Architecture

A production rating engine is far more complex than this example. Key characteristics include:

  • Versioned Rate Tables: Each rate table has an effective date. The system must apply the correct table based on the policy effective date, not the current date
  • Separate Rule Engine: Business rules (limits, exclusions, discounts) are configurable without code deployment, typically via a rules engine (Drools, RETE, or JSON-based)
  • Complete Audit Trail: Every step of the calculation must be traceable for regulatory compliance
  • Horizontal Scalability: The rating engine must handle spikes of thousands of quotes per second (comparison sites, open enrollment)
  • Idempotency: The same input must always produce the same output (pure function)

Domain-Driven Design for Insurance

Domain-Driven Design (DDD) is the most suitable architectural approach for the insurance domain due to its inherent complexity and the need to align software with business language. Let's see how to apply it concretely.

Bounded Contexts of the Insurance Ecosystem

Each bounded context has its own ubiquitous language, its own models, and its own rules. The term "policy" has different meanings in the Underwriting context (proposal to evaluate) versus Policy Administration (issued contract).

Bounded Context Map - Insurance Platform
// ============================================
// Bounded Context Map
// ============================================
//
// +------------------+     +------------------+     +------------------+
// |   Distribution   |     |   Underwriting   |     |  Policy Admin    |
// |                  |     |                  |     |                  |
// |  - Lead          |     |  - Submission    |     |  - Policy        |
// |  - Opportunity   |---->|  - Risk          |---->|  - Coverage      |
// |  - Quote Request |     |  - Rating        |     |  - Endorsement   |
// |  - Channel       |     |  - Decision      |     |  - Renewal       |
// +------------------+     +------------------+     +------------------+
//         |                        |                        |
//         |                        |                        |
//         v                        v                        v
// +------------------+     +------------------+     +------------------+
// |   CRM / Party    |     |    Reinsurance   |     |     Claims       |
// |                  |     |                  |     |                  |
// |  - Customer      |     |  - Treaty        |     |  - Claim         |
// |  - Agent         |     |  - Cession       |     |  - Reserve       |
// |  - Broker        |     |  - Recovery      |     |  - Payment       |
// |  - Address       |     |  - Bordereaux    |     |  - Assessment    |
// +------------------+     +------------------+     +------------------+
//         |                        |                        |
//         v                        v                        v
// +------------------+     +------------------+     +------------------+
// |     Billing      |     |   Compliance     |     |  Fraud Detection |
// |                  |     |                  |     |                  |
// |  - Invoice       |     |  - Filing        |     |  - Alert         |
// |  - Payment       |     |  - Report        |     |  - Investigation |
// |  - Collection    |     |  - Audit         |     |  - Score         |
// |  - Installment   |     |  - Solvency      |     |  - Rule          |
// +------------------+     +------------------+     +------------------+

// Context Mapping Relationships:
// Distribution --[Conformist]--> Underwriting
// Underwriting --[Published Language]--> Policy Admin
// Policy Admin --[Shared Kernel]--> Billing
// Policy Admin --[Customer/Supplier]--> Claims
// Claims --[Anti-Corruption Layer]--> Fraud Detection
// Underwriting --[Partnership]--> Reinsurance
// All Contexts --[Conformist]--> Compliance

Aggregates and Invariants

In DDD, each aggregate protects its own business invariants. Here are the main invariants for the two key aggregates:

Policy Aggregate

  • A policy must have at least one active coverage
  • The expiration date must be after the effective date
  • The total premium must equal the sum of coverage premiums + loadings
  • Every endorsement must increment the version
  • State transitions must follow the state machine
  • Cannot cancel a policy with open claims

Claim Aggregate

  • The loss date must fall within the policy coverage period
  • The reserve cannot be negative
  • Total paid cannot exceed the coverage limit
  • A closed claim cannot receive new payments (must be reopened)
  • A decision requires at least one adjuster assessment
  • Payment requires approval if it exceeds the authority threshold

Domain Events

Domain events are the mechanism by which bounded contexts communicate in a decoupled manner. Each event represents something significant that happened in the domain.

TypeScript - Insurance Domain Events
// ============================================
// Insurance Domain Events
// ============================================

interface DomainEvent {
  readonly eventId: string;
  readonly eventType: string;
  readonly occurredAt: Date;
  readonly aggregateId: string;
  readonly aggregateType: string;
  readonly version: number;
  readonly correlationId: string;
  readonly causationId?: string;
}

// --- Policy Events ---

interface PolicyQuoted extends DomainEvent {
  readonly eventType: 'POLICY_QUOTED';
  readonly aggregateType: 'Policy';
  readonly quoteNumber: string;
  readonly premium: Money;
  readonly validUntil: Date;
}

interface PolicyIssued extends DomainEvent {
  readonly eventType: 'POLICY_ISSUED';
  readonly aggregateType: 'Policy';
  readonly policyNumber: string;
  readonly effectiveDate: Date;
  readonly premium: Money;
  readonly coverages: readonly string[];
}

interface PolicyEndorsed extends DomainEvent {
  readonly eventType: 'POLICY_ENDORSED';
  readonly aggregateType: 'Policy';
  readonly endorsementNumber: number;
  readonly changes: readonly FieldChange[];
  readonly premiumAdjustment: Money;
}

// --- Claim Events ---

interface ClaimFiled extends DomainEvent {
  readonly eventType: 'CLAIM_FILED';
  readonly aggregateType: 'Claim';
  readonly policyNumber: string;
  readonly lossDate: Date;
  readonly lossType: string;
  readonly initialReserve: Money;
}

interface ClaimSettled extends DomainEvent {
  readonly eventType: 'CLAIM_SETTLED';
  readonly aggregateType: 'Claim';
  readonly settlementAmount: Money;
  readonly payee: string;
}

// --- Cross-Context Reactions ---
// PolicyIssued  -> Billing: createInvoice()
// PolicyIssued  -> Reinsurance: calculateCession()
// ClaimFiled    -> FraudDetection: screenClaim()
// ClaimFiled    -> Reinsurance: notifyCession()
// ClaimSettled  -> Billing: adjustReserve()
// ClaimSettled  -> Reinsurance: calculateRecovery()

ACORD Standards and Interoperability

ACORD (Association for Cooperative Operations Research and Development) is the organization that defines data standards for the global insurance industry. For a developer, ACORD is the equivalent of HL7/FHIR for healthcare or SWIFT for finance.

ACORD Data Standards: What They Are

ACORD provides granular data models, message formats (XML and JSON), and schemas specifically designed for insurance processes. The goal is to ensure efficient data flow between all ecosystem actors: insurers, brokers, agents, reinsurers, and regulators.

ACORD Standard Format Usage Sector
ACORD AL3 Proprietary (fixed-length) Agent-insurer data exchange (USA) P&C Personal Lines
ACORD XML XML Schema Quoting, issuance, claims, billing P&C, Life, Health
ACORD GRLC 2.0 JSON / XML Reinsurance and large commercial (global) Reinsurance, Specialty
ACORD Next-Gen JSON (API-first) Microservices, REST APIs, async events Cross-sector
ACORD Forms PDF/XML Standardized forms (certificates, endorsements) P&C (USA)

GRLC Generation 2.0 (April 2025)

The most significant update in recent years is the launch of GRLC Generation 2.0, a digital-first JSON-based standard optimized for fine-grained transactions like microservices and APIs. It supports straight-through end-to-end processing of the entire policy lifecycle in the global reinsurance ecosystem, enabling modern integrations without relying on legacy XML batch formats.

API Patterns for Insurance

Modern insurance APIs follow specific patterns that reflect the domain's complexity:

  • Asynchronous Quoting: Complex quotes (commercial, specialty) require asynchronous processing. The API returns a quoteRequestId and notifies via webhook upon completion
  • Temporal Versioning: Every resource (policy, coverage) has a temporal dimension. The API supports queries by asOfDate to retrieve historical state
  • Idempotency: Bind and payment operations must be idempotent via idempotencyKey to prevent duplications
  • Event Streaming: State changes are notified via events (webhook or message broker) for integration with downstream systems

The Regulatory Landscape

The insurance industry is one of the most heavily regulated in the world. For a developer, this means that many architectural decisions are driven by compliance, not just engineering best practices.

Key Regulations and Software Impact

Regulation Scope Software Impact
Solvency II EU - Capital and solvency requirements Risk models, SCR (Solvency Capital Requirement) calculation, XBRL reporting, data quality framework
IFRS 17 Global - Insurance contract accounting CSM (Contractual Service Margin) calculation, contract grouping, measurement models (BBA, VFA, PAA)
GDPR / Privacy EU - Personal data protection Granular consent, right to erasure vs mandatory retention (conflict), pseudonymization, DPIA
IDD EU - Insurance distribution Advice traceability, product suitability, conflicts of interest, pre-contractual documentation
DORA EU - Digital operational resilience ICT risk management, incident reporting, resilience testing, third-party risk (cloud providers)
ICS 2025 Global - Capital standard for IAIGs Regulatory capital calculation with unified global standard (operational since 2025)

The GDPR vs Insurance Retention Conflict

One of the most complex dilemmas for the insurance developer: GDPR mandates the right to erasure, but insurance regulations require data retention for periods ranging from 5 to 30 years (claims with open reserves, legal disputes, actuarial obligations). The typical solution involves progressive pseudonymization: identifying data is separated from transactional data and deleted when possible, while anonymized data is maintained for regulatory obligations.

Modern InsurTech Platform Architecture

Modern insurance platforms adopt a cloud-native, API-first, event-driven architecture. The legacy monolith is decomposed into microservices aligned with the domain's bounded contexts.

Typical Technology Stack (2025-2026)

Layer Technologies Function
Frontend Angular, React, Vue + micro-frontend Agent portal, customer portal, back-office
API Gateway Kong, AWS API Gateway, Apigee Rate limiting, auth, routing, versioning
Microservices Node.js/NestJS, Java/Spring Boot, Go Policy, Claims, Rating, Billing, CRM
Event Bus Apache Kafka, AWS EventBridge, RabbitMQ Domain events, CQRS, event sourcing
Database PostgreSQL, MongoDB, DynamoDB Database per service (database per bounded context)
AI/ML Python, TensorFlow, SageMaker Fraud detection, pricing optimization, document OCR
Orchestration Kubernetes, AWS ECS, Temporal.io Scaling, saga pattern, long-running workflows
Observability Datadog, Grafana, OpenTelemetry Distributed tracing, metrics, centralized logging

Key Architectural Patterns

Event Sourcing for Policies

Event sourcing is particularly well-suited for the insurance domain because a policy is inherently a sequence of events (issuance, endorsement, renewal, claim). Instead of storing only the current state, all events are stored and the current state is reconstructed through replay. This automatically provides a complete audit trail, a fundamental regulatory requirement.

Saga Pattern for Cross-Context Processes

Processes like policy issuance involve multiple bounded contexts: Underwriting approves, Policy Admin issues, Billing creates the invoice, Reinsurance calculates the cession. The saga pattern (orchestrated or choreographed) coordinates these distributed steps, ensuring eventual consistency and compensation in case of failure. Temporal.io is particularly popular in InsurTech for implementing long-running workflows with persistent state.

Multi-Agent AI Systems (2026 Trend)

Instead of a single monolithic AI assistant, the most advanced InsurTech platforms are implementing multi-agent systems with specialized agents: one for claims intake, one for fraud checking, one for customer communications, one for payments and recovery. These agents are orchestrated through clear workflows and collaborate to automate the entire claims lifecycle.

Conclusions and Next Steps

The insurance domain is one of the richest and most complex that a developer can tackle. In this article, we have built the foundations: products, actors, value chain, data models, lifecycles, rating engine, applied DDD, and the regulatory landscape.

Here are the key takeaways:

  • The insurance product is a promise: this makes data models inherently temporal and versioned
  • There is no universal model: Life, P&C, Health, and Specialty have profoundly different needs
  • The state machine is the fundamental pattern: for both policies and claims, the lifecycle is a state machine with well-defined transitions
  • Immutability is a regulatory requirement: every change generates a new version, never an overwrite
  • DDD is the natural approach: bounded contexts aligned with the value chain, aggregates protecting business invariants, domain events for decoupling
  • Compliance drives architecture: Solvency II, IFRS 17, GDPR, and DORA are not "nice to have" but primary architectural constraints
  • ACORD is the lingua franca: ACORD standards (especially Next-Gen JSON) are essential for ecosystem interoperability

In the Next Article

In the next article of the InsurTech Engineering series, we will dive deep into the Policy Administration System: how to design a modern PAS with product configurator, temporal versioning, endorsement engine, and rating engine integration. We will explore concrete patterns for managing the complexity of multi-coverage insurance products with a domain-driven approach.

Commenti

Caricamento commenti...

Sign in to comment

Discuss this article in the community

Join the Telegram group to discuss with other developers, ask questions and share your experiences.

Did you like this article?

Explore other content on the blog or discover my projects