Lesson 4.3.7 showed the strangler fig pattern on a small scale: the appointment planner stayed in place as a facade, the aggregate grew behind it, and characterization tests proved after every step that the behavior had not changed. Lesson 4.6.5 put an anti-corruption layer at the boundary to TED that keeps the foreign model out. This lesson brings both together, on a large scale: a whole legacy system with its own database, a facade that forwards requests, and a move that happens function by function while both systems keep running.
The occasion is invented but typical. The Kladde's IT office has taken over a smaller office. That office's old customer management, the “office database”, keeps running, and other programs there call it over HTTP: a program that prints form letters, and the display of appointments at the front desk. These callers cannot be switched over in a single day. The owner still wants the taken-over office's customers to be in the Kladde soon.
In this lesson you build three small programs in an exercise folder: the legacy system, the Kladde in small, and a facade with YARP. Then you move one function over, reconcile the data and walk the way back. Nothing changes in the Kladde itself; the exercises give you the plan and the tool for the day your office really brings along such a legacy system.
The pattern on a large scale
Microsoft's page on the strangler fig pattern describes four phases:
- A facade, a proxy, stands between the callers, the legacy system and the new system. At first it forwards almost everything to the legacy system.
- Step by step it forwards more requests to the new system, which takes over one more function with every step.
- When nothing depends on the legacy system any more, it is switched off. The facade forwards everything to the new system.
- The facade is removed, and the callers talk directly to the new system. Microsoft also allows keeping the facade as an adapter for old callers.
The callers notice nothing: they use the same interface as before. Microsoft names a series of points that count in a large move:
- Shared data and services. If both systems need the same data, both must be able to reach it during the move.
- Dependencies in both directions. The new system calls functions that are still in the old one, and the old one calls functions that have already moved. An anti-corruption layer belongs in between, so that the new system does not adopt the old one's terms.
- The facade keeps pace with the move, and it must be neither a single point of failure nor a bottleneck.
- A transitional architecture. Facade, reconciliation and doubled contracts cost operations effort while the move is running. That is the price for the lower risk.
- A legacy system that cannot be changed sometimes gets its changes noticed through the database, for example through change data capture.
The pattern is unsuitable if the requests to the legacy system cannot be intercepted, if there is neither source code nor another point of attack such as the database, if the system is small and quickly replaced, or if the old one has to be gone very quickly.
YARP as the facade: how Microsoft describes it
For moving a large application from ASP.NET to ASP.NET Core, Microsoft recommends exactly this pattern. The overview page on migration says so explicitly: incremental migration is an implementation of the strangler fig pattern, meant for large applications that have to stay in operation during the move. The first step is a new ASP.NET Core application next to the old one that forwards to the old one with YARP (Yet Another Reverse Proxy). A route the new application knows itself, it answers itself; all other requests go to the old application over a fallback route. After that you clean up technical debt, settle cross-cutting concerns such as sign-in and sessions between the two applications, and only then raise libraries, and only those the routes being moved need, from the bottom up through the dependency tree.
There, the new application itself is the facade. In this lesson the facade stands on its own, as a small program of its own in front of two applications: the Kladde is already a finished application, and which function goes where should stand in a configuration, not in the code of either system. Both forms are the same pattern.
YARP is a library for ASP.NET Core; the package is called Yarp.ReverseProxy. Version 2.3.0 supports .NET 8 and later. A facade consists of
routes and clusters:
- A route has a name, a condition (
Match: a path or host names, plus methods, headers and query parameters if you like) and the name of a cluster. - A cluster has one or more destinations (
Destinations) with their addresses. - If several routes match, the most specific one wins, by the routing rules of ASP.NET Core;
Ordersets the sequence, smaller values first. - If YARP reads its configuration from a file, it picks up changes without a restart. Only changed routes and clusters are replaced, all at once, and only for new requests; requests already running are not affected. If the new configuration is faulty, YARP logs the error and keeps the last valid one.
The last point turns a move into a change to a file, which can be reviewed in one pull request and rolled back in a second.