<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bob Palermo</title>
    <description>The latest articles on DEV Community by Bob Palermo (@bpalermo11).</description>
    <link>https://dev.to/bpalermo11</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4158503%2F5e3045cc-2a6e-42d5-b872-ad8dc17d3448.png</url>
      <title>DEV Community: Bob Palermo</title>
      <link>https://dev.to/bpalermo11</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bpalermo11"/>
    <language>en</language>
    <item>
      <title>The First Seam</title>
      <dc:creator>Bob Palermo</dc:creator>
      <pubDate>Thu, 29 Oct 2026 13:53:56 +0000</pubDate>
      <link>https://dev.to/bpalermo11/the-first-seam-56p0</link>
      <guid>https://dev.to/bpalermo11/the-first-seam-56p0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Identifying Fulfillment as a boundary is only the first step. Now ZibiWorks needs a safe place to separate it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article is part of ZibiWorks, an ongoing fictional modernization case study. The ZibiWorks site is the canonical home of the series, with the full company context, starting architecture, and related installments. Explore the full series at &lt;a href="https://bobpalermo.github.io/zibiworks/" rel="noopener noreferrer"&gt;ZibiWorks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://bobpalermo.github.io/zibiworks/story/03-rewrite-vs-evolve/" rel="noopener noreferrer"&gt;Installment 03: Rewrite vs. Evolve&lt;/a&gt;, ZibiWorks chose evolutionary modernization over a rewrite. In &lt;a href="https://bobpalermo.github.io/zibiworks/story/04-boundaries-and-meaning/" rel="noopener noreferrer"&gt;Installment 04: Boundaries and Meaning&lt;/a&gt;, we identified the business boundaries that should guide that evolution.&lt;/p&gt;

&lt;p&gt;Now we need somewhere to act on those decisions.&lt;/p&gt;

&lt;p&gt;Fulfillment is still embedded in the monolith, tightly coupled to ordering and inventory. We know &lt;em&gt;what&lt;/em&gt; should be separated. The question is &lt;em&gt;where and how&lt;/em&gt; to begin separating it safely.&lt;/p&gt;

&lt;p&gt;That place is a seam.&lt;/p&gt;

&lt;h2&gt;
  
  
  From boundary to seam
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The business boundary establishes what must be separated. The technical seam establishes where and how.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Installment 04 did the first half, establishing Fulfillment as a real business capability with its own responsibilities, distinct from Inventory and Order Management. This installment does the second: finding a place where that boundary can be acted on in code without forcing everything around it to change at once.&lt;/p&gt;

&lt;p&gt;A seam is a controlled point of substitution, where one implementation can replace another without requiring changes to the consuming business logic.&lt;/p&gt;

&lt;p&gt;In Installment 03, ZibiWorks chose to evolve the monolith incrementally rather than replace it wholesale. That approach follows the &lt;a href="https://bobpalermo.github.io/zibiworks/reference/glossary/#strangler-fig" rel="noopener noreferrer"&gt;Strangler Fig&lt;/a&gt; pattern: gradually moving responsibilities out of the monolith while the existing application continues to operate.&lt;/p&gt;

&lt;p&gt;But to redirect a responsibility, we first need a place where that redirection can happen. Fulfillment doesn't have one. Its logic is still tangled into Ordering, and introducing that substitution point is the work of this installment.&lt;/p&gt;

&lt;p&gt;The challenge is that not every technically substitutable point is a good place to make the cut.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Fulfillment lives today
&lt;/h2&gt;

&lt;p&gt;Right now, ordering reaches straight past the boundaries Installment 04 identified. When an order is submitted, the same code path reserves inventory and creates the shipment request itself, inline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;InventoryRepository&lt;/span&gt; &lt;span class="n"&gt;_inventory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ShipmentRepository&lt;/span&gt; &lt;span class="n"&gt;_shipments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SubmitOrderAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ... order validation, pricing, persistence ...&lt;/span&gt;

        &lt;span class="c1"&gt;// Fulfillment, done inline:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_inventory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReserveAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_shipments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateFulfillmentRequestAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// ... confirmation, events, the rest of ordering ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;OrderService&lt;/code&gt; does not just ask for fulfillment to happen. It knows &lt;em&gt;how&lt;/em&gt; fulfillment happens: that an inventory reservation comes first, then a shipment request, and in that order. Those two calls do not even belong to the same context. Reserving stock is Inventory's responsibility; arranging a shipment is work Fulfillment coordinates. Ordering reaches into both directly. There is no seam here, and there is no single place to substitute a new Fulfillment without editing ordering itself. That coupling is the thing standing between ZibiWorks and its first cut.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not every cut is a good seam
&lt;/h2&gt;

&lt;p&gt;There are several places ZibiWorks could technically insert a substitution point around this code. They are not equally useful, and the differences are the whole architectural question.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Repository-level substitution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Put interfaces in front of &lt;code&gt;InventoryRepository&lt;/code&gt; and &lt;code&gt;ShipmentRepository&lt;/code&gt;. The calls are now swappable, but ordering still orchestrates fulfillment step by step and still reaches into Inventory itself. The seam exposes Fulfillment's mechanics and preserves the existing coupling. We have made the wrong thing replaceable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Legacy-shaped facade&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Move the existing logic behind a single facade, but expose a contract that mirrors its internal steps: &lt;code&gt;ReserveInventory&lt;/code&gt; then &lt;code&gt;CreateShipment&lt;/code&gt;. Ordering no longer calls the repositories directly, but it still knows how fulfillment works and in what sequence. The code has moved; the coupling has not. Replacing the legacy implementation would still mean preserving that sequence or changing the callers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Business-intent contract&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Expose what ordering actually needs: &lt;code&gt;RequestFulfillment&lt;/code&gt;. Ordering states its intent and says nothing about how inventory or shipment are handled. The contract describes the capability, so any implementation, legacy or extracted, is free to satisfy it differently.&lt;/p&gt;

&lt;p&gt;The difference between the second and third is not the facade; a facade can express intent perfectly well. The difference is what the contract is shaped around. A legacy-shaped contract describes the steps the old code takes. A business-intent contract describes what ordering wants to be true. Only the latter gives Fulfillment room to change behind it, which is the point of drawing the boundary in the first place.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A good seam expresses business intent, not legacy implementation mechanics.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An interface can enforce a technical separation, but it doesn't establish where the business boundary belongs. That decision comes from understanding the responsibilities on either side. The seam is how ZibiWorks acts on a boundary it has already reasoned about, so the contract has to reflect that boundary rather than the legacy call sequence.&lt;/p&gt;

&lt;h3&gt;
  
  
  What makes a seam worth cutting
&lt;/h3&gt;

&lt;p&gt;Business intent is the primary test, but a few other principles decide whether a candidate seam is worth committing to.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criterion&lt;/th&gt;
&lt;th&gt;What to look for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Business alignment&lt;/td&gt;
&lt;td&gt;Represents a meaningful business capability, not an incidental grouping of code.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Narrow surface&lt;/td&gt;
&lt;td&gt;Few callers need to understand or participate in the transition.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stable contract&lt;/td&gt;
&lt;td&gt;Remains meaningful while implementations change.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Substitutability&lt;/td&gt;
&lt;td&gt;One implementation can replace another without changing consuming business logic.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model independence&lt;/td&gt;
&lt;td&gt;The contract carries only the data the capability needs without exposing the caller's domain model.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;RequestFulfillment&lt;/code&gt; satisfies these criteria. The repository-level alternatives may be technically substitutable, but they preserve the wrong responsibilities and dependencies. The difference isn't whether an interface exists. It's whether that interface establishes a useful boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the seam
&lt;/h2&gt;

&lt;p&gt;With the seam identified, the next decision is what crosses it. Ordering needs to request fulfillment, but Fulfillment shouldn't need to understand Ordering's entire domain model.&lt;/p&gt;

&lt;p&gt;That means defining a contract around the business intent and a request containing only the information Fulfillment needs. Once those decisions are made, &lt;a href="https://bobpalermo.github.io/zibiworks/reference/glossary/#dependency-injection-di" rel="noopener noreferrer"&gt;dependency injection (DI)&lt;/a&gt; provides the mechanism for selecting the implementation.&lt;/p&gt;

&lt;p&gt;Ordering depends on a contract that states intent, and nothing more. Note what it does &lt;em&gt;not&lt;/em&gt; pass: the &lt;code&gt;Order&lt;/code&gt; itself. It hands Fulfillment a request carrying only what Fulfillment needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IFulfillmentService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;RequestFulfillmentAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FulfillmentRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IFulfillmentService&lt;/span&gt; &lt;span class="n"&gt;_fulfillment&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IFulfillmentService&lt;/span&gt; &lt;span class="n"&gt;fulfillment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;_fulfillment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fulfillment&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SubmitOrderAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ... order responsibilities ...&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FulfillmentRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;From&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_fulfillment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RequestFulfillmentAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// ... remaining order responsibilities ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That request is deliberately small: the items and quantities to fulfill, a destination, an order reference. Not the &lt;code&gt;Order&lt;/code&gt; &lt;a href="https://bobpalermo.github.io/zibiworks/reference/glossary/#aggregate" rel="noopener noreferrer"&gt;aggregate&lt;/a&gt; with its pricing, payment, and customer detail.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A seam can hide implementation mechanics and still leak the caller's domain model. A good contract carries only what the receiving capability needs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Passing &lt;code&gt;Order&lt;/code&gt; across the seam would tie Fulfillment to Ordering's model, so a change to how orders are shaped could ripple into Fulfillment. The seam should decouple both the implementation behavior and the shape of the data that crosses it.&lt;/p&gt;

&lt;p&gt;The contract has two implementations, and they are alternatives, not a chain. Today, dependency injection supplies the legacy one; later it can supply the extracted one. Only one is active for a given configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Today: the existing orchestration, lifted behind the&lt;/span&gt;
&lt;span class="c1"&gt;// seam, still running inside the monolith. Fulfillment&lt;/span&gt;
&lt;span class="c1"&gt;// coordinates the existing process. Inventory retains its&lt;/span&gt;
&lt;span class="c1"&gt;// stock reservation rules, and the existing shipping&lt;/span&gt;
&lt;span class="c1"&gt;// integration remains unchanged. Domain ownership and&lt;/span&gt;
&lt;span class="c1"&gt;// integration boundaries are not redesigned in this step.&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LegacyFulfillmentService&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IFulfillmentService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;InventoryRepository&lt;/span&gt; &lt;span class="n"&gt;_inventory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ShipmentRepository&lt;/span&gt; &lt;span class="n"&gt;_shipments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;LegacyOrderAdapter&lt;/span&gt; &lt;span class="n"&gt;_adapter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;LegacyFulfillmentService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;InventoryRepository&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ShipmentRepository&lt;/span&gt; &lt;span class="n"&gt;shipments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;LegacyOrderAdapter&lt;/span&gt; &lt;span class="n"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_inventory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_shipments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shipments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_adapter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;RequestFulfillmentAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;FulfillmentRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Load the Order the legacy repositories still&lt;/span&gt;
        &lt;span class="c1"&gt;// require, by order reference. The request carries&lt;/span&gt;
        &lt;span class="c1"&gt;// fulfillment intent, not Ordering's whole&lt;/span&gt;
        &lt;span class="c1"&gt;// aggregate, so the adapter resolves the persisted&lt;/span&gt;
        &lt;span class="c1"&gt;// Order here.&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;legacyOrder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_adapter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoadLegacyOrderAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_inventory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReserveAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;legacyOrder&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_shipments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateFulfillmentRequestAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;legacyOrder&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Later: a separately deployed Fulfillment service,&lt;/span&gt;
&lt;span class="c1"&gt;// satisfying the same contract from across a network&lt;/span&gt;
&lt;span class="c1"&gt;// boundary. It owns the Fulfillment responsibilities and&lt;/span&gt;
&lt;span class="c1"&gt;// collaborates with Inventory through a defined contract.&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FulfillmentApiClient&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IFulfillmentService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// POST /fulfillment/requests&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The original repositories expect an &lt;code&gt;Order&lt;/code&gt;, but the new contract accepts a smaller &lt;code&gt;FulfillmentRequest&lt;/code&gt;. The legacy adapter uses the order reference to retrieve the original Order and supply the input those repositories still require. That compatibility logic stays behind the seam and disappears when the legacy implementation is retired.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The legacy implementation adapts to the new contract, not the other way around.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A clean boundary doesn't require the legacy implementation to be clean. It requires the legacy complexity to remain behind the boundary.&lt;/p&gt;

&lt;p&gt;Both implementations must honor the same business contract, including what it means for a fulfillment request to be accepted, even as the execution model changes. During this initial refactoring the underlying business behavior is intended to stay the same: the legacy adapter runs the same inventory and shipment operations against the same data stores, now reached through the new contract.&lt;/p&gt;

&lt;p&gt;Which implementation is live is decided in one place, the &lt;a href="https://bobpalermo.github.io/zibiworks/reference/glossary/#composition-root" rel="noopener noreferrer"&gt;composition root&lt;/a&gt;, by registering the contract against one or the other. Ordering never learns which it got.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fneeio3e2shnly4ge8fzi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fneeio3e2shnly4ge8fzi.png" alt="OrderService depends on IFulfillmentService, which today resolves to LegacyFulfillmentService inside the monolith (loading the existing Order by reference, requesting stock reservation from Inventory, and arranging the shipment) and later to a FulfillmentApiClient calling an extracted Fulfillment service. The composition root configures which implementation is active; it is not a step in the request." width="485" height="817"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;At runtime, Ordering depends on the contract and reaches whichever implementation is wired in: the legacy one today, an extracted service later. The composition root is configuration, not a step in the request, it selects which implementation satisfies the contract. The dotted paths are the future state, not calls the legacy implementation makes.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What the seam settles, and what it doesn't
&lt;/h2&gt;

&lt;p&gt;The seam gives ZibiWorks a controlled way to change how Fulfillment is implemented without changing Ordering. That is meaningful progress, but it is important to distinguish what has been accomplished from what remains unresolved.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Business boundary&lt;/td&gt;
&lt;td&gt;Established&lt;/td&gt;
&lt;td&gt;Fulfillment was identified as a distinct business capability in &lt;a href="https://bobpalermo.github.io/zibiworks/story/04-boundaries-and-meaning/" rel="noopener noreferrer"&gt;Installment 04: Boundaries and Meaning&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technical seam&lt;/td&gt;
&lt;td&gt;Established&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;IFulfillmentService&lt;/code&gt; gives Ordering one stable contract through which to request fulfillment.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation substitution&lt;/td&gt;
&lt;td&gt;Enabled&lt;/td&gt;
&lt;td&gt;The composition root can select the legacy implementation or a future extracted one without changing Ordering.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Business responsibility ownership&lt;/td&gt;
&lt;td&gt;Partially settled&lt;/td&gt;
&lt;td&gt;Fulfillment's boundary is established, but responsibilities involving Inventory and shipping still need refinement.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service extraction&lt;/td&gt;
&lt;td&gt;Not yet&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;LegacyFulfillmentService&lt;/code&gt; still executes inside the monolith.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data ownership&lt;/td&gt;
&lt;td&gt;Not yet&lt;/td&gt;
&lt;td&gt;The shared database is unchanged. An interface does not establish data ownership.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distributed communication and consistency&lt;/td&gt;
&lt;td&gt;Not yet&lt;/td&gt;
&lt;td&gt;Timeouts, retries, idempotency, partial failures, and cross-service coordination remain migration concerns.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cutover and rollback&lt;/td&gt;
&lt;td&gt;Partially enabled&lt;/td&gt;
&lt;td&gt;Switching implementations is localized, but reversing distributed side effects or data changes takes more than switching a DI registration.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The seam makes the application-level substitution decision reversible. It does not automatically make every consequence of extraction reversible. While &lt;code&gt;LegacyFulfillmentService&lt;/code&gt; runs in the monolith against the same tables, pointing Ordering back at it is a single change. Once an extracted service starts writing its own data or causing side effects across a network, rollback stops being a dependency-injection choice and becomes a broader architectural problem of data and consistency. That belongs to &lt;a href="https://bobpalermo.github.io/zibiworks/start-here/the-journey/#part-iii" rel="noopener noreferrer"&gt;later installments of Part III&lt;/a&gt;, not here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Temporary by design
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;LegacyFulfillmentService&lt;/code&gt; and the transition wiring are &lt;a href="https://bobpalermo.github.io/zibiworks/reference/glossary/#migration-scaffolding" rel="noopener noreferrer"&gt;migration scaffolding&lt;/a&gt;. They exist to make the change incremental, not because they belong in the final architecture. As discussed in &lt;a href="https://bobpalermo.github.io/zibiworks/story/03-rewrite-vs-evolve/" rel="noopener noreferrer"&gt;Installment 03: Rewrite vs. Evolve&lt;/a&gt;, temporary code is not waste when it makes a difficult transition safer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this leaves ZibiWorks
&lt;/h2&gt;

&lt;p&gt;ZibiWorks now has a controlled place to begin extracting Fulfillment. Ordering expresses what it needs, the legacy implementation continues to serve that request, and the path to a replacement no longer runs through Ordering's business logic.&lt;/p&gt;

&lt;p&gt;But the seam is only a technical mechanism. It doesn't decide which responsibilities belong to Fulfillment or which should remain elsewhere.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Creating a technical seam does not determine ownership. The business boundary does, and the seam only gives ZibiWorks somewhere to act on it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Next: Domain or Utility?
&lt;/h2&gt;

&lt;p&gt;Inventory reservation illustrates why that distinction matters. Fulfillment needs inventory reserved, but as we established in &lt;a href="https://bobpalermo.github.io/zibiworks/story/04-boundaries-and-meaning/" rel="noopener noreferrer"&gt;Installment 04: Boundaries and Meaning&lt;/a&gt;, Inventory owns the rules governing stock. Fulfillment requests a reservation; it doesn't own Inventory's business logic.&lt;/p&gt;

&lt;p&gt;Shipping presents a different question. Fulfillment needs to arrange delivery through Handle With Care, and potentially other carriers in the future. But does integrating with a carrier belong inside Fulfillment, in a separate business domain, or behind a shared technical capability?&lt;/p&gt;

&lt;p&gt;These aren't simply decisions about where to put code. They are decisions about business responsibility, ownership, and reuse. And that's where another common architectural mistake appears: not everything reusable should become a shared service, and not every external integration deserves its own bounded context.&lt;/p&gt;

&lt;p&gt;The next installment explores how to distinguish business domains from supporting utilities, and why getting that distinction wrong can recreate the coupling we're trying to remove.&lt;/p&gt;




&lt;p&gt;ZibiWorks is fictional. The ideas and opinions expressed throughout the series are my own and do not represent the views, positions, or practices of my employer.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>refactoring</category>
      <category>dotnet</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Boundaries and Meaning</title>
      <dc:creator>Bob Palermo</dc:creator>
      <pubDate>Wed, 21 Oct 2026 16:29:43 +0000</pubDate>
      <link>https://dev.to/bpalermo11/boundaries-and-meaning-3lg7</link>
      <guid>https://dev.to/bpalermo11/boundaries-and-meaning-3lg7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Before separating the software, ZibiWorks needs to understand the business boundaries hidden inside its monolith.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article is part of ZibiWorks, an ongoing fictional modernization case study. The ZibiWorks site is the canonical home of the series, with the full company context, starting architecture, and related installments. Explore the full series at &lt;a href="https://bobpalermo.github.io/zibiworks/" rel="noopener noreferrer"&gt;ZibiWorks&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  We've chosen Fulfillment. But what is it?
&lt;/h2&gt;

&lt;p&gt;ZibiWorks decided to evolve its platform rather than replace it, and picked&lt;br&gt;
Fulfillment as the first target. The obvious next step is to find the Fulfillment&lt;br&gt;
code and pull it out. But before doing that, the team needs to establish what&lt;br&gt;
actually belongs to Fulfillment.&lt;/p&gt;

&lt;p&gt;In the current application, ordering, inventory, and fulfillment are tightly&lt;br&gt;
woven together. They share classes, business logic, and the same Order model.&lt;br&gt;
Extract everything that touches that model and the move drags along far more than&lt;br&gt;
Fulfillment; extract too little and the new service still depends on the monolith&lt;br&gt;
for decisions it should own. Before moving any code, the team has to understand&lt;br&gt;
how the business actually works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the business, not the database
&lt;/h2&gt;

&lt;p&gt;That understanding comes from the people who run the work, not from reading the&lt;br&gt;
schema. Each business function that participates in an order has its own&lt;br&gt;
perspective on it. ZibiWorks brings together subject matter experts from Order&lt;br&gt;
Management, Inventory, and Fulfillment, along with business analysts and software&lt;br&gt;
architects. Rather than starting with&lt;br&gt;
the existing application, the group walks through several business scenarios: a&lt;br&gt;
straightforward order, a return, a cancellation already in progress, and the&lt;br&gt;
example below. The goal isn't to document every workflow, but to uncover where&lt;br&gt;
responsibilities, decision ownership, and the meaning of common terms begin to&lt;br&gt;
diverge.&lt;/p&gt;

&lt;p&gt;One of those scenarios exposes the problem particularly well. A customer orders&lt;br&gt;
three of the same product. Only two are on hand; the third is out of stock. That&lt;br&gt;
single situation pulls the groups apart. Inventory can reserve two units and has to decide what the third&lt;br&gt;
reservation even means with no stock behind it. Fulfillment can pick and pack the&lt;br&gt;
two available units now, but only if a partial shipment is allowed. Order&lt;br&gt;
Management owns that call, because splitting one order into two deliveries touches&lt;br&gt;
pricing and the customer commitment. Nobody can answer alone, and each is&lt;br&gt;
answering a different part of the question.&lt;/p&gt;

&lt;p&gt;That disagreement is the useful part. The same word starts to mean different&lt;br&gt;
things depending on who is saying it. Take &lt;em&gt;available&lt;/em&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Business perspective&lt;/th&gt;
&lt;th&gt;What "available" means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Order Management&lt;/td&gt;
&lt;td&gt;The customer is allowed to buy it; it is sellable right now&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inventory&lt;/td&gt;
&lt;td&gt;Physical stock is on hand and not already reserved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fulfillment&lt;/td&gt;
&lt;td&gt;The item is somewhere it can actually be picked and packed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;None of these is sloppy or wrong. A product can be available to sell while there&lt;br&gt;
is no stock to reserve, which is precisely how a backorder happens. The word is&lt;br&gt;
only unambiguous inside one context.&lt;/p&gt;

&lt;p&gt;It is tempting to treat these differences as a data problem and add more statuses&lt;br&gt;
or flags to the shared Order model. But that doesn't resolve the underlying issue.&lt;br&gt;
The business functions have different rules and make different decisions. More&lt;br&gt;
fields won't establish who owns those decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Candidate boundaries
&lt;/h2&gt;

&lt;p&gt;Domain-Driven Design gives the team a useful concept: the bounded context. A&lt;br&gt;
bounded context defines where a particular business model, its rules, and its&lt;br&gt;
language have consistent meaning. That is exactly why &lt;em&gt;available&lt;/em&gt; can carry three&lt;br&gt;
different meanings at once without any of them being wrong: each belongs to a&lt;br&gt;
different context.&lt;/p&gt;

&lt;p&gt;The differences in language aren't just vocabulary problems. They point to&lt;br&gt;
different rules, responsibilities, and decisions. So the team draws its candidate&lt;br&gt;
boundaries from those rules, language, and decision ownership, not by copying the&lt;br&gt;
org chart into the architecture.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key Concept&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ubiquitous language needs to be consistent within a bounded context, not across the entire business.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The conversations point to three candidate bounded contexts: Order Management,&lt;br&gt;
Inventory, and Fulfillment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Order Management&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The customer's commercial transaction&lt;/li&gt;
&lt;li&gt;Orders, order lines, pricing, commitments&lt;/li&gt;
&lt;li&gt;Cancellations and the order lifecycle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Inventory&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Availability and commitment of stock&lt;/li&gt;
&lt;li&gt;SKUs, on-hand quantities, reservations&lt;/li&gt;
&lt;li&gt;Replenishment and warehouse locations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fulfillment&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The operational work to satisfy an order&lt;/li&gt;
&lt;li&gt;Pick lists, packing, shipment preparation&lt;/li&gt;
&lt;li&gt;Handoff, dispatch, exceptions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A fourth candidate shows up as the group follows a package out the door. Getting&lt;br&gt;
items picked and packed is Fulfillment's work. Moving the parcel after that,&lt;br&gt;
transportation itself, is a different kind of concern with different experts and&lt;br&gt;
a different model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fulfillment&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Picking and packing items&lt;/li&gt;
&lt;li&gt;Preparing shipments&lt;/li&gt;
&lt;li&gt;Managing fulfillment work and handoff&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Shipping&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Carrier selection and transportation&lt;/li&gt;
&lt;li&gt;Route optimization and delivery exceptions&lt;/li&gt;
&lt;li&gt;Fleet maintenance, where applicable&lt;/li&gt;
&lt;li&gt;Delivery tracking in transit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ZibiWorks does not run trucks. It hands parcels to &lt;strong&gt;Handle With Care&lt;/strong&gt;, an&lt;br&gt;
external carrier, so most of that right-hand list is the carrier's business, not&lt;br&gt;
ZibiWorks'. The open question is whether ZibiWorks has enough Shipping rules and&lt;br&gt;
decisions of its own to justify a separate bounded context, or whether Shipping is&lt;br&gt;
primarily an integration concern. How that integration is implemented is a&lt;br&gt;
separate architectural decision we'll address later. For now, Shipping remains a&lt;br&gt;
candidate worth investigating, and the focus stays on Fulfillment.&lt;/p&gt;

&lt;h2&gt;
  
  
  What belongs to Fulfillment
&lt;/h2&gt;

&lt;p&gt;Working from the conversations rather than the schema, the team sorts&lt;br&gt;
responsibilities by who owns each decision.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;th&gt;Business owner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Capture and manage customer orders&lt;/td&gt;
&lt;td&gt;Order Management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Handle commercial cancellations&lt;/td&gt;
&lt;td&gt;Order Management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintain stock availability&lt;/td&gt;
&lt;td&gt;Inventory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reserve and allocate stock&lt;/td&gt;
&lt;td&gt;Inventory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decide how accepted order lines are fulfilled&lt;/td&gt;
&lt;td&gt;Fulfillment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manage picking and packing&lt;/td&gt;
&lt;td&gt;Fulfillment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prepare shipments and hand off to the carrier&lt;/td&gt;
&lt;td&gt;Fulfillment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Report fulfillment progress&lt;/td&gt;
&lt;td&gt;Fulfillment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Naming an owner for each decision does not remove the need to collaborate.&lt;br&gt;
Cancellation is the clearest case. Order Management owns the decision, but it&lt;br&gt;
cannot make it alone: whether a cancellation is even possible depends on how far&lt;br&gt;
the physical work has gone, and that fact belongs to Fulfillment. Neither context&lt;br&gt;
takes over the other's rules, and the decision still needs both.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F73qdfee4wxuduoq0n5hw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F73qdfee4wxuduoq0n5hw.png" alt="Cancellation sequence: the customer asks Order Management to cancel; Order Management asks Fulfillment whether the order can still be stopped; Fulfillment replies with operational progress and what is reversible; Order Management applies commercial rules and returns the outcome." width="784" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Order Management owns the decision and the commercial rules; Fulfillment owns the operational facts. The diagram is conceptual: it says who decides what, not how the two contexts talk to each other.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;That is the shape of a real boundary: collaboration without shared ownership.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  One order, several lifecycles
&lt;/h2&gt;

&lt;p&gt;The product owner asks the fair question: if Fulfillment separates from Order&lt;br&gt;
Management and Inventory, how does a customer check their order? They do not want&lt;br&gt;
three status screens.&lt;/p&gt;

&lt;p&gt;A coherent customer view does not require one context to own the whole order.&lt;br&gt;
Take the three-item order: Order Management knows it was accepted, Inventory knows&lt;br&gt;
two units are reserved and one is not, Fulfillment knows the available items are&lt;br&gt;
packed and on their way. The customer-facing view can present information from&lt;br&gt;
all three contexts without becoming authoritative for their underlying business&lt;br&gt;
state. Whether ZibiWorks assembles that information on demand or maintains a&lt;br&gt;
separate read model is a decision for a later installment. The read view is&lt;br&gt;
derived; the owning contexts remain the source of truth.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A context owns its data. That doesn't mean it's the only context that needs access to it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Now look at the monolith
&lt;/h2&gt;

&lt;p&gt;The business analysis produced a clean map of responsibilities. The next step is&lt;br&gt;
to hold it up against the existing application and see how much of it the code and&lt;br&gt;
data actually reflect. The answer, in the legacy &lt;code&gt;Orders&lt;/code&gt; table, is not much. A&lt;br&gt;
single row has accumulated fields from several parts of the business at once.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frkl4f7nhcedcu4b4scub.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frkl4f7nhcedcu4b4scub.png" alt="The legacy ORDERS row carries fields owned by Order Management, Inventory, and Fulfillment at once, with two shipping fields still unassigned, surrounded by Customer, Order Lines, Products, Stock, and Warehouses relationships." width="535" height="1119"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The same row holds Order Management, Inventory, and Fulfillment fields, with the two shipping fields still unassigned. The business drew three clean boundaries; the table drew none.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The shape made sense once. ZibiWorks shipped from one warehouse, an order was one&lt;br&gt;
shipment, and the path from placed to shipped was close to linear, so putting it&lt;br&gt;
all on the order record was the simplest thing that worked. Then the business&lt;br&gt;
changed. More warehouses broke the single &lt;code&gt;WarehouseId&lt;/code&gt;. Partial shipments turned&lt;br&gt;
one order into several deliveries. Backorders, specialized carriers, and more&lt;br&gt;
involved inventory commitments each needed somewhere to live, and the fastest&lt;br&gt;
place was another column on a table everything already understood. No single one&lt;br&gt;
of those decisions was wrong; added up, they left the Order model owning work&lt;br&gt;
from three different parts of the business, and a fourth knocking at the door.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The database reflects years of implementation decisions, not necessarily where the business boundaries belong.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Deciding what moves
&lt;/h2&gt;

&lt;p&gt;The business boundaries turn the extraction choice into something the team can&lt;br&gt;
defend rather than guess at.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Candidate bounded context&lt;/th&gt;
&lt;th&gt;Decision&lt;/th&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fulfillment&lt;/td&gt;
&lt;td&gt;Extract&lt;/td&gt;
&lt;td&gt;The scalability and resilience pressures that started the modernization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Order Management&lt;/td&gt;
&lt;td&gt;Remain&lt;/td&gt;
&lt;td&gt;Commercial workflows are stable and were never the problem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inventory&lt;/td&gt;
&lt;td&gt;Remain&lt;/td&gt;
&lt;td&gt;Reach extends well beyond this effort; extracting it now blows up the scope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shipping&lt;/td&gt;
&lt;td&gt;Investigate&lt;/td&gt;
&lt;td&gt;Still an open question, not an extraction commitment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Picking Fulfillment is the easy part. The risk is moving the code without&lt;br&gt;
achieving the separation. Fulfillment still needs stock reserved by Inventory and&lt;br&gt;
order and cancellation decisions from Order Management, and today it reaches all&lt;br&gt;
of that through the shared Order row. Give it its own service but leave those&lt;br&gt;
dependencies in place, and Fulfillment spends its life calling back into the&lt;br&gt;
monolith: slower than the method calls it replaced, still hostage to legacy data&lt;br&gt;
and release schedules, and buying little of the scalability and resilience that&lt;br&gt;
justified the move. Moving the code into its own service doesn't mean you've&lt;br&gt;
removed the coupling. The business decision is settled; making the separation&lt;br&gt;
real is the part still to work out.&lt;/p&gt;

&lt;h2&gt;
  
  
  The boundary before the seam
&lt;/h2&gt;

&lt;p&gt;ZibiWorks now understands its business boundaries, and it knows the&lt;br&gt;
implementation does not honor them. That gap is the real result of this&lt;br&gt;
installment. Knowing what to separate tells ZibiWorks what Fulfillment must stop&lt;br&gt;
sharing, but not where the running system can actually be cut or how Fulfillment&lt;br&gt;
gets what it needs once it is. That is the next problem.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key Takeaway&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The business boundary establishes what must be separated. The technical seam establishes where and how.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;ZibiWorks is fictional. The ideas and opinions expressed throughout the series are my own and do not represent the views, positions, or practices of my employer.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>ddd</category>
      <category>softwaredesign</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Rewrite vs. Evolve</title>
      <dc:creator>Bob Palermo</dc:creator>
      <pubDate>Wed, 14 Oct 2026 12:34:17 +0000</pubDate>
      <link>https://dev.to/bpalermo11/rewrite-vs-evolve-5l7</link>
      <guid>https://dev.to/bpalermo11/rewrite-vs-evolve-5l7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The platform has to change. The question is how: replace it with a clean-sheet build, or evolve the existing system toward the target.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article is part of ZibiWorks, an ongoing fictional modernization case study. The ZibiWorks site is the canonical home of the series, with the full company context, starting architecture, and related installments. Explore the full series at &lt;a href="https://bobpalermo.github.io/zibiworks/" rel="noopener noreferrer"&gt;ZibiWorks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The infrastructure question is settled. ZibiWorks chose to move before&lt;br&gt;
modernizing the application, but that decision left the monolith, the shared&lt;br&gt;
database, and the tangled domains intact. With the migration path established,&lt;br&gt;
attention turns to how the application itself should change.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem forcing the question
&lt;/h2&gt;

&lt;p&gt;A specific business capability is under pressure: &lt;strong&gt;Order Fulfillment.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As ZibiWorks has grown, seasonal demand has grown with it, and Fulfillment&lt;br&gt;
needs to scale more independently than it can today. Because Fulfillment is&lt;br&gt;
embedded in the monolith, adding capacity for it means scaling far more of the&lt;br&gt;
application than necessary, and problems or deployments in unrelated areas can&lt;br&gt;
disrupt fulfillment processing. As order volume climbs, its availability and&lt;br&gt;
resiliency matter more every quarter, and leadership wants this materially&lt;br&gt;
improved before the next major peak season.&lt;/p&gt;

&lt;p&gt;ZibiWorks is not modernizing for cleanliness. It has a business capability&lt;br&gt;
that has to get better on a specific timeline, and that deadline is the single&lt;br&gt;
most important criterion weighing on the decision.&lt;/p&gt;

&lt;p&gt;Before comparing the two paths, one thing is worth settling: this is a decision&lt;br&gt;
about &lt;em&gt;how ZibiWorks travels&lt;/em&gt;, not &lt;em&gt;where it is going&lt;/em&gt;. Whichever path it takes,&lt;br&gt;
good domain analysis should land on substantially the same business&lt;br&gt;
capabilities, bounded contexts, ownership, and data responsibilities. An&lt;br&gt;
evolutionary path may surface new information along the way, so the final&lt;br&gt;
implementation can differ in detail, but the migration mechanism should not&lt;br&gt;
dictate the target decomposition. The destination is not what is in question&lt;br&gt;
here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two credible options
&lt;/h2&gt;

&lt;p&gt;Both are credible. Neither is foolish.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A: Rewrite&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Design and build the new platform alongside the existing system&lt;/li&gt;
&lt;li&gt;Migrate the business onto it&lt;/li&gt;
&lt;li&gt;Retire the old system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Option B: Evolve&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create seams in the existing platform&lt;/li&gt;
&lt;li&gt;Move capabilities into new bounded contexts over time&lt;/li&gt;
&lt;li&gt;Retire legacy implementations as confidence grows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A clean rewrite is genuinely appealing: clean boundaries from day one, a modern&lt;br&gt;
architecture without legacy compromises during construction, freedom to rethink&lt;br&gt;
persistence and integration, and far less transitional scaffolding cluttering&lt;br&gt;
the codebase. Building straight toward the desired end state is a real&lt;br&gt;
advantage, and it deserves a serious evaluation rather than a reflexive "big&lt;br&gt;
rewrites fail."&lt;/p&gt;

&lt;p&gt;Evolution has its own strengths and its own real costs. The two differ on three&lt;br&gt;
things that matter here: where the complexity lands, when the business sees&lt;br&gt;
value, and how data moves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the complexity lands
&lt;/h2&gt;

&lt;p&gt;Neither approach avoids complexity; they place it differently in time. Both&lt;br&gt;
involve substantial construction work. What differs is where the &lt;em&gt;migration,&lt;br&gt;
coexistence, validation, and cutover&lt;/em&gt; complexity falls.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Evolution&lt;/th&gt;
&lt;th&gt;Rewrite&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Migration complexity distributed throughout the transition: temporary adapters, compatibility layers, and synchronization.&lt;/td&gt;
&lt;td&gt;Cleaner construction path, but migration and cutover complexity concentrated near completion.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delivery of value&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fulfillment can improve independently while the rest of the platform evolves.&lt;/td&gt;
&lt;td&gt;Fulfillment improvements depend on enough of the replacement platform being ready.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data migration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Incremental, domain by domain. Ownership and validation move in smaller steps.&lt;/td&gt;
&lt;td&gt;Larger, coordinated migration and reconciliation near cutover.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Evolution distributes migration complexity across the journey. A rewrite concentrates much of it near the destination.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Can a rewrite meet the deadline?
&lt;/h2&gt;

&lt;p&gt;On construction alone, a rewrite might. The deciding question is not whether&lt;br&gt;
ZibiWorks could &lt;em&gt;build&lt;/em&gt; a replacement in time, but what it would take to put one&lt;br&gt;
safely into production before the busiest period of the year.&lt;/p&gt;

&lt;p&gt;A full-platform replacement couples the Fulfillment deadline to the readiness of&lt;br&gt;
the entire new platform. Before peak season, ZibiWorks would have to validate&lt;br&gt;
not just Fulfillment but every capability on it, resolve defects in capabilities&lt;br&gt;
that are not driving the deadline, and complete data migration, reconciliation,&lt;br&gt;
and a production cutover, then stabilize an entirely new platform right before&lt;br&gt;
its highest-demand weeks.&lt;/p&gt;

&lt;p&gt;Those pressures have nothing to do with Fulfillment. It could be&lt;br&gt;
production-ready while defects in Customer Management or Billing hold up the&lt;br&gt;
cutover, with QA and release capacity consumed by unrelated problems. Even a&lt;br&gt;
rewrite delivered on time would send ZibiWorks into peak season on a platform&lt;br&gt;
with little production history. The risk is not simply missing the date; it is&lt;br&gt;
introducing broad operational uncertainty across the whole system at the exact&lt;br&gt;
moment Fulfillment needs to be &lt;em&gt;more&lt;/em&gt; reliable.&lt;/p&gt;

&lt;p&gt;This is not an argument that rewrites fail. A narrowly scoped or phased&lt;br&gt;
replacement could be viable under different constraints, and in that form it&lt;br&gt;
starts to resemble evolution anyway. ZibiWorks is weighing a broad replacement&lt;br&gt;
because its problems span the monolith, and at that scope the coordination and&lt;br&gt;
stabilization risk against this deadline is what makes it hard to justify.&lt;/p&gt;

&lt;h2&gt;
  
  
  Could ZibiWorks buy more time?
&lt;/h2&gt;

&lt;p&gt;The opposite move is to attempt nothing structural: scale out the existing&lt;br&gt;
monolith, survive one more peak season, and defer modernization. It is a&lt;br&gt;
legitimate option, and it turns on a distinction that is easy to blur, between&lt;br&gt;
&lt;strong&gt;capacity and architectural resilience&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Horizontal scaling might handle the extra load, assuming Fulfillment's&lt;br&gt;
bottleneck responds to additional compute. But more instances do not give&lt;br&gt;
Fulfillment independent deployment or failure isolation, and the shared&lt;br&gt;
constraints stay shared: database contention and synchronous dependencies can&lt;br&gt;
remain, or even worsen as more instances press on the same backend. Scaling the&lt;br&gt;
whole monolith to serve one capability raises cost too, though that is&lt;br&gt;
secondary here.&lt;/p&gt;

&lt;p&gt;Leadership asked for two things, scalability &lt;em&gt;and&lt;/em&gt; resiliency. Additional&lt;br&gt;
capacity may improve availability, but it does not eliminate the shared failure&lt;br&gt;
risks Fulfillment faces within the monolith. Under lighter constraints, buying a&lt;br&gt;
season of runway could be reasonable. For ZibiWorks it defers the problem rather&lt;br&gt;
than reducing the risk it needs to retire before peak season.&lt;/p&gt;

&lt;h3&gt;
  
  
  Evaluating the options against the business needs
&lt;/h3&gt;

&lt;p&gt;The choice becomes clearer when each approach is measured against what ZibiWorks&lt;br&gt;
actually needs before peak season.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decision criterion&lt;/th&gt;
&lt;th&gt;Full rewrite&lt;/th&gt;
&lt;th&gt;Scale existing monolith&lt;/th&gt;
&lt;th&gt;Evolve incrementally&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Improve Fulfillment before peak season&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Depends on full-platform readiness&lt;/td&gt;
&lt;td&gt;Can improve capacity quickly&lt;/td&gt;
&lt;td&gt;Can prioritize Fulfillment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scale Fulfillment independently&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes, after replacement&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes, after extraction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Improve Fulfillment resilience&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Can design for resilience, but benefits arrive with replacement&lt;/td&gt;
&lt;td&gt;Can add redundancy; shared failure risks remain&lt;/td&gt;
&lt;td&gt;Can improve resilience as Fulfillment is extracted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Isolate Fulfillment from unrelated failures&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes, with appropriate boundaries&lt;/td&gt;
&lt;td&gt;Existing application coupling remains&lt;/td&gt;
&lt;td&gt;Yes, with appropriate boundaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Minimize peak-season deployment risk&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Coordinated cutover and stabilization&lt;/td&gt;
&lt;td&gt;Smaller immediate change, existing coupling remains&lt;/td&gt;
&lt;td&gt;Smaller incremental transitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Continue broader modernization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Complete replacement&lt;/td&gt;
&lt;td&gt;Deferred&lt;/td&gt;
&lt;td&gt;Progressive modernization&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Leadership's requirements are explicit: improve Fulfillment's scalability &lt;em&gt;and&lt;/em&gt;&lt;br&gt;
resilience before peak season. Those are what the first rows measure. The rest,&lt;br&gt;
deployment risk and the ability to keep modernizing, are supporting&lt;br&gt;
architectural criteria rather than mandates, but they shape which option meets&lt;br&gt;
the requirements safely. Read that way, no column strictly dominates. A rewrite&lt;br&gt;
reaches the same target capabilities but gates them behind the whole&lt;br&gt;
replacement; scaling adds capacity fastest but leaves the coupling and&lt;br&gt;
resilience gap in place; evolution can act on Fulfillment first, provided the&lt;br&gt;
extraction and its boundaries hold.&lt;/p&gt;

&lt;h2&gt;
  
  
  The decision
&lt;/h2&gt;

&lt;p&gt;ZibiWorks chooses &lt;strong&gt;evolutionary modernization&lt;/strong&gt;. It is the clearest path to&lt;br&gt;
improving Fulfillment on its own timeline, without gating that improvement&lt;br&gt;
behind a complete replacement. Scaling the monolith could improve capacity and,&lt;br&gt;
through redundancy, availability, but it does not resolve the shared application&lt;br&gt;
failure modes or give Fulfillment the independent failure isolation leadership's&lt;br&gt;
resilience requirement implies. Evolution's gains are not free: they depend on&lt;br&gt;
successfully extracting Fulfillment behind boundaries that hold. On ZibiWorks'&lt;br&gt;
constraints that is the better bet than coupling Fulfillment's deadline to a&lt;br&gt;
platform-wide cutover.&lt;/p&gt;

&lt;p&gt;Under different constraints (no deadline, a smaller system, an appetite for one&lt;br&gt;
coordinated cutover) the rewrite could be the better call. Here, the deadline&lt;br&gt;
and the breadth of the system decide it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What evolution requires
&lt;/h2&gt;

&lt;p&gt;"Evolve the monolith" is a decision, not yet an architecture. Delivering it&lt;br&gt;
means designing two architectures at once and keeping them distinct.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Target architecture&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The system ZibiWorks ultimately wants&lt;/li&gt;
&lt;li&gt;Domains, ownership, data responsibilities, integration&lt;/li&gt;
&lt;li&gt;Permanent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Migration architecture&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The temporary mechanisms that get there safely&lt;/li&gt;
&lt;li&gt;Adapters, shims, DI seams, sync paths, fallbacks, routing, bridges&lt;/li&gt;
&lt;li&gt;Designed to disappear&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keeping these separate is what stops the migration scaffolding from being&lt;br&gt;
mistaken for the target. The migration architecture is real, designed work, even&lt;br&gt;
though most of it is meant to be removed when the migration is done.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reversibility, and its limit
&lt;/h3&gt;

&lt;p&gt;One benefit of a designed migration architecture is that transitions can be made&lt;br&gt;
reversible. The monolith depends on an interface representing a business&lt;br&gt;
capability. Dependency injection first resolves that interface to the legacy&lt;br&gt;
implementation; during migration it is swapped for an adapter that talks to a&lt;br&gt;
newly extracted domain. From the monolith's point of view the contract is&lt;br&gt;
unchanged even though the implementation boundary has moved, so routing can be&lt;br&gt;
pointed back at the legacy implementation through configuration. A large&lt;br&gt;
architectural change becomes a sequence of smaller, individually reversible&lt;br&gt;
steps.&lt;/p&gt;

&lt;p&gt;But routing rollback is not the same as business-state rollback. Flipping the&lt;br&gt;
switch back is easy at the application boundary; whether it is &lt;em&gt;safe&lt;/em&gt; depends on&lt;br&gt;
what has happened to the underlying data. If the new domain has become&lt;br&gt;
authoritative, how can the legacy implementation still be a viable fallback?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ &lt;strong&gt;Data completeness and data authority are not the same thing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Authoritative writes can move to the extracted domain while the monolith keeps a &lt;em&gt;complete enough&lt;/em&gt; representation of the data it needs, maintained from the new domain's events. The new domain owns the writes; the monolith's copy stays current enough for legacy workflows to keep running; the old implementation remains a plausible fallback. Call it &lt;strong&gt;dual presence, single authority&lt;/strong&gt;: the data is present in both places, but only one side is the source of truth.&lt;/p&gt;

&lt;p&gt;Completeness is not the same as authority, and it is not the same as safe rollback. Once authority has moved, reversing it also depends on state compatibility, business semantics, and the ability to reconcile whatever each side wrote in the meantime. Asynchronous synchronization keeps the fallback viable; it does not, by itself, guarantee a clean reversal.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Strangler Fig
&lt;/h3&gt;

&lt;p&gt;With evolution chosen and the migration architecture understood, the &lt;strong&gt;Strangler&lt;br&gt;
Fig&lt;/strong&gt; pattern is the strategy that organizes the work. It gives the migration a&lt;br&gt;
repeatable sequence: pick a capability, redirect its responsibility to a new&lt;br&gt;
domain behind the existing contract, let old and new run side by side, move data&lt;br&gt;
authority across once confidence builds, then retire the legacy implementation.&lt;br&gt;
The cycle repeats, one capability at a time, until the monolith has been&lt;br&gt;
strangled. Fulfillment is the natural first candidate, since it is the capability&lt;br&gt;
under the most pressure.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Scaffolding Fallacy
&lt;/h3&gt;

&lt;p&gt;Most of that migration architecture is built to be thrown away, which invites a&lt;br&gt;
tempting but wrong conclusion: that if something is going to be removed later,&lt;br&gt;
the effort spent building it must be waste. Construction scaffolding is temporary&lt;br&gt;
and is no part of the finished building, yet no one calls it wasted. Its value is&lt;br&gt;
letting the building go up safely. Migration architecture is the same. It can&lt;br&gt;
have no place in the target and still be worth a great deal during the&lt;br&gt;
transition.&lt;/p&gt;

&lt;p&gt;The guardrail is that "temporary" is not a license for uncontrolled complexity.&lt;br&gt;
Scaffolding that earns its place has a purpose, a transition it enables, an&lt;br&gt;
owner, and a known removal condition.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Temporary by design is very different from accidentally permanent. The problem&lt;br&gt;
is not throwaway code; it is throwaway code that nobody ever throws away.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Consequences
&lt;/h2&gt;

&lt;p&gt;The decision is a trade made with its costs in view.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What ZibiWorks gains&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fulfillment addressed first, ahead of peak season&lt;/li&gt;
&lt;li&gt;Business value arriving earlier, not at the end&lt;/li&gt;
&lt;li&gt;Smaller, independently validated transitions&lt;/li&gt;
&lt;li&gt;A smaller blast radius per change, with more chances to roll back&lt;/li&gt;
&lt;li&gt;Data migration spread across the program&lt;/li&gt;
&lt;li&gt;No single platform-wide cutover&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What ZibiWorks accepts&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Temporary architecture and some disposable code&lt;/li&gt;
&lt;li&gt;Coexistence between old and new&lt;/li&gt;
&lt;li&gt;Eventual-consistency and synchronization concerns&lt;/li&gt;
&lt;li&gt;Added operational complexity during migration&lt;/li&gt;
&lt;li&gt;The ongoing job of actively retiring scaffolding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The right column is the price of the left one.&lt;/p&gt;

&lt;p&gt;ZibiWorks has now decided to modernize, chosen evolution, and settled on&lt;br&gt;
Strangler Fig as the approach. But a strategy is not a cut line. Before the&lt;br&gt;
system can come apart safely, ZibiWorks has to understand where its real&lt;br&gt;
boundaries are, and that starts with the language the business uses to describe&lt;br&gt;
them.&lt;/p&gt;




&lt;p&gt;ZibiWorks is fictional. The ideas and opinions expressed throughout the series are my own and do not represent the views, positions, or practices of my employer.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>refactoring</category>
      <category>softwareengineering</category>
      <category>devops</category>
    </item>
    <item>
      <title>Move It or Fix It First?</title>
      <dc:creator>Bob Palermo</dc:creator>
      <pubDate>Wed, 07 Oct 2026 14:14:56 +0000</pubDate>
      <link>https://dev.to/bpalermo11/move-it-or-fix-it-first-5f8d</link>
      <guid>https://dev.to/bpalermo11/move-it-or-fix-it-first-5f8d</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Leadership wants to move to the cloud. Before evaluating that answer, separate what migration solves from what modernization solves.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article is part of ZibiWorks, an ongoing fictional modernization case study. The ZibiWorks site is the canonical home of the series, with the full company context, starting architecture, and related installments. Explore the full series at &lt;a href="https://bobpalermo.github.io/zibiworks/" rel="noopener noreferrer"&gt;ZibiWorks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;"Move to the cloud" is a reasonable instinct, and it comes from real pressure.&lt;br&gt;
But it is a solution, not a problem. Before evaluating it, the job is to work&lt;br&gt;
out what moving would actually solve, what it would leave untouched, and whether&lt;br&gt;
the timing even makes sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  What leadership is actually reacting to
&lt;/h2&gt;

&lt;p&gt;Behind "move to the cloud" is a stack of legitimate pressures: hardware&lt;br&gt;
approaching its refresh cycle, a rising operational burden in the data center,&lt;br&gt;
disaster-recovery expectations that are getting harder to meet, seasonal demand&lt;br&gt;
that forces sizing for peaks, environment provisioning that takes too long, and&lt;br&gt;
a reluctance to sign off on another large capital investment just as the company&lt;br&gt;
expects to grow.&lt;/p&gt;

&lt;p&gt;ZibiWorks also has a separate, well-known set of problems in the application&lt;br&gt;
itself, established back in &lt;a href="https://bobpalermo.github.io/zibiworks/start-here/the-starting-point/" rel="noopener noreferrer"&gt;The Starting&lt;br&gt;
Point&lt;/a&gt;: risky monolithic releases,&lt;br&gt;
teams colliding in one codebase, a shared database, tightly coupled business&lt;br&gt;
domains, brittle partner integrations, and no way to scale business capabilities&lt;br&gt;
independently.&lt;/p&gt;

&lt;p&gt;Both lists are real. The trap is treating them as one problem with one answer.&lt;br&gt;
Sort the pressures by what kind of problem they are, and a pattern shows up:&lt;br&gt;
some are about &lt;em&gt;where the application runs&lt;/em&gt;, and some are about &lt;em&gt;how it is&lt;br&gt;
built&lt;/em&gt;. A third set is not a problem class at all. These are the constraints and&lt;br&gt;
timing that shape when and how either kind of change can happen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it runs (infrastructure)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hardware refresh and OS lifecycle&lt;/li&gt;
&lt;li&gt;Disaster recovery&lt;/li&gt;
&lt;li&gt;Capacity planning and seasonal peaks&lt;/li&gt;
&lt;li&gt;Slow environment provisioning&lt;/li&gt;
&lt;li&gt;Data-center burden and capital expenditure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How it is built (application)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tightly coupled business capabilities&lt;/li&gt;
&lt;li&gt;Shared database&lt;/li&gt;
&lt;li&gt;Monolithic, risky deployment&lt;/li&gt;
&lt;li&gt;Brittle partner integrations&lt;/li&gt;
&lt;li&gt;No independent scaling of capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Constraints and timing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Systems that must stay on premises&lt;/li&gt;
&lt;li&gt;Unsupported runtimes or licensing limits&lt;/li&gt;
&lt;li&gt;Prepaid or committed licenses&lt;/li&gt;
&lt;li&gt;Upcoming renewals, refreshes, or growth events&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Migration and modernization solve different problems
&lt;/h2&gt;

&lt;p&gt;Migration changes &lt;em&gt;where&lt;/em&gt; the application runs. Modernization changes &lt;em&gt;how it is&lt;br&gt;
built&lt;/em&gt;. They are not rival strategies so much as answers to different questions,&lt;br&gt;
and the split in the pressures above maps almost cleanly onto them.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pressure&lt;/th&gt;
&lt;th&gt;Migration helps?&lt;/th&gt;
&lt;th&gt;Modernization helps?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hardware refresh, DR, seasonal peaks&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slow environment provisioning&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monolithic, risky releases&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shared database, domain coupling&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brittle partner integrations&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Systems that must stay on premises&lt;/td&gt;
&lt;td&gt;Negative / adds complexity&lt;/td&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The one line to carry out of this installment:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Moving the monolith to the cloud does not un-share the database or untangle the domains. Modernizing in place does not replace aging hardware or fix DR.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is why "migrate or modernize" is a false binary. The real options sit on a&lt;br&gt;
spectrum: &lt;strong&gt;modernize in place&lt;/strong&gt;, &lt;strong&gt;migrate largely as-is&lt;/strong&gt;, &lt;strong&gt;migrate with&lt;br&gt;
selective remediation&lt;/strong&gt;, or &lt;strong&gt;migrate and modernize together&lt;/strong&gt;. Doing both at&lt;br&gt;
once addresses the most problems, which is exactly why it is tempting, but it&lt;br&gt;
also carries the most cost, the longest duration, the heaviest coordination, and&lt;br&gt;
the greatest execution risk. The question is not which option is best in the&lt;br&gt;
abstract. It is which sequence fits ZibiWorks right now.&lt;/p&gt;

&lt;h2&gt;
  
  
  What narrows the decision
&lt;/h2&gt;

&lt;p&gt;Sequencing is decided less by architecture than by lifecycle, money, and&lt;br&gt;
dependencies. Three facts do most of the work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Timing and economics.&lt;/strong&gt; Much of what makes a move cheap or expensive is not&lt;br&gt;
technical at all. The cost of leaving and the cost of staying both turn on where&lt;br&gt;
ZibiWorks sits in the hardware, license, and contract lifecycle, and that&lt;br&gt;
position changes constantly. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ &lt;strong&gt;Timing is part of the architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The same decision flips with the calendar. A migration that looks unattractive the month after a hardware purchase can look obvious when that hardware is a year from replacement. Note when things expire, not just what they are.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Dependencies.&lt;/strong&gt; A workload's readiness to move is partly set by the systems&lt;br&gt;
around it, and ZibiWorks does not run in isolation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8c4ztdqyso04ow4fyeow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8c4ztdqyso04ow4fyeow.png" alt="The ZibiWorks platform depends on SQL Server, a legacy Fulfillment system, and Warehouse / Manufacturing, so its readiness to move is partly set by systems around it." width="800" height="731"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Legacy dependencies do not automatically block a move; they change its cost and&lt;br&gt;
shape. Some can move, some cannot move yet, and anything that must stay on&lt;br&gt;
premises turns a clean migration into a hybrid one, with private connectivity&lt;br&gt;
and latency to manage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Whether the move is even necessary.&lt;/strong&gt; It is worth saying plainly that if the&lt;br&gt;
current infrastructure had years of life left, met capacity and recovery needs,&lt;br&gt;
and was cheap to run, delaying the move could be the right call. Migration has&lt;br&gt;
to earn its place against simply modernizing where the system already runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  How ZibiWorks lands, and why
&lt;/h2&gt;

&lt;p&gt;For ZibiWorks, those facts line up in one direction. The hardware refresh is&lt;br&gt;
approaching and major licenses are nearing renewal at roughly the same time.&lt;br&gt;
Recovery expectations are rising faster than the data center can meet them,&lt;br&gt;
seasonal peaks force expensive over-provisioning, and expected growth would&lt;br&gt;
otherwise trigger a fresh round of infrastructure investment. Environment&lt;br&gt;
provisioning is already too slow for the delivery pace. On the application side,&lt;br&gt;
the monolith can run in the target cloud with manageable remediation, and the&lt;br&gt;
few legacy systems that must stay on premises can be reached over private&lt;br&gt;
connectivity. Crucially, full modernization would take far longer than the&lt;br&gt;
infrastructure lifecycle allows.&lt;/p&gt;

&lt;p&gt;Put together, the infrastructure clock is forcing a decision now, and the&lt;br&gt;
application work cannot be finished on that timeline. So:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Move now. Modernize next. Change only what is required to make the move safe and useful.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is &lt;strong&gt;migration with selective remediation&lt;/strong&gt;: not a blind lift-and-shift,&lt;br&gt;
and not a big-bang modernize-while-moving. Under different facts (new hardware,&lt;br&gt;
prepaid licenses with years left, no growth event, healthy DR), the same&lt;br&gt;
reasoning could land on "stay and modernize." The decision follows the&lt;br&gt;
constraints, not a preference for one strategy.&lt;/p&gt;

&lt;h3&gt;
  
  
  What gets fixed now, and what waits
&lt;/h3&gt;

&lt;p&gt;Selective remediation only works if the line between the two is explicit. The&lt;br&gt;
deferred items are real and important; they simply do not need to be solved to&lt;br&gt;
move the application safely. Keeping that line clear is what stops the move from&lt;br&gt;
sliding into a rewrite.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix now: migration blockers&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repeatable, automated deployment&lt;/li&gt;
&lt;li&gt;Externalized environment configuration&lt;/li&gt;
&lt;li&gt;Remove hard dependencies on server identity&lt;/li&gt;
&lt;li&gt;Health checks&lt;/li&gt;
&lt;li&gt;Baseline observability&lt;/li&gt;
&lt;li&gt;Reliable backup and recovery&lt;/li&gt;
&lt;li&gt;Infrastructure automation&lt;/li&gt;
&lt;li&gt;Connectivity to systems staying on premises&lt;/li&gt;
&lt;li&gt;Anything unsupported in the target environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Defer: non-blocking modernization concerns&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The shared database&lt;/li&gt;
&lt;li&gt;Business-domain coupling&lt;/li&gt;
&lt;li&gt;Monolithic deployment&lt;/li&gt;
&lt;li&gt;Brittle integration boundaries&lt;/li&gt;
&lt;li&gt;Most code-level modernization work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A known problem can be deliberately deferred when solving it is not required to&lt;br&gt;
reach the current objective. That is a discipline, not an oversight.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;When "later" becomes "never"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Deferring modernization is a deliberate decision, but it carries a risk. Once the migration is complete and the immediate infrastructure pressures are gone, the urgency to address those architectural problems may fade. "Migrate now, modernize later" can quietly become "migrate now, modernize never."&lt;/p&gt;

&lt;p&gt;That does not make migration-first the wrong choice. It means the deferred work has to stay visible as part of the broader modernization effort.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What the move leaves behind
&lt;/h2&gt;

&lt;p&gt;After the move, ZibiWorks has better infrastructure automation, better recovery,&lt;br&gt;
faster provisioning, a more flexible capacity model, and less dependence on&lt;br&gt;
physical hardware. The platform is in a better position to evolve. But when the&lt;br&gt;
teams open the codebase again, the same problems are waiting: the monolith is&lt;br&gt;
still a monolith, the database is still shared, the business capabilities are&lt;br&gt;
still tightly coupled. ZibiWorks chose not to solve those during the move. Now&lt;br&gt;
it is time to.&lt;/p&gt;

&lt;p&gt;The next question is whether to replace the application or evolve it.&lt;/p&gt;




&lt;p&gt;ZibiWorks is fictional. The ideas and opinions expressed throughout the series are my own and do not represent the views, positions, or practices of my employer.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>cloud</category>
      <category>devops</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>What's Driving Change?</title>
      <dc:creator>Bob Palermo</dc:creator>
      <pubDate>Mon, 05 Oct 2026 12:33:27 +0000</pubDate>
      <link>https://dev.to/bpalermo11/whats-driving-change-51l</link>
      <guid>https://dev.to/bpalermo11/whats-driving-change-51l</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Before designing anything, find where the current platform is starting to limit the business, and which touch points deserve a closer look.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article is part of ZibiWorks, an ongoing fictional modernization case study. The ZibiWorks site is the canonical home of the series, with the full company context, starting architecture, and related installments. Explore the full series at &lt;a href="https://bobpalermo.github.io/zibiworks/" rel="noopener noreferrer"&gt;ZibiWorks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;ZibiWorks believes they need to modernize, but that conclusion did not come from an architecture diagram. It came from mounting pressure on &lt;a href="https://bobpalermo.github.io/zibiworks/start-here/meet-zibiworks/" rel="noopener noreferrer"&gt;the business&lt;/a&gt; and the teams supporting it. Changes take longer than they should. Deployments carry more risk. Partner integrations are getting harder to manage. New customer experiences and connected-product capabilities are placing demands on a platform that was built for a simpler business.&lt;/p&gt;

&lt;p&gt;Those pressures are real, but they do not yet tell us what architecture to build. The instinct is often to jump directly to services, a message bus, the cloud, or a cleaner data model. That is premature. Before choosing an architecture, we need to understand where the current one is actually getting in the way, what business outcomes are being affected, and which problems are significant enough to justify change.&lt;/p&gt;

&lt;p&gt;So this installment is about where modernization should start, not what to build. We will identify the business and technical pressures worth addressing, turn them into an initial set of success criteria, and establish the guiding principles that later architecture choices can be weighed against. Those criteria and principles become the decision lens for everything that follows.&lt;/p&gt;

&lt;h2&gt;
  
  
  What should guide the decision?
&lt;/h2&gt;

&lt;p&gt;Before choosing an architecture, we need to know what should guide the choice.&lt;br&gt;
That comes from combining two things: the business context, which tells us what&lt;br&gt;
matters, and the technical context, which tells us what is feasible,&lt;br&gt;
sustainable, and responsible to build and operate. The business rarely asks for&lt;br&gt;
"changeability" or "resilience" by name; those are architectural&lt;br&gt;
interpretations. Neither side provides the answer alone.&lt;/p&gt;

&lt;p&gt;In brownfield work that business context often shows up as existing pain; in greenfield&lt;br&gt;
work it more often arrives as goals, constraints, and assumptions. Either way,&lt;br&gt;
the two inputs are:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Business context&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Desired outcomes and business capabilities&lt;/li&gt;
&lt;li&gt;Where the pressure is (for ZibiWorks, existing pain)&lt;/li&gt;
&lt;li&gt;Cost, delivery, and timing&lt;/li&gt;
&lt;li&gt;Customer expectations and growth assumptions&lt;/li&gt;
&lt;li&gt;Risk tolerance and regulatory constraints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Technical context&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Current platform capabilities, where one exists&lt;/li&gt;
&lt;li&gt;Available technology choices and constraints&lt;/li&gt;
&lt;li&gt;Team skills and operational maturity&lt;/li&gt;
&lt;li&gt;Resilience, security, data integrity, observability&lt;/li&gt;
&lt;li&gt;Technical debt (where applicable) and cost to operate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Combining the two produces the first useful output, and it is not a target&lt;br&gt;
architecture. It gives us two things we can use to evaluate architecture&lt;br&gt;
choices: &lt;strong&gt;success criteria&lt;/strong&gt;, which describe what needs to improve and how we&lt;br&gt;
will know, and &lt;strong&gt;guiding principles&lt;/strong&gt;, which describe how we make decisions&lt;br&gt;
while pursuing those improvements.&lt;/p&gt;

&lt;p&gt;Those criteria and principles become the lens for the decisions that follow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8l71578r321sqcdlgg1p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8l71578r321sqcdlgg1p.png" alt="The decision lens: business and technical context feed principles and success criteria, which drive evaluation, tradeoffs, outcomes, and revision." width="800" height="126"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are working criteria and principles, not commandments. As the business and&lt;br&gt;
technical context change, the decision lens changes with them.&lt;/p&gt;

&lt;p&gt;For ZibiWorks, then, the first step is not to redesign the platform because the&lt;br&gt;
architecture looks old. It is to find where the current architecture is actually&lt;br&gt;
creating meaningful friction, trace those pressures into the platform, and mark&lt;br&gt;
where architectural change might create leverage, stopping short of choosing a&lt;br&gt;
solution. Each pressure below follows the same path:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Business pressure → Business constraint → Architectural touch point → Options examined later&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The platform still works
&lt;/h2&gt;

&lt;p&gt;The platform is doing its job. Orders are placed, Zibis are shipped, customers and partners are served. The business is operating.&lt;/p&gt;

&lt;p&gt;The original architecture was a good fit for an earlier ZibiWorks. What changed is the business around it. Growth has changed the consequences of the original tradeoffs.&lt;/p&gt;

&lt;p&gt;The result is not a broken platform. It is a working platform beginning to show wear in a few important places. Those pressures are where the rest of this installment focuses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pressure 01 · Brittleness: The platform is getting harder to change safely
&lt;/h3&gt;

&lt;p&gt;Over time, ZibiWorks' concerns have grown into one another. Orders reach into&lt;br&gt;
Pricing, Inventory, Payments, Fulfillment, Shipping, Customer, and Zibi&lt;br&gt;
Lifecycle, with product and catalog logic close behind. Shared models span&lt;br&gt;
several business areas. Entity Framework entities flow upward through the stack.&lt;br&gt;
&lt;code&gt;Common&lt;/code&gt; models are reused broadly, and shared tables carry ambiguous ownership.&lt;/p&gt;

&lt;p&gt;No single one of these dependencies is wrong. An Order genuinely needs pricing&lt;br&gt;
and inventory. The trouble comes from how many of them accumulate and cross&lt;br&gt;
through the same shared models, so a change in one area rarely stays in one&lt;br&gt;
area.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foa1v0bv72xcsrjys6inl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foa1v0bv72xcsrjys6inl.png" alt="Orders is entangled with Pricing, Payments, Customer, Inventory, Fulfillment, and Shipping, so a local change rarely stays local." width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A local change around Orders rarely stays local, because Orders is entangled with several surrounding concerns.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interwoven concerns → Larger blast radius → Broader testing → More coordination → Slower delivery&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A local change can have non-local effects. Making a change safely takes broader&lt;br&gt;
knowledge of the system, regression testing expands, releases need more&lt;br&gt;
coordination, and one change is harder to isolate from everything else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pressure 02 · Partner Coupling: External change has become an internal delivery problem
&lt;/h3&gt;

&lt;p&gt;ZibiWorks depends on PayPaw, Handle With Care, Paw &amp;amp; Circuit, RoboMotion&lt;br&gt;
Systems, and HomeSphere. As covered in &lt;a href="https://bobpalermo.github.io/zibiworks/start-here/the-starting-point/" rel="noopener noreferrer"&gt;The Starting&lt;br&gt;
Point&lt;/a&gt;, partner-specific fields,&lt;br&gt;
statuses, identifiers, workflows, payload shapes, and batch formats have worked&lt;br&gt;
their way into internal logic and shared models.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8c1h8mkndu96pvs1kdcs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8c1h8mkndu96pvs1kdcs.png" alt="Partner concerns leak inward into Orders, Shipping, and Inventory and through shared models." width="800" height="545"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Partner change now reaches beyond the partner boundary.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The business impact is specific: a partner change can require changes in parts&lt;br&gt;
of ZibiWorks that look unrelated to that partner. A new API version, changed&lt;br&gt;
payload, revised status model, new capability, deprecated interface, or new&lt;br&gt;
compliance requirement can all ripple inward.&lt;/p&gt;

&lt;p&gt;There is a second cost that is easy to miss. Because the integrations are woven&lt;br&gt;
so deeply into the platform, ZibiWorks may not be able to adopt a better&lt;br&gt;
partner API or a new partner feature quickly. That quietly limits its options.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pressure 03 · Growth: Each new capability increases the cost of the next one
&lt;/h3&gt;

&lt;p&gt;ZibiWorks wants to expand: more connected Zibis, more partners, more&lt;br&gt;
customer and internal capabilities, new products and services, more reporting,&lt;br&gt;
and eventually new platform opportunities. The current platform can support all&lt;br&gt;
of it. The problem is that each new capability tends to make the next one&lt;br&gt;
harder. That points at a kind of scalability people often skip past.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Runtime scalability&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More requests&lt;/li&gt;
&lt;li&gt;More transactions&lt;/li&gt;
&lt;li&gt;More connected devices&lt;/li&gt;
&lt;li&gt;Can the infrastructure handle growth?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Change scalability&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More capabilities&lt;/li&gt;
&lt;li&gt;More teams changing the system&lt;/li&gt;
&lt;li&gt;More dependencies and coordination&lt;/li&gt;
&lt;li&gt;Can the organization keep changing safely?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scalability is not only about runtime capacity. Runtime scaling and change&lt;br&gt;
scaling are different problems. Right now, change scalability is the bigger&lt;br&gt;
problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pressure 04 · Capacity: Normal demand or peak demand?
&lt;/h3&gt;

&lt;p&gt;ZibiWorks runs on traditional infrastructure, so it has to size capacity&lt;br&gt;
ahead of demand. Demand is not steady. Holidays, promotions, product launches,&lt;br&gt;
partner campaigns, and seasonal buying all push it around.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flierppajml322a3a0j05.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flierppajml322a3a0j05.png" alt="Demand fluctuates over time against a flat normal-sized capacity line and a higher peak-sized capacity line." width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Size for normal demand:&lt;/strong&gt; lower steady-state cost, greater peak risk.&lt;br&gt;
&lt;strong&gt;Size for peak demand:&lt;/strong&gt; more headroom, more idle capacity.&lt;/p&gt;

&lt;p&gt;The architecture makes this sharper. Much of the platform scales as a&lt;br&gt;
coordinated unit, so a spike in one area can force ZibiWorks to add capacity&lt;br&gt;
broadly rather than where the load actually is. The conclusion is not that cloud&lt;br&gt;
is the answer. ZibiWorks needs a better answer to variable demand, and cloud&lt;br&gt;
elasticity may eventually be one option worth weighing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pressure 05 · Customer Experience: Reliability is becoming part of the product
&lt;/h3&gt;

&lt;p&gt;ZibiWorks is increasingly customer-facing. When the platform mostly supported&lt;br&gt;
internal operations, downtime was an internal disruption. As more of it faces&lt;br&gt;
customers, downtime becomes something customers see: a slow storefront, a failed&lt;br&gt;
checkout, account functions that will not load, delayed order information,&lt;br&gt;
Zibi management that is unavailable, connected features that degrade.&lt;/p&gt;

&lt;p&gt;This connects directly to the capacity pressure. The periods of highest customer&lt;br&gt;
demand are often the periods of highest infrastructure strain, so a capacity&lt;br&gt;
shortfall tends to show up as a customer-facing outage at the worst possible&lt;br&gt;
time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customer demand ↑ → Infrastructure pressure ↑ → Performance / availability risk → Customer experience&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Reliability and scalability are becoming part of the customer experience, which&lt;br&gt;
raises later questions about resilience, observability, failure isolation,&lt;br&gt;
graceful degradation, and recovery.&lt;/p&gt;

&lt;p&gt;None of these pressures selects an architecture. They mark where the current&lt;br&gt;
platform constrains the business. Patterns and technologies come later.&lt;/p&gt;

&lt;h2&gt;
  
  
  From business pressure to architectural touch point
&lt;/h2&gt;

&lt;p&gt;Each pressure points at an area of the architecture worth examining later. These&lt;br&gt;
touch points show where the series will look next without assuming the eventual&lt;br&gt;
solution.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Business pressure&lt;/th&gt;
&lt;th&gt;Architectural touch point&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Changes ripple&lt;/td&gt;
&lt;td&gt;Boundaries, dependency structure, modularity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partner blast radius&lt;/td&gt;
&lt;td&gt;Integration boundaries, anti-corruption layers (ACLs), contract isolation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Growth cost rises&lt;/td&gt;
&lt;td&gt;Extensibility, deployment boundaries, incremental evolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Variable demand&lt;/td&gt;
&lt;td&gt;Scaling model, workload isolation, elasticity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visible downtime&lt;/td&gt;
&lt;td&gt;Resilience, failure isolation, observability, recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ambiguous ownership&lt;/td&gt;
&lt;td&gt;Domain modeling, data ownership, contextual models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Diverging experiences&lt;/td&gt;
&lt;td&gt;API boundaries, consumer-specific contracts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reporting pressure&lt;/td&gt;
&lt;td&gt;Analytical separation, read models, data movement&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Sorting what is fine from what is not
&lt;/h2&gt;

&lt;p&gt;Not everything imperfect in the platform is worth changing. Some of what looks&lt;br&gt;
dated is still doing its job perfectly well, and some of what works today is&lt;br&gt;
already starting to strain. The point of sorting is to separate the parts that&lt;br&gt;
are fine from the parts that are actually costing the business something.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Still serving the business&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.NET&lt;/li&gt;
&lt;li&gt;SQL Server&lt;/li&gt;
&lt;li&gt;Entity Framework&lt;/li&gt;
&lt;li&gt;Responsive web application&lt;/li&gt;
&lt;li&gt;Relational transactions&lt;/li&gt;
&lt;li&gt;Shared deployment where it still simplifies operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Creating measurable friction&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cross-domain business coupling&lt;/li&gt;
&lt;li&gt;Ambiguous data ownership&lt;/li&gt;
&lt;li&gt;Partner logic inside core services&lt;/li&gt;
&lt;li&gt;Coordinated deployment&lt;/li&gt;
&lt;li&gt;Broad regression impact&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The business is beginning to require&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stable API contracts&lt;/li&gt;
&lt;li&gt;Additional user experiences&lt;/li&gt;
&lt;li&gt;More independent scaling&lt;/li&gt;
&lt;li&gt;Clear telemetry and state ownership&lt;/li&gt;
&lt;li&gt;Separation of operational and analytical workloads&lt;/li&gt;
&lt;li&gt;Higher customer-facing availability&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;If this does not materially affect delivery speed, risk, scalability, availability, security, cost, or a business capability, why are we changing it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We carry forward only the areas where a technical constraint can be tied to&lt;br&gt;
meaningful business impact.&lt;/p&gt;

&lt;h2&gt;
  
  
  The initial decision lens
&lt;/h2&gt;

&lt;p&gt;The pressures point to five areas worth deeper investigation. Together they&lt;br&gt;
become the first working &lt;strong&gt;success criteria&lt;/strong&gt; for ZibiWorks: for each, the&lt;br&gt;
current strain and what improvement would look like.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area &amp;amp; current strain&lt;/th&gt;
&lt;th&gt;What success looks like&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Changeability.&lt;/strong&gt; Cross-domain coupling, shared ownership, and broad regression impact are raising the cost of change.&lt;/td&gt;
&lt;td&gt;A smaller blast radius, less regression and coordination effort, and more changes that stay local.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Extensibility.&lt;/strong&gt; Partner coupling and the rising cost of adding capabilities make external change expensive.&lt;/td&gt;
&lt;td&gt;Less effort to absorb partner and capability changes, with fewer unrelated areas affected.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Scalability.&lt;/strong&gt; Variable demand and increasingly different workload profiles expose both runtime and change-scaling constraints.&lt;/td&gt;
&lt;td&gt;Handling variable demand without scaling the platform as one unit, while improving the system's ability to absorb ongoing change.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Reliability.&lt;/strong&gt; Performance and availability problems are becoming visible to customers.&lt;/td&gt;
&lt;td&gt;Fewer customer-visible failures, with better failure isolation and recovery where the business impact justifies it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Data.&lt;/strong&gt; Ambiguous ownership and competing operational and analytical needs are creating friction.&lt;/td&gt;
&lt;td&gt;Clearer ownership and meaning, with less interference between operational and analytical workloads.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are directions, not numeric targets. The evidence tells us what needs to&lt;br&gt;
improve; the levels worth committing to come as specific decisions get made.&lt;/p&gt;

&lt;p&gt;These criteria describe what needs to improve. The guiding principles describe how ZibiWorks should make decisions while pursuing those improvements. They are not intended to map one-to-one to the criteria; most apply across several of them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Favor change isolation where coupling is materially slowing delivery.&lt;/li&gt;
&lt;li&gt;Improve resilience where failures are becoming customer-visible.&lt;/li&gt;
&lt;li&gt;Prefer reversible changes while the cost of being wrong is high.&lt;/li&gt;
&lt;li&gt;Add complexity only when it buys a capability the business actually needs.&lt;/li&gt;
&lt;li&gt;Preserve simplicity where the current design is still serving the business.&lt;/li&gt;
&lt;li&gt;Design for foreseeable growth, not every theoretical future.&lt;/li&gt;
&lt;li&gt;Treat security, data integrity, and operability as baseline constraints.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are ZibiWorks' first working criteria and principles, not permanent rules.&lt;br&gt;
They give the next decisions a consistent starting point and will be revisited&lt;br&gt;
as the context changes.&lt;/p&gt;

&lt;p&gt;With the pressures identified, the criteria named, and the principles in hand,&lt;br&gt;
ZibiWorks can take on its first real choice: do we address these constraints&lt;br&gt;
before moving the platform, or move first and modernize afterward?&lt;/p&gt;




&lt;p&gt;ZibiWorks is fictional. The ideas and opinions expressed throughout the series are my own and do not represent the views, positions, or practices of my employer.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>softwareengineering</category>
      <category>dotnet</category>
      <category>csharp</category>
    </item>
  </channel>
</rss>
