Commit Envelope Examples


Wallet transfer

Atomically debit a sender and credit a receiver. Neither balance changes unless both sides succeed.

# events
events:
  TRANSFER_PREPARED:
    state: transfer
    entity_expr: event.transfer_id
    payload:
      transfer_id: string
      sender_id:   string
      receiver_id: string
      amount:      number
 
  TRANSFER_PREPARE_CONFIRMED:
    state: transfer
    entity_expr: event.transfer_id
    payload:
      transfer_id: string
 
  TRANSFER_COMMITTED:
    state: transfer
    entity_expr: event.transfer_id
    payload:
      transfer_id: string
 
  TRANSFER_ABORTED:
    state: transfer
    entity_expr: event.transfer_id
    payload:
      transfer_id: string
      reason: string
 
# state
state:
  transfer:
    entity_key: transfer_id
    fields:
      - name: status
        type: string
        default: ""
      - name: amount
        type: number
        default: 0
 
  wallet:
    entity_key: wallet_id
    fields:
      - name: available_balance
        type: number
        default: 0
      - name: held_balance
        type: number
        default: 0
      - name: total_balance
        type: number
        default: 0
 
# action
actions:
  INITIATE_TRANSFER:
    state: transfer
    entity_id_expr: intent.transfer_id
    input:
      transfer_id: { type: string, required: true }
      sender_id:   { type: string, required: true }
      receiver_id: { type: string, required: true }
      amount:      { type: number, required: true }
    preflight:
      rules:
        - name: reject_self_transfer
          when:
            expr: "event.sender_id == event.receiver_id"
          then:
            - op: reject
              code: SELF_TRANSFER
        - name: reject_zero_amount
          when:
            expr: "event.amount <= 0"
          then:
            - op: reject
              code: INVALID_AMOUNT
    core:
      rules:
        - name: create_transfer_record
          when: {}
          then:
            - op: set
              path: /status
              value: preparing
            - op: set
              path: /amount
              value: event.amount
            - op: emit
              event_type: TRANSFER_PREPARED
              payload:
                transfer_id: event.transfer_id
                sender_id:   event.sender_id
                receiver_id: event.receiver_id
                amount:      event.amount
 
# commit envelope
commit_envelopes:
  wallet_transfer:
    start_action: INITIATE_TRANSFER
    envelope_state: transfer
    envelope_state_path: /transfer_state
    lifecycle:
      prepare_event:   TRANSFER_PREPARED
      prepared_event:  TRANSFER_PREPARE_CONFIRMED
      commit_event:    TRANSFER_COMMITTED
      abort_event:     TRANSFER_ABORTED
    timeout:
      abort_after_seconds: 30
      tick_event: CLOCK_TICK
    participants:
      - name: sender
        state: wallet
        entity_expr: event.sender_id
        delta_expr: "-event.amount"
        staging:
          rules:
            - name: hold_sender_funds
              when: {}
              then:
                - op: sub
                  path: /available_balance
                  value: event.amount
                  cap:
                    min: 0
                - op: add
                  path: /held_balance
                  value: event.amount
        apply:
          rules:
            - name: debit_sender
              when: {}
              then:
                - op: sub
                  path: /held_balance
                  value: event.amount
                - op: sub
                  path: /total_balance
                  value: event.amount
        abort_cleanup:
          rules:
            - name: release_sender_hold
              when: {}
              then:
                - op: sub
                  path: /held_balance
                  value: event.amount
                  cap:
                    min: 0
                - op: add
                  path: /available_balance
                  value: event.amount
 
      - name: receiver
        state: wallet
        entity_expr: event.receiver_id
        delta_expr: "event.amount"
        apply:
          rules:
            - name: credit_receiver
              when: {}
              then:
                - op: add
                  path: /available_balance
                  value: event.amount
                - op: add
                  path: /total_balance
                  value: event.amount

Inventory reserve + payment capture

Reserve stock and capture payment atomically. If payment fails, stock is released automatically.

commit_envelopes:
  order_fulfillment:
    start_action: PLACE_ORDER
    envelope_state: order
    envelope_state_path: /fulfillment_state
    lifecycle:
      prepare_event:   ORDER_FULFILLMENT_PREPARED
      prepared_event:  ORDER_FULFILLMENT_READY
      commit_event:    ORDER_FULFILLMENT_COMMITTED
      abort_event:     ORDER_FULFILLMENT_ABORTED
    timeout:
      abort_after_seconds: 60
      tick_event: CLOCK_TICK
    participants:
      - name: inventory
        state: product
        entity_expr: event.product_id
        staging:
          rules:
            - name: reserve_stock
              when: {}
              then:
                - op: sub
                  path: /available_stock
                  value: event.quantity
                  cap:
                    min: 0
                - op: add
                  path: /reserved_stock
                  value: event.quantity
        apply:
          rules:
            - name: confirm_reservation
              when: {}
              then:
                - op: sub
                  path: /reserved_stock
                  value: event.quantity
        abort_cleanup:
          rules:
            - name: release_reservation
              when: {}
              then:
                - op: sub
                  path: /reserved_stock
                  value: event.quantity
                  cap:
                    min: 0
                - op: add
                  path: /available_stock
                  value: event.quantity
 
      - name: payment
        state: customer
        entity_expr: event.customer_id
        staging:
          rules:
            - name: authorize_payment
              when: {}
              then:
                - op: sub
                  path: /wallet/available_balance
                  value: event.total_price
                  cap:
                    min: 0
                - op: add
                  path: /wallet/held_balance
                  value: event.total_price
        apply:
          rules:
            - name: capture_payment
              when: {}
              then:
                - op: sub
                  path: /wallet/held_balance
                  value: event.total_price
        abort_cleanup:
          rules:
            - name: void_authorization
              when: {}
              then:
                - op: sub
                  path: /wallet/held_balance
                  value: event.total_price
                  cap:
                    min: 0
                - op: add
                  path: /wallet/available_balance
                  value: event.total_price