Template-Driven Architecture Implementation¶
Overview¶
This document describes the implementation of the template-driven architecture for ServicePlan management, which simplifies agent logic configuration and ensures contract stability.
Updates (Sept 2025) - ServicePlanTemplate aligns with current schema:
country_code,legal_jurisdiction,billing_currency,contract_terms,service_cycle_fsm,payment_cycle_fsm, andagent_config. - Removed legacy fields:default_plan_params,default_agent_params,agent,agent_version. - ServicePlan embedsplan_state,service_account,payment_account, and hasservice_state/payment_stateinitialized from FSMs; no plan-levelcurrency. - No overrides increateServicePlanFromTemplate; onlytemplate_idandcustomer_id. - Agents expose a singleserviceAgent;CalculationFunctionis(request, servicePlan, context)and agent params are derived fromservicePlan.service_plan_template.agent_config.agent_params.
Key Changes Made¶
1. Schema Updates¶
ServicePlanTemplate (current schema)¶
type ServicePlanTemplate {
id: ID!
name: String!
description: String!
version: String!
status: String!
country_code: String!
legal_jurisdiction: String!
billing_currency: String!
contract_terms: CommonTerms!
service_cycle_fsm: MealyFSM!
payment_cycle_fsm: MealyFSM!
agent_config: AgentConfig!
created_at: DateTime!
updated_at: DateTime!
created_by: String!
change_log: [TemplateChange!]!
}
ServicePlan (current schema highlights)¶
type ServicePlan {
id: ID!
customer_id: ID!
template_id: ID!
template_version: String!
plan_state: CommonState!
service_state: String!
payment_state: String!
agent_state: JSON @domainSpecific
service_account: ServiceAccount!
payment_account: PaymentAccount!
}
Added Template Operations¶
type Query {
servicePlanTemplates: [ServicePlanTemplate!]!
servicePlanTemplate(id: ID!): ServicePlanTemplate
}
type Mutation {
createServicePlanTemplate(input: CreateTemplateInput!): ServicePlanTemplate!
updateServicePlanTemplate(id: ID!, input: UpdateTemplateInput!): ServicePlanTemplate!
deprecateServicePlanTemplate(id: ID!): ServicePlanTemplate!
createServicePlanFromTemplate(input: CreateServicePlanFromTemplateInput!): ServicePlanCloneResult!
}
2. Removed Complex Handler System¶
Deleted Files¶
src/core/commands.ts- Command registry systemsrc/core/runtime.ts- Dynamic handler runtimesrc/core/handlers/- Individual handler filessrc/core/registries/- Registry system
Simplified Agent Logic¶
The agent now directly uses ServicePlan data without complex handler abstraction:
export class BssPlanAgent extends BaseAgent {
verifyServiceQuota(input: QuotaCheckInput) {
// 🔥 Use ServicePlan's own data in computation
const planParams = this.params.plan_params;
const agentParams = this.params.agent_params;
const planState = this.params.plan_state;
const agentState = this.state; // ServicePlan's agent_state
// 🔥 Apply ServicePlan-specific logic based on its params
const strictMode = agentParams?.quota_validation?.strict_mode || false;
// ... computation logic ...
// 🔥 Update ServicePlan's own agent_state
this.updateState(updatedState);
}
}
3. Template Service Implementation¶
Created src/core/template-service.ts¶
export class ServicePlanTemplateService {
// Create new template
async createTemplate(input: CreateTemplateInput): Promise<ServicePlanTemplate>
// Create ServicePlan from template
async createServicePlanFromTemplate(input: CreateServicePlanFromTemplateInput): Promise<ServicePlanCloneResult>
// Refine template (create new version)
async refineTemplate(templateId: string, refinements: any): Promise<ServicePlanTemplate>
// Validate template refinements don't break existing contracts
async validateTemplateRefinement(template: ServicePlanTemplate, refinements: any): Promise<void>
}
4. Updated Resolvers¶
Template Operations¶
export const resolvers = {
Query: {
async servicePlanTemplates() { /* ... */ },
async servicePlanTemplate(_: any, { id }: { id: string }) { /* ... */ }
},
Mutation: {
async createServicePlanTemplate(_: any, { input }: { input: any }) {
return await templateService.createTemplate(input);
},
async createServicePlanFromTemplate(_: any, { input }: { input: any }) {
return await templateService.createServicePlanFromTemplate(input);
}
}
}
ServicePlan Data Integration¶
function createAgent(servicePlan: any): BssPlanAgent {
const config: BaseAgentConfig = {
// ... agent config ...
// ServicePlan's own data
params: {
plan_params: servicePlan.plan_params,
plan_state: servicePlan.plan_state,
agent_params: servicePlan.agent_params,
service_account: servicePlan.service_account,
payment_account: servicePlan.payment_account
},
// ServicePlan's own agent_state
initial_state: servicePlan.agent_state || {}
};
return new BssPlanAgent(config);
}
Benefits Achieved¶
1. Simplified Architecture¶
- Removed complex handler system - No more dynamic registry building
- Direct template logic - Agents follow template configuration directly
- Clear data flow - ServicePlan data flows directly into agent computation
2. Contract Stability¶
- Template version pinning - ServicePlan instances pin their template version
- Immutable contracts - Running ServicePlans maintain their original behavior
- Template refinement - New template versions can be created without affecting existing contracts
3. Behavior Consistency¶
- Template-driven behavior - All ServicePlans from same template use same logic
- Instance-specific data - Each ServicePlan uses its own data (plan_params, plan_state, agent_params, agent_state)
- Clear separation - Template defines behavior, instance provides data
4. Schema Stability¶
- JSON fields remain unchanged -
@domainSpecificdirective maintains flexibility - Backward compatibility - Existing ServicePlan structure preserved
- Minimal breaking changes - Only added template reference fields
Usage Examples¶
Creating a Template¶
mutation {
createServicePlanTemplate(input: {
name: "BSS Premium Plan"
description: "Premium battery swap service plan"
country_code: "KE"
legal_jurisdiction: "Nairobi County, Kenya"
contract_terms_id: "terms-kenya-premium"
service_cycle_fsm_id: "fsm-bss-service-cycle-v1"
payment_cycle_fsm_id: "fsm-bss-payment-cycle-v1"
agent_config_id: "bss-agent-config-001"
created_by: "admin"
}) {
id
name
version
}
}
Creating ServicePlan from Template¶
mutation {
createServicePlanFromTemplate(input: {
template_id: "template-123"
customer_id: "customer-456"
}) {
cloned_plan {
id
template_id
template_version
}
}
}
Next Steps¶
- Implement data persistence - Connect template service to actual database
- Add template validation - Validate template refinements against existing contracts
- Enhance agent logic - Add more sophisticated computation using ServicePlan data
- Add template versioning - Implement semantic versioning for templates
- Add template migration - Tools to migrate ServicePlans between template versions
Conclusion¶
The template-driven architecture successfully simplifies the agent logic implementation while maintaining the flexibility to evolve behavior through template refinement. The key insight is that templates provide the structural restrictions needed to prevent type explosion, eliminating the need for complex handler systems.