Skip to content

04. Boundaries and Meaning

Before separating the software, ZibiWorks needs to understand the business boundaries hidden inside its monolith.

  • Architecture
  • Modernization

We’ve chosen Fulfillment. But what is it?

Section titled “We’ve chosen Fulfillment. But what is it?”

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

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

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

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

That disagreement is the useful part. The same word starts to mean different things depending on who is saying it. Take available:

Business perspective What “available” means
Order Management The customer is allowed to buy it; it is sellable right now
Inventory Physical stock is on hand and not already reserved
Fulfillment The item is somewhere it can actually be picked and packed

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

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

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

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

The conversations point to three candidate bounded contexts: Order Management, Inventory, and Fulfillment.

Order Management

  • The customer’s commercial transaction
  • Orders, order lines, pricing, commitments
  • Cancellations and the order lifecycle

Inventory

  • Availability and commitment of stock
  • SKUs, on-hand quantities, reservations
  • Replenishment and warehouse locations

Fulfillment

  • The operational work to satisfy an order
  • Pick lists, packing, shipment preparation
  • Handoff, dispatch, exceptions

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

Fulfillment

  • Picking and packing items
  • Preparing shipments
  • Managing fulfillment work and handoff

Shipping

  • Carrier selection and transportation
  • Route optimization and delivery exceptions
  • Fleet maintenance, where applicable
  • Delivery tracking in transit

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

Working from the conversations rather than the schema, the team sorts responsibilities by who owns each decision.

Responsibility Business owner
Capture and manage customer orders Order Management
Handle commercial cancellations Order Management
Maintain stock availability Inventory
Reserve and allocate stock Inventory
Decide how accepted order lines are fulfilled Fulfillment
Manage picking and packing Fulfillment
Prepare shipments and hand off to the carrier Fulfillment
Report fulfillment progress Fulfillment

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

sequenceDiagram
    actor Customer
    participant OM as Order Management
    participant F as Fulfillment
    Customer->>OM: Request cancellation
    OM->>F: Can this order still be stopped?
    F-->>OM: Operational progress and what is reversible
    OM->>OM: Apply commercial rules and refund policy
    OM-->>Customer: Cancellation outcome

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.

That is the shape of a real boundary: collaboration without shared ownership.

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

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

A context owns its data. That doesn’t mean it’s the only context that needs access to it.

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

erDiagram
    CUSTOMER ||--o{ ORDERS : places
    ORDERS ||--o{ ORDER_LINES : contains
    PRODUCTS ||--o{ ORDER_LINES : "appears in"
    PRODUCTS ||--o{ STOCK : "tracked as"
    WAREHOUSES ||--o{ STOCK : holds
    ORDERS }o--|| WAREHOUSES : "allocated to"
    ORDERS {
        int OrderId PK "Order Mgmt"
        int CustomerId FK "Order Mgmt"
        date OrderDate "Order Mgmt"
        string OrderStatus "Order Mgmt"
        money TotalAmount "Order Mgmt"
        int WarehouseId FK "Inventory"
        string AllocationStatus "Inventory"
        bool BackorderFlag "Inventory"
        string PickStatus "Fulfillment"
        string PackStatus "Fulfillment"
        string ShipStatus "Fulfillment"
        date ShippedDate "Shipping?"
        string TrackingNumber "Shipping?"
    }

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.

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

The database reflects years of implementation decisions, not necessarily where the business boundaries belong.

The business boundaries turn the extraction choice into something the team can defend rather than guess at.

Candidate bounded context Decision Reason
Fulfillment Extract The scalability and resilience pressures that started the modernization
Order Management Remain Commercial workflows are stable and were never the problem
Inventory Remain Reach extends well beyond this effort; extracting it now blows up the scope
Shipping Investigate Still an open question, not an extraction commitment

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

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

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.