Skip to content

Design Patterns & Architectural Principles

Core Design Patterns

1. Finite State Machine (FSM) Pattern {#fsm-patterns}

O(1) FSM Implementation

The ABS Platform implements mathematically optimized FSMs with precomputed transition maps for constant-time lookups.

// FSM transition function: f:(S,Σ)→(S′,P(Λ))
interface FSMTransition {
  currentState: State;
  input: Signal;
  targetState: State;
  outputs: Set<Output>;
}

// Precomputed transition map for O(1) performance
class OptimizedFSM {
  private transitionMap: Map<string, FSMTransition>;

  getTransition(state: State, input: Signal): FSMTransition | null {
    const key = `${state.id}:${input.type}`;
    return this.transitionMap.get(key) || null;
  }
}

Signal Compression Pattern

Complex external events are compressed into fundamental FSM inputs to maintain performance and simplicity.

// External event compression
export class SignalCompressor {
  compressExternalEvent(event: ExternalEvent): FSMSignal[] {
    // Complex business logic reduced to fundamental signals
    switch (event.type) {
      case 'CUSTOMER_PAYMENT_PROCESSED':
        return [{ type: 'DEPOSIT_PAID', data: event.paymentData }];
      case 'BATTERY_SWAP_COMPLETED':
        return [
          { type: 'BATTERY_RETURNED', data: event.returnData },
          { type: 'BATTERY_ISSUED', data: event.issueData }
        ];
    }
  }
}

2. Data-Driven Agent Pattern

Functional Agent Design

Agents are pure functions that receive context and return state updates and outputs.

// Agent function signature
export type AgentFunction = (
  request: AgentRequest,
  servicePlan: ServicePlan,
  context: AgentContext
) => Promise<AgentResult>;

// Example agent implementation
export const batterySwapAgent: AgentFunction = async (request, servicePlan, context) => {
  // 1. Validate business rules
  const validation = await validateServiceAccess(request, servicePlan, context);

  // 2. Process business logic
  const result = await processBatterySwap(request, servicePlan, context);

  // 3. Update agent state
  const updatedState = updateAgentState(context.state, result);

  // 4. Generate FSM signals
  const fsmSignals = generateFSMSignals(result);

  return {
    success: true,
    updatedState,
    outputs: result.outputs,
    fsmSignals
  };
};

Context Injection Pattern

Rich execution context provides utilities and dependencies without tight coupling.

interface AgentContext {
  state: AgentState;
  utilities: AgentUtilities;
  services: ServiceRegistry;
  logger: Logger;
  metrics: MetricsCollector;
}

// Context injection enables testing and modularity
export const executeAgent = async (
  agentFunction: AgentFunction,
  request: AgentRequest,
  servicePlan: ServicePlan,
  context: AgentContext
): Promise<AgentResult> => {
  return await agentFunction(request, servicePlan, context);
};

3. Template-Driven Configuration Pattern

Immutable Template Design

ServicePlanTemplates provide immutable configuration that ServicePlans inherit.

interface ServicePlanTemplate {
  readonly id: string;
  readonly country: string;        // Immutable after creation
  readonly jurisdiction: string;   // Immutable after creation
  readonly currency: string;       // Inherited by all derived objects
  readonly agent_config: AgentConfig;
  readonly fsm_definitions: FSMDefinitions;
}

// Template inheritance ensures consistency
export class ServicePlanFactory {
  createFromTemplate(
    template: ServicePlanTemplate,
    customData: ServicePlanCustomData
  ): ServicePlan {
    return {
      ...customData,
      // Inherited immutable properties
      country: template.country,
      jurisdiction: template.jurisdiction,
      currency: template.currency,
      // Template references
      templateId: template.id,
      agent_config: template.agent_config,
      fsm_definitions: template.fsm_definitions
    };
  }
}

4. Event-Driven Messaging Pattern

MQTT Topic Hierarchy

Structured topic patterns enable efficient message routing and filtering.

/dirac/{component}/{domain}/{entity_id}/{event_type}

Examples:
/dirac/abs/service/plan-001/state_changed
/dirac/arm/asset/battery-123/status_updated
/dirac/bro/coordination/system/health_check

Message Criticality Patterns

Different message patterns based on criticality and coordination requirements.

// High criticality: call/return pattern
export const callReturnPattern = {
  call: '/call/abs/service/{plan_id}/asset_allocation',
  return: '/return/abs/service/{plan_id}/asset_allocated'
};

// Medium criticality: emit/echo pattern  
export const emitEchoPattern = {
  emit: '/emit/abs/service/{plan_id}/service_signal',
  echo: '/echo/abs/service/{plan_id}/signal_received'
};

// Low criticality: stat/meta pattern
export const statMetaPattern = {
  stat: '/stat/abs/metrics/service_usage',
  meta: '/meta/abs/system/configuration'
};

Architectural Principles

1. Separation of Concerns

Domain Boundaries

Clear boundaries between different platform concerns:

  • FSM Layer: Pure state management without business logic
  • Agent Layer: Business logic without state management
  • Service Layer: Coordination without implementation details
  • API Layer: Interface without business rules

Model Confinement

Agent-specific types and state confined to agent modules:

// Agent state interface (confined to agent module)
interface BSSAgentState {
  quotaUsage: QuotaUsage;
  paymentStatus: PaymentStatus;
  // Agent-specific state properties
}

// Generic platform interface (shared)
interface ServicePlan {
  id: string;
  agent_state: Record<string, unknown>; // Generic storage
}

2. Single Responsibility Principle

Component Responsibilities

Each component has a single, well-defined responsibility:

  • FED (Federated API): API coordination and schema federation
  • BRO (Messaging Broker): Asynchronous event coordination
  • ABS (Asset Services): Service orchestration and business logic
  • ARM (Asset Relations): Asset management and IoT coordination

3. Open/Closed Principle

Extensible Agent System

New agents can be added without modifying existing code:

// Agent registry is open for extension
export const agentRegistry = new Map<string, AgentFunction>([
  ['bss-service-agent', bssServiceAgent],
  ['fleet-charging-agent', fleetChargingAgent],
  // New agents added here without modification
]);

// Agent factory creates agents based on configuration
export class AgentFactory {
  createAgent(agentType: string): AgentFunction {
    const agentFunction = this.agentRegistry.get(agentType);
    if (!agentFunction) {
      throw new Error(`Unknown agent type: ${agentType}`);
    }
    return agentFunction;
  }
}

4. Dependency Inversion Principle

Interface-Based Dependencies

High-level modules depend on abstractions, not concretions:

// Abstract interface
interface PaymentProcessor {
  processPayment(amount: number, currency: string): Promise<PaymentResult>;
}

// Service depends on abstraction
export class ServicePlanService {
  constructor(private paymentProcessor: PaymentProcessor) {}

  async processSubscriptionPayment(plan: ServicePlan): Promise<void> {
    await this.paymentProcessor.processPayment(plan.amount, plan.currency);
  }
}

// Concrete implementation
export class OdooPaymentProcessor implements PaymentProcessor {
  async processPayment(amount: number, currency: string): Promise<PaymentResult> {
    // Odoo-specific implementation
  }
}

Performance Patterns

1. Caching Strategies

Multi-Level Caching

Strategic caching at different architectural levels:

// Application-level caching
export class ServicePlanService {
  private templateCache = new LRUCache<string, ServicePlanTemplate>(100);

  async getTemplate(templateId: string): Promise<ServicePlanTemplate> {
    // Check cache first
    if (this.templateCache.has(templateId)) {
      return this.templateCache.get(templateId)!;
    }

    // Load from database and cache
    const template = await this.repository.findById(templateId);
    this.templateCache.set(templateId, template);
    return template;
  }
}

// Database-level caching
export class FSMEngine {
  private transitionCache = new Map<string, CompiledTransitionMap>();

  getCompiledFSM(fsmId: string): CompiledTransitionMap {
    if (!this.transitionCache.has(fsmId)) {
      const fsm = this.loadAndCompileFSM(fsmId);
      this.transitionCache.set(fsmId, fsm);
    }
    return this.transitionCache.get(fsmId)!;
  }
}

2. Lazy Loading Pattern

On-Demand Resource Loading

Load expensive resources only when needed:

export class AgentExecutor {
  private agentInstances = new Map<string, AgentFunction>();

  async executeAgent(agentType: string, ...args: unknown[]): Promise<AgentResult> {
    // Lazy load agent function
    if (!this.agentInstances.has(agentType)) {
      const agentFunction = await this.loadAgentFunction(agentType);
      this.agentInstances.set(agentType, agentFunction);
    }

    const agent = this.agentInstances.get(agentType)!;
    return await agent(...args);
  }
}

Security Patterns

1. Defense in Depth

Multi-Layer Security

Security controls at multiple architectural layers:

  • Network Layer: VPC, subnets, security groups
  • Application Layer: Authentication, authorization, input validation
  • Data Layer: Encryption at rest, encrypted connections
  • Code Layer: Secure coding practices, dependency scanning

2. Principle of Least Privilege

Role-Based Access Control

Granular permissions based on actual requirements:

interface UserPermissions {
  canCreateServicePlan: boolean;
  canModifyServicePlan: boolean;
  canViewCustomerData: boolean;
  allowedCountries: string[];
  allowedServiceTypes: string[];
}

export class AuthorizationService {
  async checkPermission(
    user: User, 
    action: string, 
    resource: Resource
  ): Promise<boolean> {
    const permissions = await this.getUserPermissions(user);
    return this.evaluatePermission(permissions, action, resource);
  }
}

These patterns collectively create a robust, scalable, and maintainable architecture that supports complex multi-stakeholder coordination while maintaining high performance and reliability.