Deal Engine Architecture Proposal: Clause Types, Deal Types, and Deal Instances
Version: 2.0
Last Updated: January 2026
Overview
The Deal Engine v2 architecture uses a Clause Type Composition Model to transform entertainment contracts into executable deal models. The architecture separates structure (defined in DSL) from data (provided in JSON), enabling reusable building blocks that compose into complete deals.
flowchart LR
subgraph Catalog["Design-Time Catalog"]
CT[Clause Types]
DT[Deal Types]
end
subgraph Runtime["Runtime"]
DI[Deal Instance]
end
CT -->|"copied into"| DI
DT -->|"guides creation"| DI
style DI fill:#e1f5feThe system consists of three core components:
| Component | Purpose | Lifetime |
|---|---|---|
| Clause Type | Reusable financial arrangement template | Design-time |
| Deal Type | Template suggesting clause combinations | Design-time |
| Deal Instance | Self-contained executable contract | Runtime |
Clause Types
A Clause Type defines a reusable financial arrangement—the building block of deals. Each clause type answers the fundamental financial questions: how much, when earned, and when received.
Structure
Clause Types define a set of data and calculations that capture the logic of a component of a deal. Each Clause Type contains the following sections.
| Section | Purpose |
|---|---|
header | Identity and classification (id, version, category, value_type, name) |
schema | JSON Schema defining the data structure for this clause |
inputs | Declarations of deal-level data this clause needs access to |
logic | Events, computations, and for_each iterations that process data |
financial | Core financial interface: amount, earned timing, received timing |
outputs | Values exposed to deal-level logic and other clauses |
template | Contract text rendering with variable substitution |
Two-Dimensional Classification
Every clause is classified along two orthogonal dimensions. Category determines certainty of obligation; Value Type determines the nature of value:
| earning | reimbursement | third_party | in_kind | |
|---|---|---|---|---|
| guarantee | Base fee | Per diem | Publicist fee | Wardrobe allowance |
| contingent | Performance bonus | Travel if extended | Stylist if awards | Extra wardrobe if option |
| simple | (non-financial: exclusivity, territory rights, etc.) |
This two-dimensional classification enables accurate aggregation for commissions, accounting, and reporting—a guarantee reimbursement is treated differently than a contingent earning.
Earned vs. Received
Every financial clause has two timing dimensions, reflecting the separation of revenue recognition and cash flow:
| Dimension | Question | Business Concept |
|---|---|---|
| Earned | When is the value recognized? | Revenue recognition (accounting) |
| Received | When is cash received? | Cash flow |
A show may be earned when it plays (revenue is recognized), but cash may not be received until 30-60 days later. The architecture tracks both independently.
Financial Section
The financial section is the core interface for guarantee and contingent clauses:
financial {
amount: total_earned // How much?
earned: on earning_schedule // When earned? (revenue recognition)
received: on receipt_schedule // When received? (cash flow)
when: all_shows_settled // Optional event guard
}Full specification: clause-type-specification-v1_4.md
Deal Types
A Deal Type is a template that guides deal composition. It suggests which clause types to use together but does not constrain—users can freely add, omit, or modify clauses as long as the resulting deal logic is internally consistent (i.e., the deal can "compile").
Structure
Deal Types contain the following sections.
| Section | Purpose |
|---|---|
header | Identity and classification (id, version, name, department, tags) |
schema | JSON Schema defining deal-level data structure (parties, dates, territory, currency) |
suggested_clauses | Clause types commonly used with this deal type, with cardinality hints |
logic | Deal-level events and computations for cross-clause aggregation |
outputs | Aggregated values exposed for reporting and downstream systems |
Guiding Principle: Suggest, Don't Dictate
Deal Type: "Music Touring"
├── Suggests: show-settlement clause (required: true)
├── Suggests: tour-versus clause
├── Suggests: travel-reimbursement clause
└── User decides which to includeThe required: true flag is advisory only—it pre-selects clauses in the UI and may trigger warnings, but never blocks deal creation. Nonetheless, deals must be internally consistent and any deal lacking an input required by a selected clause would fail validation.
Deal-Level Aggregation
Deal types define logic for aggregating across clauses:
logic {
computations {
output total_earned =
@show_settlement.total_earned + @bonus.total_earned
output total_pending = total_earned - total_received
}
}Full specification: deal-type-specification-v1_3.md
Key DSL Features
for_each Iteration
Process collections with dynamic event naming:
for_each show in shows {
event {
name: show_settled_{show.id}
condition: show.settled == true
}
computations {
metric show.earned = max(show.guarantee, show.artist_share)
}
}Cross-Clause References
Clauses communicate through declared outputs:
// Single instance
@show_settlement.total_earned
// Multiple instances (cardinality: many)
sum(@bonus_groups[*].earned)Collection Operations
sum(shows[*].earned) ?? 0
count(shows where show.settled == true)
max(tiers where tier.achieved == true, tier.amount)Expressions
| Feature | Syntax |
|---|---|
| Null coalescing | value ?? default |
| Conditional | if cond then a else b |
| Arithmetic | gross - expenses |
| Comparison | amount >= threshold |
Full DSL reference: dsl-reference-v2_1.md
ANTLR grammar: DealModel-v2.g4
Deal Instances
A Deal Instance is a self-contained, executable representation of a specific contract. Once created, it has no runtime dependencies on the catalog.
Structure: Two Levels of Data
A deal instance contains data and logic at two levels:
flowchart TB
subgraph DI["Deal Instance"]
DD[Deal Data]
DL[Deal Logic]
subgraph CI1["Clause Instance A"]
ID1[Instance Data]
CL1[Clause Logic]
end
subgraph CI2["Clause Instance B"]
ID2[Instance Data]
CL2[Clause Logic]
end
subgraph CI3["Clause Instance C"]
ID3[Instance Data]
CL3[Clause Logic]
end
end
DD --- CI1
DD --- CI2
DD --- CI3
DL --- CI1
DL --- CI2
DL --- CI3| Level | Data | Logic |
|---|---|---|
| Deal Instance | Parties, dates, territory, currency | Aggregation across clauses, deal-level events |
| Clause Instance | Clause-specific values (shows, tiers, amounts) | Copied from clause type, frozen at creation |
Self-Containment Principle
When a deal is created, logic is frozen—copied from the clause types and deal type into the instance. Contract terms don't change if types are later updated in the catalog.
flowchart LR
subgraph Catalog["Design-Time Catalog"]
DT1[Deal Type 1]
DT2[Deal Type 2]
CTA[Clause Type A]
CTB[Clause Type B]
CTC[Clause Type C]
CTD[Clause Type D]
end
subgraph Instance["Deal Instance"]
DD[Deal Data]
DL[Deal Logic]
CIA[Clause Instance A]
CIC[Clause Instance C]
CID[Clause Instance D]
end
DT1 -.->|suggests| CTA
DT1 -.->|suggests| CTB
DT2 ==>|suggests| CTB
DT2 ==>|suggests| CTC
DT2 ==>|suggests| CTD
DT2 -->|"schema + logic"| DD
DT2 -->|"schema + logic"| DL
CTA -->|"logic copied"| CIA
CTC -->|"logic copied"| CIC
CTD -->|"logic copied"| CID
style Instance fill:#e8f5e9Reactive Computation Model
Deal Engine uses a full-recalculation approach: any data change triggers complete recomputation of all values.
flowchart TB
IC[Input Change] --> FR[Full Recomputation]
FR --> EC[Evaluate Clauses]
EC --> ED[Evaluate Deal Logic]
ED --> NS[New Computed State]
subgraph EC[Evaluate Clauses]
FE[for_each loops]
CE[Clause events]
CO[Clause outputs]
endThis approach is simpler than incremental computation and guarantees consistent state—there are no stale values or complex dependency tracking.
Full specification: deal-instance-specification-v1_3.md
Instance Versioning
Deal instances are versioned. Every change—whether to data or logic—creates a new immutable version, providing a complete audit trail.
flowchart LR
V1["v1<br/>Initial"] --> V2["v2<br/>Show 1 settled"]
V2 --> V3["v3<br/>Show 2 settled"]
V3 --> V4["v4<br/>Bonus amended"]
V4 --> V5["v5<br/>Show 3 settled"]
style V5 fill:#27ae60,color:whiteWhat Triggers New Versions
| Change Type | Example | Recalculation |
|---|---|---|
data_update | Settlement entered, event occurred | Full recalculation |
clause_addition | New bonus structure added | Full recalculation |
clause_replacement | Renegotiated terms | Full recalculation (new clause only) |
logic_amendment | Calculation formula modified | Full recalculation from inception |
Key Versioning Concepts
| Concept | Description |
|---|---|
| Immutable versions | Each version is a complete, frozen snapshot |
| Effective dates | Versions have effective dates for point-in-time queries |
| Archived clauses | Replaced clauses retain their final computed state |
| Prior version chain | Each version references its predecessor |
Effective dates enable backdated amendments (legal changes that apply retroactively) and point-in-time queries ("What did we think this was worth on June 15?").
How Components Relate
flowchart TB
subgraph DesignTime["Design Time"]
CT1[show-settlement]
CT2[tour-versus]
CT3[reimbursement]
DT[Music Touring Deal Type]
DT -.->|suggests| CT1
DT -.->|suggests| CT2
DT -.->|suggests| CT3
end
subgraph Runtime["Runtime"]
DI[Deal Instance]
DI --> DD[deal_data]
DI --> C1[show_settlement clause]
DI --> C2[tour_versus clause]
DI --> CS[computed_state]
end
CT1 -->|"logic frozen"| C1
CT2 -->|"logic frozen"| C2
DT -->|"schema + logic"| DI
style DI fill:#fff3e0Design Rationale
Why Self-Contained Instances?
Legal contracts shouldn't change unexpectedly. When a deal is created, all logic is copied into the instance. If a clause type is later updated in the catalog, existing deals are unaffected—they contain the logic that was in effect when the contract was made.
Why Suggestions, Not Constraints?
Entertainment contracts are highly variable. Even within the same deal category, contracts differ by artist tier, region, and negotiated terms. Rigid templates would require constant exceptions. Deal types provide guidance while respecting real-world variability.
Why "Deal Must Compile"?
While users have flexibility to add, omit, or modify clauses, the resulting deal must be internally consistent. Cross-clause references must resolve, required fields must be present, and computations must be valid. This constraint ensures that every deal instance is executable.
Why Full Recalculation?
Incremental computation (only recalculating changed values) requires complex dependency tracking and risks stale state. Full recalculation is simpler, always consistent, and easier to audit. The deterministic model means the same inputs always produce the same outputs.
Why Two Classification Dimensions?
Category (guarantee/contingent) and value type (earning/reimbursement/third_party/in_kind) are orthogonal concerns. A guarantee can be an earning (base fee) or a reimbursement (per diem). Both dimensions are needed for accurate commission calculation and accounting treatment.
Why Immutable Versioning?
Every version is preserved as a complete snapshot. This provides a full audit trail, enables point-in-time queries, and meets compliance requirements for financial systems. Storage efficiency is handled at the infrastructure level, not by complicating the versioning model.
Why Explicit Output Interfaces?
Clauses communicate only through declared outputs using @clause.output syntax. This creates clear boundaries between clauses, makes dependencies visible and auditable, and supports clause replacement without breaking references (as long as the output interface is maintained).
Summary
| Principle | Implementation |
|---|---|
| Separation of concerns | DSL defines structure; JSON provides data |
| Reusability | Clause types compose into deal types |
| Flexibility | Deal types suggest, don't dictate (within compilation constraints) |
| Self-containment | Instances have no runtime catalog dependencies |
| Auditability | Frozen logic and immutable versions preserve history |
| Explicit interfaces | Clauses communicate only through outputs |
| Dual timing | Earned (recognition) and received (cash flow) tracked separately |