DEV Community

Cover image for Contract-First Engineering in Distributed Core Banking
mountek
mountek

Posted on

Contract-First Engineering in Distributed Core Banking

In enterprise core banking modernizations, microservice decomposition often creates an unexpected governance failure: Semantic Drift. When dozens of distributed product teams build microservices independently, subtle discrepancies emerge between architectural specifications and real-world implementations. A field defined as ISO4217 currency string in a central domain model might be implemented as a raw string in one service, a numeric currency code in another, or omitted entirely in an asynchronous event payload.

Relying on a "Code-First" development approach—where engineers write Java controllers or Kafka producers first and auto-generate API documentation after the fact—reverses domain governance. Code becomes the de facto truth, documentation lags behind reality, breaking changes are discovered late in integration testing, and BIAN (Banking Industry Architecture Network) service boundaries erode.

Under the Xenon Architecture Standards, modern core banking platforms eliminate semantic drift by enforcing Contract-First Engineering. Machine-readable OpenAPI 3.1 (for synchronous REST) and AsyncAPI 3.0 (for event-driven streams) specifications serve as the single, immutable source of truth. Automated build pipelines generate non-modifiable domain interfaces, Data Transfer Objects (DTOs), and event schemas directly from these specs during compilation—ensuring runtime fidelity across all engineering teams.

💡 Explore the Xenon Architecture Standard For comprehensive architectural blueprints, BIAN service domain mappings, and dual-orchestration integration patterns, visit the official Xenon Architecture Guide. To inspect reference code, infrastructure templates, and open-source banking modules, explore the VecPay-Tech GitHub Organization.

BIAN Service Domain Contract Taxonomy

In a BIAN-compliant architecture, interaction paradigms dictate contract specifications. Synchronous service operations map to OpenAPI, while state-change events published across event buses map to AsyncAPI.

BIAN Service Domain Interaction Paradigm Primary Contract Standard Scaffolding Output Enforcing Tooling
Payment Execution Synchronous Request/Response OpenAPI 3.1 Spring HTTP Interfaces / DTOs Spectral + OpenAPI Generator
Position Keeping Event Stream Publishing AsyncAPI 3.0 Jackson Java Records / Kafka Listeners AsyncAPI CLI + Confluent Schema Registry
Credit Assessment Synchronous RPC / gRPC Protobuf 3 / OpenAPI gRPC Stubs / Protobuf Models protoc + Buf CLI
Customer Onboarding Hybrid (REST + Events) OpenAPI + AsyncAPI REST Interfaces + Event DTOs Combined Maven Toolchain

Mathematical Model: Quantifying Semantic Drift Risk

When microservice contracts are allowed to drift independently, the probability of a breaking integration failure increases exponentially with the number of interconnected service dependencies.

The probability of zero semantic failure Psystem-integrityP_{\text{system-integrity}} across a call chain of NN microservice interfaces, each having an un-gated semantic drift probability of Δdrift-i\Delta_{\text{drift-i}} , is defined as:

Psystem-integrity=∏i=1N(1−Δdrift-i) P_{\text{system-integrity}} = \prod_{i=1}^{N} \bigl( 1 - \Delta_{\text{drift-i}} \bigr)

In a code-first architecture where Δdrift-i≈0.05\Delta_{\text{drift-i}} \approx 0.05 (due to manual DTO mapping, untracked field renames, or missing validation rules), a transaction spanning 10 microservices yields:

Psystem-integrity=(1−0.05)10≈0.598(59.8 P_{\text{system-integrity}} = (1 - 0.05)^{10} \approx 0.598 \quad (59.8% \text{ System Reliability})

Contract-First Engineering reduces Δdrift-i→0\Delta_{\text{drift-i}} \to 0 at compile time by turning schema mismatches into hard compilation errors.


Contract-First Pipeline Topology

The Xenon Architecture mandates that contract repositories exist independently of microservice source code. Specs are versioned, linted for BIAN standards, and compiled into versioned SDK artifacts before application code is compiled.

                         CONTRACT-FIRST DOMAIN SCAFFOLDING TOPOLOGY

  +-----------------------+      1. Git Push Spec      +------------------------+
  | Central BIAN Schema   | -------------------------> | Contract CI/CD         |
  | Repository (Git)      |                            | (GitHub Actions)       |
  +-----------------------+                            +-----------+------------+
                                                                   |
                                       2. Lint (Spectral)          |
                                       3. Verify Diff (Buf/OAS)    v
  +-----------------------+      4. Publish Generated  +------------------------+
  | Enterprise Artifact   | <------------------------- | Compiler Tools         |
  | Registry (Nexus/Art)  |     SDK Jars / Packages    | (OpenAPI / AsyncAPI)   |
  +-----------+-----------+                            +------------------------+
              |
              | 5. Pull Versioned Domain Interfaces
              v
  +-----------------------+
  | Core Microservice     |
  | (Implements Interface)|
  +-----------------------+

Enter fullscreen mode Exit fullscreen mode

Dual Contract Specifications: OpenAPI 3.1 & AsyncAPI 3.0

1. Synchronous REST Contract: OpenAPI 3.1 (payment-execution-v1.yaml)

This specification defines the synchronous API surface for initiating a payment within the BIAN Payment Execution domain, referencing shared enterprise schema components.

openapi: 3.1.0
info:
  title: BIAN Payment Execution Service Domain
  version: 1.4.0
  description: Synchronous API contract for processing credit transfers under Xenon Core Banking Standards.
paths:
  /v1/payments/execution:
    post:
      summary: Initiate Payment Transfer
      operationId: initiatePaymentExecution
      parameters:
        - name: X-Correlation-ID
          in: header
          required: true
          schema:
            type: string
            format: uuid
            example: "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PaymentExecutionInitiation'
      responses:
        '202':
          description: Payment Accepted for Processing
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentExecutionResponse'
        '400':
          description: Invalid Request Payload or Domain Constraint Violation

components:
  schemas:
    PaymentExecutionInitiation:
      type: object
      required:
        - sourceAccountId
        - targetAccountId
        - amount
        - currency
      properties:
        sourceAccountId:
          type: string
          pattern: '^[A-Z0-9]{10,34}$'
          example: "GB29XENON12345678901234"
        targetAccountId:
          type: string
          pattern: '^[A-Z0-9]{10,34}$'
          example: "DE89XENON98765432109876"
        amount:
          type: number
          format: double
          minimum: 0.01
          example: 1250.50
        currency:
          type: string
          pattern: '^[A-Z]{3}$'
          example: "EUR"

    PaymentExecutionResponse:
      type: object
      required:
        - paymentId
        - status
        - timestamp
      properties:
        paymentId:
          type: string
          format: uuid
        status:
          type: string
          enum: [PENDING, EXECUTED, REJECTED]
        timestamp:
          type: string
          format: date-time

Enter fullscreen mode Exit fullscreen mode

2. Asynchronous Event Stream Contract: AsyncAPI 3.0 (payment-events-v1.yaml)

This specification defines the asynchronous event emitted to Apache Kafka when a payment completes, consumed by downstream domains like Position Keeping and Fraud Evaluation.

asyncapi: 3.0.0
info:
  title: BIAN Payment Execution Event Stream
  version: 1.4.0
  description: Event channel contracts for payment lifecycle state changes.

channels:
  paymentExecutedChannel:
    address: core.bian.payment-execution.events.v1
    messages:
      paymentExecutedMessage:
        $ref: '#/components/messages/PaymentExecutedMessage'

operations:
  publishPaymentExecuted:
    action: send
    channel:
      $ref: '#/channels/paymentExecutedChannel'
    summary: Emitted when a payment successfully executes.

components:
  messages:
    PaymentExecutedMessage:
      name: PaymentExecutedEvent
      title: Payment Executed Notification
      contentType: application/json
      headers:
        type: object
        properties:
          correlationId:
            type: string
            format: uuid
      payload:
        $ref: '#/components/schemas/PaymentExecutedPayload'

  schemas:
    PaymentExecutedPayload:
      type: object
      required:
        - paymentId
        - sourceAccountId
        - targetAccountId
        - amount
        - currency
        - executedAt
      properties:
        paymentId:
          type: string
          format: uuid
        sourceAccountId:
          type: string
        targetAccountId:
          type: string
        amount:
          type: number
          format: double
        currency:
          type: string
        executedAt:
          type: string
          format: date-time

Enter fullscreen mode Exit fullscreen mode

Automated Scaffolding & Build Toolchain Integration

To prevent manual DTO creation, code generation plugins execute during Maven’s generate-sources phase. Handcrafted DTOs and Controllers are strictly forbidden in pull requests.

1. Maven Code Scaffolding Configuration (pom.xml)

<plugin>
    <groupId>org.openapitools</groupId>
    <artifactId>openapi-generator-maven-plugin</artifactId>
    <version>7.4.0</version>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <inputSpec>${project.basedir}/src/main/resources/contracts/payment-execution-v1.yaml</inputSpec>
                <generatorName>spring</generatorName>
                <apiPackage>com.xenon.banking.payment.api</apiPackage>
                <modelPackage>com.xenon.banking.payment.model</modelPackage>
                <configOptions>
                    <interfaceOnly>true</interfaceOnly>
                    <skipDefaultInterface>true</skipDefaultInterface>
                    <useSpringBoot3>true</useSpringBoot3>
                    <useOptional>false</useOptional>
                    <unhandledException>true</unhandledException>
                    <useEnumCaseInsensitive>true</useEnumCaseInsensitive>
                    <openApiNullable>false</openApiNullable>
                </configOptions>
            </configuration>
        </execution>
    </executions>
</plugin>

Enter fullscreen mode Exit fullscreen mode

2. Microservice Business Logic Implementation (Java 21)

The developer implements the generated, non-modifiable interface (PaymentExecutionApi). If the spec changes upstream, the application fails to compile locally until the implementation matches the updated contract.

package com.xenon.banking.payment.controller;

import com.xenon.banking.payment.api.PaymentExecutionApi;
import com.xenon.banking.payment.model.PaymentExecutionInitiation;
import com.xenon.banking.payment.model.PaymentExecutionResponse;
import com.xenon.banking.payment.service.PaymentExecutionDomainService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

@RestController
public class PaymentExecutionController implements PaymentExecutionApi {

    private final PaymentExecutionDomainService domainService;

    public PaymentExecutionController(PaymentExecutionDomainService domainService) {
        this.domainService = domainService;
    }

    @Override
    public ResponseEntity<PaymentExecutionResponse> initiatePaymentExecution(
            UUID xCorrelationID,
            PaymentExecutionInitiation initiationRequest) {

        // Domain logic handles pure business processing
        PaymentExecutionResponse response = domainService.processTransfer(
                xCorrelationID,
                initiationRequest.getSourceAccountId(),
                initiationRequest.getTargetAccountId(),
                initiationRequest.getAmount(),
                initiationRequest.getCurrency()
        );

        return ResponseEntity.status(HttpStatus.ACCEPTED).body(response);
    }
}

Enter fullscreen mode Exit fullscreen mode

Contract Governance & Breaking Change Enforcement

Contract-First Engineering requires automated linting and breaking-change detection in the CI/CD pipeline before specs are published to the central schema registry.

1. Spectral Linting Rules for BIAN Compliance (.spectral.yaml)

Spectral enforces architectural standards across all OpenAPI specs (e.g., mandatory correlation headers, strict ISO currency patterns, camelCase properties).

extends: ["spectral:oas"]

rules:
  bian-correlation-id-header:
    description: "All POST and PUT operations must accept an X-Correlation-ID UUID header."
    recommended: true
    given: "$.paths..[post,put].parameters[?(@.name === 'X-Correlation-ID')]"
    then:
      field: "in"
      function: pattern
      functionOptions:
        match: "header"

  bian-property-camel-case:
    description: "Property names must adhere to strict camelCase formatting."
    severity: error
    given: "$.components.schemas..properties"
    then:
      field: "@key"
      function: casing
      functionOptions:
        type: camel

  bian-no-raw-strings-for-currency:
    description: "Currency properties must declare a 3-character uppercase ISO4217 pattern."
    severity: error
    given: "$.components.schemas..properties[?(@.name === 'currency')]"
    then:
      field: "pattern"
      function: defined

Enter fullscreen mode Exit fullscreen mode

2. Automated Breaking Change Validation Pipeline

This GitHub Actions workflow validates incoming contract changes against current production specifications using openapi-diff. If a breaking change (such as deleting a field, altering an enum, or adding a mandatory request parameter) is detected without a major version bump, the build is blocked.

name: Contract Governance and Backward Compatibility Gate

on:
  pull_request:
    paths:
      - 'contracts/**.yaml'

jobs:
  validate-contract:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout PR Branch Specs
        uses: actions/checkout@v4

      - name: Setup Node.js for Spectral Linting
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install Spectral Linter
        run: npm install -g @stoplight/spectral-cli

      - name: Lint Specs Against BIAN Governance Rules
        run: |
          spectral lint contracts/*.yaml --ruleset .spectral.yaml

      - name: Checkout Base Branch (Production Spec Snapshot)
        uses: actions/checkout@v4
        with:
          ref: ${{ github.base_ref }}
          path: base-contracts

      - name: Verify Backward Compatibility (OpenAPI Diff)
        uses: docker://openapitools/openapi-diff:latest
        with:
          args: --fail-on-incompatible base-contracts/contracts/payment-execution-v1.yaml contracts/payment-execution-v1.yaml

Enter fullscreen mode Exit fullscreen mode

Edge Cases and Operational Governance

1. Managing Large Schema Registries with Monorepos

In systems spanning hundreds of BIAN Service Domains, storing contract files inside individual service microservice repositories leads to circular dependency hell.

Contracts must be maintained inside a single Central Schema Monorepo. Changes to schemas trigger automated PR builds that generate versioned language SDKs (Java JARs, TypeScript packages, Go modules) and publish them to an enterprise artifact repository (Nexus/Artifactory). Microservices consume these generated SDKs as standard versioned library dependencies.

2. Handling Legacy System Adapters

Legacy mainframe core banking systems rarely speak native OpenAPI 3.1 or AsyncAPI 3.0.

To prevent legacy data types (e.g., fixed-width EBCDIC records or COBOL copybooks) from leaking into modern domain boundaries, Anti-Corruption Layer (ACL) microservices are deployed. The ACL service implements the modern BIAN Contract-First interface on the front, mapping requests to legacy formats via compiled transformation wrappers inside isolated infrastructure code.


Contract-First Engineering transforms system integration from an error-prone, manual effort into an automated compile-time guarantee. By driving code generation directly from version-controlled OpenAPI 3.1 and AsyncAPI 3.0 specs, modern banking architectures eliminate semantic drift, enforce strict BIAN domain boundaries, and guarantee seamless interoperability across distributed engineering teams.

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.