← Back to Pillars
Pillar 04 Migration

Wrap Legacy Systems

Integrate Keycloak, Auth0, or your own auth server without rewriting. Replace a single plugin — downstream services work automatically. Modernize incrementally without migration.

Every organization has legacy systems. The mainframe that processes payroll. The SOAP API from 2008. The internal tool someone built in PHP a decade ago. These systems work. Replacing them is expensive, risky, and often unnecessary. What if you could wrap them instead?

The Migration Trap

Traditional modernization follows a predictable pattern:

  1. Decide the legacy system must be replaced
  2. Spec out a new system with feature parity (plus improvements)
  3. Build the new system while maintaining the old one
  4. Migrate data from old to new
  5. Coordinate a cutover date
  6. Discover edge cases the new system doesn't handle
  7. Run both systems in parallel far longer than planned
  8. Finally decommission the old system (maybe)

This pattern takes years, costs millions, and often fails. The Strangler Fig Pattern offers an alternative: wrap the legacy system, expose it through modern interfaces, and incrementally replace pieces while maintaining full functionality.

Real-World Use Cases

Authentication Integration

Your enterprise uses Keycloak for SSO. Your new application needs authentication. Instead of building custom Keycloak integration code, you wrap Keycloak as a service — exposing its functionality through your unified API contract. Downstream services see a standard auth interface; the Keycloak-specific details are hidden.

Database Modernization

A 20-year-old Oracle database contains critical business data. You can't migrate it — too much risk, too many undocumented stored procedures. Wrap it: expose its data through a modern API, implement caching for performance, add validation for data quality. New applications use the wrapper; legacy applications continue unchanged.

Third-Party API Adaptation

You're integrating with a payment processor that has a quirky API — unusual authentication, inconsistent error codes, XML responses. Wrap it: normalize the interface, standardize errors, convert to JSON. Your application code sees a clean, consistent API. When you switch payment processors, only the wrapper changes.

Incremental Replacement

Your monolith's user management is entangled with everything. Extract it as a wrapped service first — same database, same logic, but behind an API contract. Now you can replace the implementation piece by piece while the contract stays stable. Consumers don't know or care when the internals change.

How Existing Tools Approach This

Kong Gateway

API gateway that can wrap legacy services with modern API management — rate limiting, authentication, monitoring. Focuses on infrastructure concerns but doesn't help with adapting the API contract itself.

MuleSoft

Enterprise integration platform that excels at connecting legacy systems. Visual flow builders, extensive connectors, data transformation. Powerful but complex; often requires dedicated MuleSoft developers.

Apache Camel

Integration framework with hundreds of connectors for legacy protocols (JMS, SOAP, file-based). Routing and transformation via DSL. Java-centric; steep learning curve for non-Java shops.

AWS AppFlow

Managed service for SaaS integrations. Configure data flows between systems without code. Limited to supported connectors; custom legacy systems often need custom solutions.

Workato / Tray.io

iPaaS platforms for connecting systems through visual workflows. Pre-built connectors for common systems; custom connectors for legacy. Good for operations; less suited for developer-centric API composition.

GraphQL as a Unifying Layer

GraphQL can wrap multiple REST APIs, databases, and legacy systems behind a unified schema. Apollo Server, Hasura, and similar tools make this pattern accessible. Adds complexity; requires GraphQL expertise.

How It Works

In an Application Operating System, wrapping legacy systems follows a configuration pattern. Define schemas that normalize the external API's data structures:

// intdb-sharepoint/config.json — Wrapping Microsoft SharePoint Graph API
{
  "databases": {
    "intdb_sharepoint": {
      "tables": {
        "sites": { "$aHCoreTables": "jsen-sym-schema-for-sites" },
        "drives": { "$aHCoreTables": "jsen-sym-schema-for-drives" },
        "items": { "$aHCoreTables": "jsen-sym-schema-for-items" },
        "lists": { "$aHCoreTables": "jsen-sym-schema-for-lists" }
      }
    }
  },
  "routes": {
    "$db:list.GET:/intdb_sharepoint.sites": {
      "path": ["plg-list-get-sites"],  // Plugin calls SharePoint Graph API
      "expects": {
        "kSType": "J",
        "kJKeys": {
          "access_token": { "kSType": "S", "kSRequired": "1" }
        }
      },
      "returns": {
        "kSType": "A",
        "kJKeys": { "$aHCoreTables": "jsen-sym-schema-for-sites" }
      }
    },
    "$db:list.GET:/intdb_sharepoint.sites,intdb_sharepoint.drives": {
      "path": ["plg-list-get-drives"],  // Nested traversal: sites → drives
      "expects": { ... },
      "returns": { ... }
    },
    "$db:list.GET:/intdb_sharepoint.sites,intdb_sharepoint.drives,intdb_sharepoint.items": {
      "path": ["plg-list-get-items"],   // Deep traversal: sites → drives → items
      "expects": { ... },
      "returns": { ... }
    }
  }
}

Each plugin handles the actual Microsoft Graph API calls — authentication, pagination, error translation. Consumers see a normalized interface with typed schemas. They query /intdb_sharepoint.sites without knowing SharePoint is underneath.

Replace SharePoint with Google Drive? Write new plugins implementing the same routes. The schemas stay identical. Downstream services work automatically.

"Integrate SharePoint, Google Drive, or your own file storage without rewriting it. Replace a single plugin — downstream services work automatically with the wrapped data."

Contract Preservation

The key to successful wrapping is contract preservation. The legacy system has implicit contracts — data formats, error codes, timing expectations, side effects. A good wrapper:

  • Documents the implicit contracts that exist
  • Normalizes them into explicit, typed contracts
  • Validates that the wrapper preserves behavior
  • Monitors for contract violations at runtime

When contracts are explicit and tested, you can safely modify the wrapper's internals — optimize performance, add caching, replace implementations — without breaking consumers.

Incremental Modernization

Wrapping enables a powerful modernization strategy:

Phase 1: Wrap

Put the legacy system behind a modern API contract. No functionality changes. The wrapper is a thin translation layer.

Phase 2: Observe

Monitor the wrapper. Understand actual usage patterns. Identify which features are critical, which are unused, which cause problems.

Phase 3: Optimize

Add caching, improve error handling, enhance monitoring. The wrapper becomes smarter while the legacy system stays stable.

Phase 4: Replace Incrementally

Reimplement features one at a time. Each new implementation is A/B tested against the legacy wrapper. When confidence is high, the wrapper delegates to the new implementation. The legacy system handles less and less.

Phase 5: Decommission

Eventually, the wrapper delegates everything to new implementations. The legacy system can be shut down. No big bang migration. No cutover date. Gradual, validated transition.

Why This Matters

Legacy systems represent decades of accumulated business logic, edge case handling, and institutional knowledge. Rewriting loses all of that. Wrapping preserves it while enabling modernization.

The real world doesn't allow greenfield development. Every organization has technical debt, legacy systems, and constraints. An application operating system embraces this reality — providing first-class support for wrapping, adapting, and incrementally replacing systems over time.